diff options
author | Moonchild <moonchild@palemoon.org> | 2021-05-08 21:00:49 +0000 |
---|---|---|
committer | Moonchild <moonchild@palemoon.org> | 2021-05-08 21:00:49 +0000 |
commit | 08da125d9cc6eea0bc514023e3a75efd64587259 (patch) | |
tree | 1fd18aee3b81c3b0da3c38b15af100db0be776af | |
parent | ca35efb84ebae522f9ab7803d8e017f721e03207 (diff) | |
download | uxp-08da125d9cc6eea0bc514023e3a75efd64587259.tar.gz |
Issue #1751 -- Remove cocoa and uikit widget support code
165 files changed, 4 insertions, 42109 deletions
diff --git a/widget/cocoa/ComplexTextInputPanel.h b/widget/cocoa/ComplexTextInputPanel.h deleted file mode 100644 index 648e6d9114..0000000000 --- a/widget/cocoa/ComplexTextInputPanel.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Modified by Josh Aas of Mozilla Corporation. - */ - -#ifndef ComplexTextInputPanel_h_ -#define ComplexTextInputPanel_h_ - -#include "nsString.h" -#include "npapi.h" - -class ComplexTextInputPanel -{ -public: - static ComplexTextInputPanel* GetSharedComplexTextInputPanel(); - virtual void PlacePanel(int32_t x, int32_t y) = 0; // Bottom left coordinate of plugin in screen coords - virtual void InterpretKeyEvent(void* aEvent, nsAString& aOutText) = 0; - virtual bool IsInComposition() = 0; - virtual void* GetInputContext() = 0; - virtual void CancelComposition() = 0; - -protected: - virtual ~ComplexTextInputPanel() {}; -}; - -#endif // ComplexTextInputPanel_h_ diff --git a/widget/cocoa/ComplexTextInputPanel.mm b/widget/cocoa/ComplexTextInputPanel.mm deleted file mode 100644 index a4b58955e2..0000000000 --- a/widget/cocoa/ComplexTextInputPanel.mm +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Modified by Josh Aas of Mozilla Corporation. - */ - -#import "ComplexTextInputPanel.h" - -#import <Cocoa/Cocoa.h> - -#include <algorithm> -#include "mozilla/Preferences.h" -#include "nsChildView.h" - -using namespace mozilla; - -extern "C" OSStatus TSMProcessRawKeyEvent(EventRef anEvent); - -#define kInputWindowHeight 20 - -@interface ComplexTextInputPanelImpl : NSPanel { - NSTextView *mInputTextView; -} - -+ (ComplexTextInputPanelImpl*)sharedComplexTextInputPanelImpl; - -- (NSTextInputContext*)inputContext; -- (void)interpretKeyEvent:(NSEvent*)event string:(NSString**)string; -- (void)cancelComposition; -- (BOOL)inComposition; - -// This places the text input panel fully onscreen and below the lower left -// corner of the focused plugin. -- (void)adjustTo:(NSPoint)point; - -@end - -@implementation ComplexTextInputPanelImpl - -+ (ComplexTextInputPanelImpl*)sharedComplexTextInputPanelImpl -{ - static ComplexTextInputPanelImpl *sComplexTextInputPanelImpl; - if (!sComplexTextInputPanelImpl) - sComplexTextInputPanelImpl = [[ComplexTextInputPanelImpl alloc] init]; - return sComplexTextInputPanelImpl; -} - -- (id)init -{ - // In the original Apple code the style mask is given by a function which is not open source. - // What could possibly be worth hiding in that function, I do not know. - // Courtesy of gdb: stylemask: 011000011111, 0x61f - self = [super initWithContentRect:NSZeroRect styleMask:0x61f backing:NSBackingStoreBuffered defer:YES]; - if (!self) - return nil; - - // Set the frame size. - NSRect visibleFrame = [[NSScreen mainScreen] visibleFrame]; - NSRect frame = NSMakeRect(visibleFrame.origin.x, visibleFrame.origin.y, visibleFrame.size.width, kInputWindowHeight); - - [self setFrame:frame display:NO]; - - mInputTextView = [[NSTextView alloc] initWithFrame:[self.contentView frame]]; - mInputTextView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable | NSViewMaxXMargin | NSViewMinXMargin | NSViewMaxYMargin | NSViewMinYMargin; - - NSScrollView* scrollView = [[NSScrollView alloc] initWithFrame:[self.contentView frame]]; - scrollView.documentView = mInputTextView; - self.contentView = scrollView; - [scrollView release]; - - [self setFloatingPanel:YES]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(keyboardInputSourceChanged:) - name:NSTextInputContextKeyboardSelectionDidChangeNotification - object:nil]; - - return self; -} - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - [mInputTextView release]; - - [super dealloc]; -} - -- (void)keyboardInputSourceChanged:(NSNotification *)notification -{ - static int8_t sDoCancel = -1; - if (!sDoCancel || ![self inComposition]) { - return; - } - if (sDoCancel < 0) { - bool cancelComposition = false; - static const char* kPrefName = - "ui.plugin.cancel_composition_at_input_source_changed"; - nsresult rv = Preferences::GetBool(kPrefName, &cancelComposition); - NS_ENSURE_SUCCESS(rv, ); - sDoCancel = cancelComposition ? 1 : 0; - } - if (sDoCancel) { - [self cancelComposition]; - } -} - -- (void)interpretKeyEvent:(NSEvent*)event string:(NSString**)string -{ - *string = nil; - - if (![[mInputTextView inputContext] handleEvent:event]) { - return; - } - - if ([mInputTextView hasMarkedText]) { - // Don't show the input method window for dead keys - if ([[event characters] length] > 0) { - [self orderFront:nil]; - } - return; - } else { - [self orderOut:nil]; - - NSString *text = [[mInputTextView textStorage] string]; - if ([text length] > 0) { - *string = [[text copy] autorelease]; - } - } - - [mInputTextView setString:@""]; -} - -- (NSTextInputContext*)inputContext -{ - return [mInputTextView inputContext]; -} - -- (void)cancelComposition -{ - [mInputTextView setString:@""]; - [self orderOut:nil]; -} - -- (BOOL)inComposition -{ - return [mInputTextView hasMarkedText]; -} - -- (void)adjustTo:(NSPoint)point -{ - NSRect selfRect = [self frame]; - NSRect rect = NSMakeRect(point.x, - point.y - selfRect.size.height, - 500, - selfRect.size.height); - - // Adjust to screen. - NSRect screenRect = [[NSScreen mainScreen] visibleFrame]; - if (rect.origin.x < screenRect.origin.x) { - rect.origin.x = screenRect.origin.x; - } - if (rect.origin.y < screenRect.origin.y) { - rect.origin.y = screenRect.origin.y; - } - CGFloat xMostOfScreen = screenRect.origin.x + screenRect.size.width; - CGFloat yMostOfScreen = screenRect.origin.y + screenRect.size.height; - CGFloat xMost = rect.origin.x + rect.size.width; - CGFloat yMost = rect.origin.y + rect.size.height; - if (xMostOfScreen < xMost) { - rect.origin.x -= xMost - xMostOfScreen; - } - if (yMostOfScreen < yMost) { - rect.origin.y -= yMost - yMostOfScreen; - } - - [self setFrame:rect display:[self isVisible]]; -} - -@end - -class ComplexTextInputPanelPrivate : public ComplexTextInputPanel -{ -public: - ComplexTextInputPanelPrivate(); - - virtual void InterpretKeyEvent(void* aEvent, nsAString& aOutText); - virtual bool IsInComposition(); - virtual void PlacePanel(int32_t x, int32_t y); - virtual void* GetInputContext() { return [mPanel inputContext]; } - virtual void CancelComposition() { [mPanel cancelComposition]; } - -private: - ~ComplexTextInputPanelPrivate(); - ComplexTextInputPanelImpl* mPanel; -}; - -ComplexTextInputPanelPrivate::ComplexTextInputPanelPrivate() -{ - mPanel = [[ComplexTextInputPanelImpl alloc] init]; -} - -ComplexTextInputPanelPrivate::~ComplexTextInputPanelPrivate() -{ - [mPanel release]; -} - -ComplexTextInputPanel* -ComplexTextInputPanel::GetSharedComplexTextInputPanel() -{ - static ComplexTextInputPanelPrivate *sComplexTextInputPanelPrivate; - if (!sComplexTextInputPanelPrivate) { - sComplexTextInputPanelPrivate = new ComplexTextInputPanelPrivate(); - } - return sComplexTextInputPanelPrivate; -} - -void -ComplexTextInputPanelPrivate::InterpretKeyEvent(void* aEvent, nsAString& aOutText) -{ - NSString* textString = nil; - [mPanel interpretKeyEvent:(NSEvent*)aEvent string:&textString]; - - if (textString) { - nsCocoaUtils::GetStringForNSString(textString, aOutText); - } -} - -bool -ComplexTextInputPanelPrivate::IsInComposition() -{ - return !![mPanel inComposition]; -} - -void -ComplexTextInputPanelPrivate::PlacePanel(int32_t x, int32_t y) -{ - [mPanel adjustTo:NSMakePoint(x, y)]; -} diff --git a/widget/cocoa/CustomCocoaEvents.h b/widget/cocoa/CustomCocoaEvents.h deleted file mode 100644 index 0043f0d695..0000000000 --- a/widget/cocoa/CustomCocoaEvents.h +++ /dev/null @@ -1,18 +0,0 @@ -/* 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/. */ - -/* - * This file defines constants to be used in the "subtype" field of - * NSApplicationDefined type NSEvents. - */ - -#ifndef WIDGET_COCOA_CUSTOMCOCOAEVENTS_H_ -#define WIDGET_COCOA_CUSTOMCOCOAEVENTS_H_ - -// Empty event, just used for prodding the event loop into responding. -const short kEventSubtypeNone = 0; -// Tracer event, used for timing the event loop responsiveness. -const short kEventSubtypeTrace = 1; - -#endif /* WIDGET_COCOA_CUSTOMCOCOAEVENTS_H_ */ diff --git a/widget/cocoa/GfxInfo.h b/widget/cocoa/GfxInfo.h deleted file mode 100644 index 05bdad158b..0000000000 --- a/widget/cocoa/GfxInfo.h +++ /dev/null @@ -1,95 +0,0 @@ -/* vim: se cin sw=2 ts=2 et : */ -/* -*- 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_GfxInfo_h__ -#define __mozilla_widget_GfxInfo_h__ - -#include "GfxInfoBase.h" - -#include "nsString.h" - -namespace mozilla { -namespace widget { - -class GfxInfo : public GfxInfoBase -{ -public: - - GfxInfo(); - // We only declare the subset of nsIGfxInfo that we actually implement. The - // rest is brought forward from GfxInfoBase. - NS_IMETHOD GetD2DEnabled(bool *aD2DEnabled) override; - NS_IMETHOD GetDWriteEnabled(bool *aDWriteEnabled) override; - NS_IMETHOD GetDWriteVersion(nsAString & aDwriteVersion) override; - NS_IMETHOD GetCleartypeParameters(nsAString & aCleartypeParams) override; - NS_IMETHOD GetAdapterDescription(nsAString & aAdapterDescription) override; - NS_IMETHOD GetAdapterDriver(nsAString & aAdapterDriver) override; - NS_IMETHOD GetAdapterVendorID(nsAString & aAdapterVendorID) override; - NS_IMETHOD GetAdapterDeviceID(nsAString & aAdapterDeviceID) override; - NS_IMETHOD GetAdapterSubsysID(nsAString & aAdapterSubsysID) override; - NS_IMETHOD GetAdapterRAM(nsAString & aAdapterRAM) override; - NS_IMETHOD GetAdapterDriverVersion(nsAString & aAdapterDriverVersion) override; - NS_IMETHOD GetAdapterDriverDate(nsAString & aAdapterDriverDate) override; - NS_IMETHOD GetAdapterDescription2(nsAString & aAdapterDescription) override; - NS_IMETHOD GetAdapterDriver2(nsAString & aAdapterDriver) override; - NS_IMETHOD GetAdapterVendorID2(nsAString & aAdapterVendorID) override; - NS_IMETHOD GetAdapterDeviceID2(nsAString & aAdapterDeviceID) override; - NS_IMETHOD GetAdapterSubsysID2(nsAString & aAdapterSubsysID) override; - NS_IMETHOD GetAdapterRAM2(nsAString & aAdapterRAM) override; - NS_IMETHOD GetAdapterDriverVersion2(nsAString & aAdapterDriverVersion) override; - NS_IMETHOD GetAdapterDriverDate2(nsAString & aAdapterDriverDate) override; - NS_IMETHOD GetIsGPU2Active(bool *aIsGPU2Active) override; - - using GfxInfoBase::GetFeatureStatus; - using GfxInfoBase::GetFeatureSuggestedDriverVersion; - using GfxInfoBase::GetWebGLParameter; - - virtual nsresult Init() override; - -#ifdef DEBUG - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIGFXINFODEBUG -#endif - - virtual uint32_t OperatingSystemVersion() override { return mOSXVersion; } - - nsresult FindMonitors(JSContext* cx, JS::HandleObject array) override; - -protected: - - virtual ~GfxInfo() {} - - virtual nsresult GetFeatureStatusImpl(int32_t aFeature, - int32_t *aStatus, - nsAString & aSuggestedDriverVersion, - const nsTArray<GfxDriverInfo>& aDriverInfo, - nsACString &aFailureId, - OperatingSystem* aOS = nullptr) override; - virtual const nsTArray<GfxDriverInfo>& GetGfxDriverInfo() override; - -private: - - void GetDeviceInfo(); - void GetSelectedCityInfo(); - void AddCrashReportAnnotations(); - - nsString mAdapterRAMString; - nsString mDeviceID; - nsString mDriverVersion; - nsString mDriverDate; - nsString mDeviceKey; - - nsString mAdapterVendorID; - nsString mAdapterDeviceID; - - uint32_t mOSXVersion; -}; - -} // namespace widget -} // namespace mozilla - -#endif /* __mozilla_widget_GfxInfo_h__ */ diff --git a/widget/cocoa/GfxInfo.mm b/widget/cocoa/GfxInfo.mm deleted file mode 100644 index 97bb4c8324..0000000000 --- a/widget/cocoa/GfxInfo.mm +++ /dev/null @@ -1,426 +0,0 @@ -/* -*- 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/. */ - -#include <OpenGL/OpenGL.h> -#include <OpenGL/CGLRenderers.h> - -#include "mozilla/ArrayUtils.h" - -#include "GfxInfo.h" -#include "nsUnicharUtils.h" -#include "nsCocoaFeatures.h" -#include "mozilla/Preferences.h" -#include <algorithm> - -#import <Foundation/Foundation.h> -#import <IOKit/IOKitLib.h> -#import <Cocoa/Cocoa.h> - -using namespace mozilla; -using namespace mozilla::widget; - -#ifdef DEBUG -NS_IMPL_ISUPPORTS_INHERITED(GfxInfo, GfxInfoBase, nsIGfxInfoDebug) -#endif - -GfxInfo::GfxInfo() -{ -} - -static OperatingSystem -OSXVersionToOperatingSystem(uint32_t aOSXVersion) { - switch (nsCocoaFeatures::ExtractMajorVersion(aOSXVersion)) { - case 10: - switch (nsCocoaFeatures::ExtractMinorVersion(aOSXVersion)) { - case 6: - return OperatingSystem::OSX10_6; - case 7: - return OperatingSystem::OSX10_7; - case 8: - return OperatingSystem::OSX10_8; - case 9: - return OperatingSystem::OSX10_9; - case 10: - return OperatingSystem::OSX10_10; - case 11: - return OperatingSystem::OSX10_11; - case 12: - return OperatingSystem::OSX10_12; - case 13: - return OperatingSystem::OSX10_13; - case 14: - return OperatingSystem::OSX10_14; - case 15: - return OperatingSystem::OSX10_15; - case 16: - // Depending on the SDK version, we either get 10.16 or 11.0. - // Normalize this to 11.0. - return OperatingSystem::OSX11_0; - default: - break; - } - break; - case 11: - switch (nsCocoaFeatures::ExtractMinorVersion(aOSXVersion)) { - case 0: - return OperatingSystem::OSX11_0; - default: - break; - } - break; - } - - return OperatingSystem::Unknown; -} -// The following three functions are derived from Chromium code -static CFTypeRef SearchPortForProperty(io_registry_entry_t dspPort, - CFStringRef propertyName) -{ - return IORegistryEntrySearchCFProperty(dspPort, - kIOServicePlane, - propertyName, - kCFAllocatorDefault, - kIORegistryIterateRecursively | - kIORegistryIterateParents); -} - -static uint32_t IntValueOfCFData(CFDataRef d) -{ - uint32_t value = 0; - - if (d) { - const uint32_t *vp = reinterpret_cast<const uint32_t*>(CFDataGetBytePtr(d)); - if (vp != NULL) - value = *vp; - } - - return value; -} - -void -GfxInfo::GetDeviceInfo() -{ - io_registry_entry_t dsp_port = CGDisplayIOServicePort(kCGDirectMainDisplay); - CFTypeRef vendor_id_ref = SearchPortForProperty(dsp_port, CFSTR("vendor-id")); - if (vendor_id_ref) { - mAdapterVendorID.AppendPrintf("0x%04x", IntValueOfCFData((CFDataRef)vendor_id_ref)); - CFRelease(vendor_id_ref); - } - CFTypeRef device_id_ref = SearchPortForProperty(dsp_port, CFSTR("device-id")); - if (device_id_ref) { - mAdapterDeviceID.AppendPrintf("0x%04x", IntValueOfCFData((CFDataRef)device_id_ref)); - CFRelease(device_id_ref); - } -} - -nsresult -GfxInfo::Init() -{ - nsresult rv = GfxInfoBase::Init(); - - // Calling CGLQueryRendererInfo causes us to switch to the discrete GPU - // even when we don't want to. We'll avoid doing so for now and just - // use the device ids. - - GetDeviceInfo(); - - AddCrashReportAnnotations(); - - mOSXVersion = nsCocoaFeatures::macOSVersion(); - - return rv; -} - -NS_IMETHODIMP -GfxInfo::GetD2DEnabled(bool *aEnabled) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetDWriteEnabled(bool *aEnabled) -{ - return NS_ERROR_FAILURE; -} - -/* readonly attribute DOMString DWriteVersion; */ -NS_IMETHODIMP -GfxInfo::GetDWriteVersion(nsAString & aDwriteVersion) -{ - return NS_ERROR_FAILURE; -} - -/* readonly attribute DOMString cleartypeParameters; */ -NS_IMETHODIMP -GfxInfo::GetCleartypeParameters(nsAString & aCleartypeParams) -{ - return NS_ERROR_FAILURE; -} - -/* readonly attribute DOMString adapterDescription; */ -NS_IMETHODIMP -GfxInfo::GetAdapterDescription(nsAString & aAdapterDescription) -{ - aAdapterDescription.AssignLiteral(""); - return NS_OK; -} - -/* readonly attribute DOMString adapterDescription2; */ -NS_IMETHODIMP -GfxInfo::GetAdapterDescription2(nsAString & aAdapterDescription) -{ - return NS_ERROR_FAILURE; -} - -/* readonly attribute DOMString adapterRAM; */ -NS_IMETHODIMP -GfxInfo::GetAdapterRAM(nsAString & aAdapterRAM) -{ - aAdapterRAM = mAdapterRAMString; - return NS_OK; -} - -/* readonly attribute DOMString adapterRAM2; */ -NS_IMETHODIMP -GfxInfo::GetAdapterRAM2(nsAString & aAdapterRAM) -{ - return NS_ERROR_FAILURE; -} - -/* readonly attribute DOMString adapterDriver; */ -NS_IMETHODIMP -GfxInfo::GetAdapterDriver(nsAString & aAdapterDriver) -{ - aAdapterDriver.AssignLiteral(""); - return NS_OK; -} - -/* readonly attribute DOMString adapterDriver2; */ -NS_IMETHODIMP -GfxInfo::GetAdapterDriver2(nsAString & aAdapterDriver) -{ - return NS_ERROR_FAILURE; -} - -/* readonly attribute DOMString adapterDriverVersion; */ -NS_IMETHODIMP -GfxInfo::GetAdapterDriverVersion(nsAString & aAdapterDriverVersion) -{ - aAdapterDriverVersion.AssignLiteral(""); - return NS_OK; -} - -/* readonly attribute DOMString adapterDriverVersion2; */ -NS_IMETHODIMP -GfxInfo::GetAdapterDriverVersion2(nsAString & aAdapterDriverVersion) -{ - return NS_ERROR_FAILURE; -} - -/* readonly attribute DOMString adapterDriverDate; */ -NS_IMETHODIMP -GfxInfo::GetAdapterDriverDate(nsAString & aAdapterDriverDate) -{ - aAdapterDriverDate.AssignLiteral(""); - return NS_OK; -} - -/* readonly attribute DOMString adapterDriverDate2; */ -NS_IMETHODIMP -GfxInfo::GetAdapterDriverDate2(nsAString & aAdapterDriverDate) -{ - return NS_ERROR_FAILURE; -} - -/* readonly attribute DOMString adapterVendorID; */ -NS_IMETHODIMP -GfxInfo::GetAdapterVendorID(nsAString & aAdapterVendorID) -{ - aAdapterVendorID = mAdapterVendorID; - return NS_OK; -} - -/* readonly attribute DOMString adapterVendorID2; */ -NS_IMETHODIMP -GfxInfo::GetAdapterVendorID2(nsAString & aAdapterVendorID) -{ - return NS_ERROR_FAILURE; -} - -/* readonly attribute DOMString adapterDeviceID; */ -NS_IMETHODIMP -GfxInfo::GetAdapterDeviceID(nsAString & aAdapterDeviceID) -{ - aAdapterDeviceID = mAdapterDeviceID; - return NS_OK; -} - -/* readonly attribute DOMString adapterDeviceID2; */ -NS_IMETHODIMP -GfxInfo::GetAdapterDeviceID2(nsAString & aAdapterDeviceID) -{ - return NS_ERROR_FAILURE; -} - -/* readonly attribute DOMString adapterSubsysID; */ -NS_IMETHODIMP -GfxInfo::GetAdapterSubsysID(nsAString & aAdapterSubsysID) -{ - return NS_ERROR_FAILURE; -} - -/* readonly attribute DOMString adapterSubsysID2; */ -NS_IMETHODIMP -GfxInfo::GetAdapterSubsysID2(nsAString & aAdapterSubsysID) -{ - return NS_ERROR_FAILURE; -} - -/* readonly attribute boolean isGPU2Active; */ -NS_IMETHODIMP -GfxInfo::GetIsGPU2Active(bool* aIsGPU2Active) -{ - return NS_ERROR_FAILURE; -} - -void -GfxInfo::AddCrashReportAnnotations() -{ - /*** STUB ***/ -} - -// We don't support checking driver versions on Mac. -#define IMPLEMENT_MAC_DRIVER_BLOCKLIST(os, vendor, device, features, blockOn, ruleId) \ - APPEND_TO_DRIVER_BLOCKLIST(os, vendor, device, features, blockOn, \ - DRIVER_COMPARISON_IGNORED, V(0,0,0,0), ruleId, "") - - -const nsTArray<GfxDriverInfo>& -GfxInfo::GetGfxDriverInfo() -{ - if (!mDriverInfo->Length()) { - IMPLEMENT_MAC_DRIVER_BLOCKLIST(OperatingSystem::OSX, - (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorATI), GfxDriverInfo::allDevices, - nsIGfxInfo::FEATURE_WEBGL_MSAA, nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION, "FEATURE_FAILURE_MAC_ATI_NO_MSAA"); - IMPLEMENT_MAC_DRIVER_BLOCKLIST(OperatingSystem::OSX, - (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorATI), (GfxDeviceFamily*) GfxDriverInfo::GetDeviceFamily(RadeonX1000), - nsIGfxInfo::FEATURE_OPENGL_LAYERS, nsIGfxInfo::FEATURE_BLOCKED_DEVICE, "FEATURE_FAILURE_MAC_RADEONX1000_NO_TEXTURE2D"); - IMPLEMENT_MAC_DRIVER_BLOCKLIST(OperatingSystem::OSX, - (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), (GfxDeviceFamily*) GfxDriverInfo::GetDeviceFamily(Geforce7300GT), - nsIGfxInfo::FEATURE_WEBGL_OPENGL, nsIGfxInfo::FEATURE_BLOCKED_DEVICE, "FEATURE_FAILURE_MAC_7300_NO_WEBGL"); - } - return *mDriverInfo; -} - -nsresult -GfxInfo::GetFeatureStatusImpl(int32_t aFeature, - int32_t* aStatus, - nsAString& aSuggestedDriverVersion, - const nsTArray<GfxDriverInfo>& aDriverInfo, - nsACString& aFailureId, - OperatingSystem* aOS /* = nullptr */) -{ - NS_ENSURE_ARG_POINTER(aStatus); - aSuggestedDriverVersion.SetIsVoid(true); - *aStatus = nsIGfxInfo::FEATURE_STATUS_UNKNOWN; - OperatingSystem os = OSXVersionToOperatingSystem(mOSXVersion); - if (aOS) - *aOS = os; - - if (mShutdownOccurred) { - return NS_OK; - } - - // Don't evaluate special cases when we're evaluating the downloaded blocklist. - if (!aDriverInfo.Length()) { - if (aFeature == nsIGfxInfo::FEATURE_WEBGL_MSAA) { - // Blacklist all ATI cards on OSX, except for - // 0x6760 and 0x9488 - if (mAdapterVendorID.Equals(GfxDriverInfo::GetDeviceVendor(VendorATI), nsCaseInsensitiveStringComparator()) && - (mAdapterDeviceID.LowerCaseEqualsLiteral("0x6760") || - mAdapterDeviceID.LowerCaseEqualsLiteral("0x9488"))) { - *aStatus = nsIGfxInfo::FEATURE_STATUS_OK; - return NS_OK; - } - } else if (aFeature == nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION) { - // See bug 1249659 - switch(os) { - case OperatingSystem::OSX10_5: - case OperatingSystem::OSX10_6: - case OperatingSystem::OSX10_7: - *aStatus = nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION; - aFailureId = "FEATURE_FAILURE_CANVAS_OSX_VERSION"; - break; - default: - *aStatus = nsIGfxInfo::FEATURE_STATUS_OK; - break; - } - return NS_OK; - } - } - - return GfxInfoBase::GetFeatureStatusImpl(aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aFailureId, &os); -} - -nsresult -GfxInfo::FindMonitors(JSContext* aCx, JS::HandleObject aOutArray) -{ - // Getting the refresh rate is a little hard on OS X. We could use - // CVDisplayLinkGetNominalOutputVideoRefreshPeriod, but that's a little - // involved. Ideally we could query it from vsync. For now, we leave it out. - int32_t deviceCount = 0; - for (NSScreen* screen in [NSScreen screens]) { - NSRect rect = [screen frame]; - - JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx)); - - JS::Rooted<JS::Value> screenWidth(aCx, JS::Int32Value((int)rect.size.width)); - JS_SetProperty(aCx, obj, "screenWidth", screenWidth); - - JS::Rooted<JS::Value> screenHeight(aCx, JS::Int32Value((int)rect.size.height)); - JS_SetProperty(aCx, obj, "screenHeight", screenHeight); - - JS::Rooted<JS::Value> scale(aCx, JS::NumberValue(nsCocoaUtils::GetBackingScaleFactor(screen))); - JS_SetProperty(aCx, obj, "scale", scale); - - JS::Rooted<JS::Value> element(aCx, JS::ObjectValue(*obj)); - JS_SetElement(aCx, aOutArray, deviceCount++, element); - } - return NS_OK; -} - -#ifdef DEBUG - -// Implement nsIGfxInfoDebug - -/* void spoofVendorID (in DOMString aVendorID); */ -NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString & aVendorID) -{ - mAdapterVendorID = aVendorID; - return NS_OK; -} - -/* void spoofDeviceID (in unsigned long aDeviceID); */ -NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString & aDeviceID) -{ - mAdapterDeviceID = aDeviceID; - return NS_OK; -} - -/* void spoofDriverVersion (in DOMString aDriverVersion); */ -NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString & aDriverVersion) -{ - mDriverVersion = aDriverVersion; - return NS_OK; -} - -/* void spoofOSVersion (in unsigned long aVersion); */ -NS_IMETHODIMP GfxInfo::SpoofOSVersion(uint32_t aVersion) -{ - mOSXVersion = aVersion; - return NS_OK; -} - -#endif diff --git a/widget/cocoa/NativeKeyBindings.h b/widget/cocoa/NativeKeyBindings.h deleted file mode 100644 index d1ba2c3701..0000000000 --- a/widget/cocoa/NativeKeyBindings.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- 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_NativeKeyBindings_h_ -#define mozilla_widget_NativeKeyBindings_h_ - -#import <Cocoa/Cocoa.h> -#include "mozilla/Attributes.h" -#include "mozilla/EventForwards.h" -#include "nsDataHashtable.h" -#include "nsIWidget.h" - -namespace mozilla { -namespace widget { - -typedef nsDataHashtable<nsPtrHashKey<struct objc_selector>, CommandInt> - SelectorCommandHashtable; - -class NativeKeyBindings final -{ - typedef nsIWidget::NativeKeyBindingsType NativeKeyBindingsType; - typedef nsIWidget::DoCommandCallback DoCommandCallback; - -public: - static NativeKeyBindings* GetInstance(NativeKeyBindingsType aType); - static void Shutdown(); - - void Init(NativeKeyBindingsType aType); - - bool Execute(const WidgetKeyboardEvent& aEvent, - DoCommandCallback aCallback, - void* aCallbackData); - -private: - NativeKeyBindings(); - - SelectorCommandHashtable mSelectorToCommand; - - static NativeKeyBindings* sInstanceForSingleLineEditor; - static NativeKeyBindings* sInstanceForMultiLineEditor; -}; // NativeKeyBindings - -} // namespace widget -} // namespace mozilla - -#endif // mozilla_widget_NativeKeyBindings_h_ diff --git a/widget/cocoa/NativeKeyBindings.mm b/widget/cocoa/NativeKeyBindings.mm deleted file mode 100644 index 2f4ecadff0..0000000000 --- a/widget/cocoa/NativeKeyBindings.mm +++ /dev/null @@ -1,292 +0,0 @@ -/* -*- 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/. */ - -#include "NativeKeyBindings.h" - -#include "nsTArray.h" -#include "nsCocoaUtils.h" -#include "mozilla/Logging.h" -#include "mozilla/TextEvents.h" - -namespace mozilla { -namespace widget { - -PRLogModuleInfo* gNativeKeyBindingsLog = nullptr; - -NativeKeyBindings* NativeKeyBindings::sInstanceForSingleLineEditor = nullptr; -NativeKeyBindings* NativeKeyBindings::sInstanceForMultiLineEditor = nullptr; - -// static -NativeKeyBindings* -NativeKeyBindings::GetInstance(NativeKeyBindingsType aType) -{ - switch (aType) { - case nsIWidget::NativeKeyBindingsForSingleLineEditor: - if (!sInstanceForSingleLineEditor) { - sInstanceForSingleLineEditor = new NativeKeyBindings(); - sInstanceForSingleLineEditor->Init(aType); - } - return sInstanceForSingleLineEditor; - case nsIWidget::NativeKeyBindingsForMultiLineEditor: - case nsIWidget::NativeKeyBindingsForRichTextEditor: - if (!sInstanceForMultiLineEditor) { - sInstanceForMultiLineEditor = new NativeKeyBindings(); - sInstanceForMultiLineEditor->Init(aType); - } - return sInstanceForMultiLineEditor; - default: - MOZ_CRASH("Not implemented"); - return nullptr; - } -} - -// static -void -NativeKeyBindings::Shutdown() -{ - delete sInstanceForSingleLineEditor; - sInstanceForSingleLineEditor = nullptr; - delete sInstanceForMultiLineEditor; - sInstanceForMultiLineEditor = nullptr; -} - -NativeKeyBindings::NativeKeyBindings() -{ -} - -#define SEL_TO_COMMAND(aSel, aCommand) \ - mSelectorToCommand.Put( \ - reinterpret_cast<struct objc_selector *>(@selector(aSel)), aCommand) - -void -NativeKeyBindings::Init(NativeKeyBindingsType aType) -{ - if (!gNativeKeyBindingsLog) { - gNativeKeyBindingsLog = PR_NewLogModule("NativeKeyBindings"); - } - - MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, - ("%p NativeKeyBindings::Init", this)); - - // Many selectors have a one-to-one mapping to a Gecko command. Those mappings - // are registered in mSelectorToCommand. - - // Selectors from NSResponder's "Responding to Action Messages" section and - // from NSText's "Action Methods for Editing" section - - // TODO: Improves correctness of left / right meaning - // TODO: Add real paragraph motions - - // SEL_TO_COMMAND(cancelOperation:, ); - // SEL_TO_COMMAND(capitalizeWord:, ); - // SEL_TO_COMMAND(centerSelectionInVisibleArea:, ); - // SEL_TO_COMMAND(changeCaseOfLetter:, ); - // SEL_TO_COMMAND(complete:, ); - SEL_TO_COMMAND(copy:, CommandCopy); - // SEL_TO_COMMAND(copyFont:, ); - // SEL_TO_COMMAND(copyRuler:, ); - SEL_TO_COMMAND(cut:, CommandCut); - SEL_TO_COMMAND(delete:, CommandDelete); - SEL_TO_COMMAND(deleteBackward:, CommandDeleteCharBackward); - // SEL_TO_COMMAND(deleteBackwardByDecomposingPreviousCharacter:, ); - SEL_TO_COMMAND(deleteForward:, CommandDeleteCharForward); - - // TODO: deleteTo* selectors are also supposed to add text to a kill buffer - SEL_TO_COMMAND(deleteToBeginningOfLine:, CommandDeleteToBeginningOfLine); - SEL_TO_COMMAND(deleteToBeginningOfParagraph:, CommandDeleteToBeginningOfLine); - SEL_TO_COMMAND(deleteToEndOfLine:, CommandDeleteToEndOfLine); - SEL_TO_COMMAND(deleteToEndOfParagraph:, CommandDeleteToEndOfLine); - // SEL_TO_COMMAND(deleteToMark:, ); - - SEL_TO_COMMAND(deleteWordBackward:, CommandDeleteWordBackward); - SEL_TO_COMMAND(deleteWordForward:, CommandDeleteWordForward); - // SEL_TO_COMMAND(indent:, ); - // SEL_TO_COMMAND(insertBacktab:, ); - // SEL_TO_COMMAND(insertContainerBreak:, ); - // SEL_TO_COMMAND(insertLineBreak:, ); - // SEL_TO_COMMAND(insertNewline:, ); - // SEL_TO_COMMAND(insertNewlineIgnoringFieldEditor:, ); - // SEL_TO_COMMAND(insertParagraphSeparator:, ); - // SEL_TO_COMMAND(insertTab:, ); - // SEL_TO_COMMAND(insertTabIgnoringFieldEditor:, ); - // SEL_TO_COMMAND(insertDoubleQuoteIgnoringSubstitution:, ); - // SEL_TO_COMMAND(insertSingleQuoteIgnoringSubstitution:, ); - // SEL_TO_COMMAND(lowercaseWord:, ); - SEL_TO_COMMAND(moveBackward:, CommandCharPrevious); - SEL_TO_COMMAND(moveBackwardAndModifySelection:, CommandSelectCharPrevious); - if (aType == nsIWidget::NativeKeyBindingsForSingleLineEditor) { - SEL_TO_COMMAND(moveDown:, CommandEndLine); - } else { - SEL_TO_COMMAND(moveDown:, CommandLineNext); - } - SEL_TO_COMMAND(moveDownAndModifySelection:, CommandSelectLineNext); - SEL_TO_COMMAND(moveForward:, CommandCharNext); - SEL_TO_COMMAND(moveForwardAndModifySelection:, CommandSelectCharNext); - SEL_TO_COMMAND(moveLeft:, CommandCharPrevious); - SEL_TO_COMMAND(moveLeftAndModifySelection:, CommandSelectCharPrevious); - SEL_TO_COMMAND(moveParagraphBackwardAndModifySelection:, - CommandSelectBeginLine); - SEL_TO_COMMAND(moveParagraphForwardAndModifySelection:, CommandSelectEndLine); - SEL_TO_COMMAND(moveRight:, CommandCharNext); - SEL_TO_COMMAND(moveRightAndModifySelection:, CommandSelectCharNext); - SEL_TO_COMMAND(moveToBeginningOfDocument:, CommandMoveTop); - SEL_TO_COMMAND(moveToBeginningOfDocumentAndModifySelection:, - CommandSelectTop); - SEL_TO_COMMAND(moveToBeginningOfLine:, CommandBeginLine); - SEL_TO_COMMAND(moveToBeginningOfLineAndModifySelection:, - CommandSelectBeginLine); - SEL_TO_COMMAND(moveToBeginningOfParagraph:, CommandBeginLine); - SEL_TO_COMMAND(moveToBeginningOfParagraphAndModifySelection:, - CommandSelectBeginLine); - SEL_TO_COMMAND(moveToEndOfDocument:, CommandMoveBottom); - SEL_TO_COMMAND(moveToEndOfDocumentAndModifySelection:, CommandSelectBottom); - SEL_TO_COMMAND(moveToEndOfLine:, CommandEndLine); - SEL_TO_COMMAND(moveToEndOfLineAndModifySelection:, CommandSelectEndLine); - SEL_TO_COMMAND(moveToEndOfParagraph:, CommandEndLine); - SEL_TO_COMMAND(moveToEndOfParagraphAndModifySelection:, CommandSelectEndLine); - SEL_TO_COMMAND(moveToLeftEndOfLine:, CommandBeginLine); - SEL_TO_COMMAND(moveToLeftEndOfLineAndModifySelection:, - CommandSelectBeginLine); - SEL_TO_COMMAND(moveToRightEndOfLine:, CommandEndLine); - SEL_TO_COMMAND(moveToRightEndOfLineAndModifySelection:, CommandSelectEndLine); - if (aType == nsIWidget::NativeKeyBindingsForSingleLineEditor) { - SEL_TO_COMMAND(moveUp:, CommandBeginLine); - } else { - SEL_TO_COMMAND(moveUp:, CommandLinePrevious); - } - SEL_TO_COMMAND(moveUpAndModifySelection:, CommandSelectLinePrevious); - SEL_TO_COMMAND(moveWordBackward:, CommandWordPrevious); - SEL_TO_COMMAND(moveWordBackwardAndModifySelection:, - CommandSelectWordPrevious); - SEL_TO_COMMAND(moveWordForward:, CommandWordNext); - SEL_TO_COMMAND(moveWordForwardAndModifySelection:, CommandSelectWordNext); - SEL_TO_COMMAND(moveWordLeft:, CommandWordPrevious); - SEL_TO_COMMAND(moveWordLeftAndModifySelection:, CommandSelectWordPrevious); - SEL_TO_COMMAND(moveWordRight:, CommandWordNext); - SEL_TO_COMMAND(moveWordRightAndModifySelection:, CommandSelectWordNext); - SEL_TO_COMMAND(pageDown:, CommandMovePageDown); - SEL_TO_COMMAND(pageDownAndModifySelection:, CommandSelectPageDown); - SEL_TO_COMMAND(pageUp:, CommandMovePageUp); - SEL_TO_COMMAND(pageUpAndModifySelection:, CommandSelectPageUp); - SEL_TO_COMMAND(paste:, CommandPaste); - // SEL_TO_COMMAND(pasteFont:, ); - // SEL_TO_COMMAND(pasteRuler:, ); - SEL_TO_COMMAND(scrollLineDown:, CommandScrollLineDown); - SEL_TO_COMMAND(scrollLineUp:, CommandScrollLineUp); - SEL_TO_COMMAND(scrollPageDown:, CommandScrollPageDown); - SEL_TO_COMMAND(scrollPageUp:, CommandScrollPageUp); - SEL_TO_COMMAND(scrollToBeginningOfDocument:, CommandScrollTop); - SEL_TO_COMMAND(scrollToEndOfDocument:, CommandScrollBottom); - SEL_TO_COMMAND(selectAll:, CommandSelectAll); - // selectLine: is complex, see KeyDown - if (aType == nsIWidget::NativeKeyBindingsForSingleLineEditor) { - SEL_TO_COMMAND(selectParagraph:, CommandSelectAll); - } - // SEL_TO_COMMAND(selectToMark:, ); - // selectWord: is complex, see KeyDown - // SEL_TO_COMMAND(setMark:, ); - // SEL_TO_COMMAND(showContextHelp:, ); - // SEL_TO_COMMAND(supplementalTargetForAction:sender:, ); - // SEL_TO_COMMAND(swapWithMark:, ); - // SEL_TO_COMMAND(transpose:, ); - // SEL_TO_COMMAND(transposeWords:, ); - // SEL_TO_COMMAND(uppercaseWord:, ); - // SEL_TO_COMMAND(yank:, ); -} - -#undef SEL_TO_COMMAND - -bool -NativeKeyBindings::Execute(const WidgetKeyboardEvent& aEvent, - DoCommandCallback aCallback, - void* aCallbackData) -{ - MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, - ("%p NativeKeyBindings::KeyPress", this)); - - // Recover the current event, which should always be the key down we are - // responding to. - - NSEvent* cocoaEvent = reinterpret_cast<NSEvent*>(aEvent.mNativeKeyEvent); - - if (!cocoaEvent || [cocoaEvent type] != NSKeyDown) { - MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, - ("%p NativeKeyBindings::KeyPress, no Cocoa key down event", this)); - - return false; - } - - MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, - ("%p NativeKeyBindings::KeyPress, interpreting", this)); - - AutoTArray<KeyBindingsCommand, 2> bindingCommands; - nsCocoaUtils::GetCommandsFromKeyEvent(cocoaEvent, bindingCommands); - - MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, - ("%p NativeKeyBindings::KeyPress, bindingCommands=%u", - this, bindingCommands.Length())); - - AutoTArray<Command, 4> geckoCommands; - - for (uint32_t i = 0; i < bindingCommands.Length(); i++) { - SEL selector = bindingCommands[i].selector; - - if (MOZ_LOG_TEST(gNativeKeyBindingsLog, LogLevel::Info)) { - NSString* selectorString = NSStringFromSelector(selector); - nsAutoString nsSelectorString; - nsCocoaUtils::GetStringForNSString(selectorString, nsSelectorString); - - MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, - ("%p NativeKeyBindings::KeyPress, selector=%s", - this, NS_LossyConvertUTF16toASCII(nsSelectorString).get())); - } - - // Try to find a simple mapping in the hashtable - Command geckoCommand = static_cast<Command>(mSelectorToCommand.Get( - reinterpret_cast<struct objc_selector*>(selector))); - - if (geckoCommand) { - geckoCommands.AppendElement(geckoCommand); - } else if (selector == @selector(selectLine:)) { - // This is functional, but Cocoa's version is direction-less in that - // selection direction is not determined until some future directed action - // is taken. See bug 282097, comment 79 for more details. - geckoCommands.AppendElement(CommandBeginLine); - geckoCommands.AppendElement(CommandSelectEndLine); - } else if (selector == @selector(selectWord:)) { - // This is functional, but Cocoa's version is direction-less in that - // selection direction is not determined until some future directed action - // is taken. See bug 282097, comment 79 for more details. - geckoCommands.AppendElement(CommandWordPrevious); - geckoCommands.AppendElement(CommandSelectWordNext); - } - } - - if (geckoCommands.IsEmpty()) { - MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, - ("%p NativeKeyBindings::KeyPress, handled=false", this)); - - return false; - } - - for (uint32_t i = 0; i < geckoCommands.Length(); i++) { - Command geckoCommand = geckoCommands[i]; - - MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, - ("%p NativeKeyBindings::KeyPress, command=%s", - this, WidgetKeyboardEvent::GetCommandStr(geckoCommand))); - - // Execute the Gecko command - aCallback(geckoCommand, aCallbackData); - } - - MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, - ("%p NativeKeyBindings::KeyPress, handled=true", this)); - - return true; -} - -} // namespace widget -} // namespace mozilla diff --git a/widget/cocoa/OSXNotificationCenter.h b/widget/cocoa/OSXNotificationCenter.h deleted file mode 100644 index 30767b5c55..0000000000 --- a/widget/cocoa/OSXNotificationCenter.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -*- 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 OSXNotificationCenter_h -#define OSXNotificationCenter_h - -#import <Foundation/Foundation.h> -#include "nsIAlertsService.h" -#include "imgINotificationObserver.h" -#include "nsITimer.h" -#include "nsTArray.h" -#include "mozilla/RefPtr.h" - -@class mozNotificationCenterDelegate; - -#if !defined(MAC_OS_X_VERSION_10_8) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8) -typedef NSInteger NSUserNotificationActivationType; -#endif - -namespace mozilla { - -class OSXNotificationInfo; - -class OSXNotificationCenter : public nsIAlertsService, - public nsIAlertsIconData, - public nsIAlertNotificationImageListener -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIALERTSSERVICE - NS_DECL_NSIALERTSICONDATA - NS_DECL_NSIALERTNOTIFICATIONIMAGELISTENER - - OSXNotificationCenter(); - - nsresult Init(); - void CloseAlertCocoaString(NSString *aAlertName); - void OnActivate(NSString *aAlertName, NSUserNotificationActivationType aActivationType, - unsigned long long aAdditionalActionIndex); - void ShowPendingNotification(OSXNotificationInfo *osxni); - -protected: - virtual ~OSXNotificationCenter(); - -private: - mozNotificationCenterDelegate *mDelegate; - nsTArray<RefPtr<OSXNotificationInfo> > mActiveAlerts; - nsTArray<RefPtr<OSXNotificationInfo> > mPendingAlerts; -}; - -} // namespace mozilla - -#endif // OSXNotificationCenter_h diff --git a/widget/cocoa/OSXNotificationCenter.mm b/widget/cocoa/OSXNotificationCenter.mm deleted file mode 100644 index e9e36a96be..0000000000 --- a/widget/cocoa/OSXNotificationCenter.mm +++ /dev/null @@ -1,589 +0,0 @@ -/* -*- 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/. */ - -#include "OSXNotificationCenter.h" -#import <AppKit/AppKit.h> -#include "imgIRequest.h" -#include "imgIContainer.h" -#include "nsICancelable.h" -#include "nsIStringBundle.h" -#include "nsNetUtil.h" -#import "nsCocoaUtils.h" -#include "nsComponentManagerUtils.h" -#include "nsContentUtils.h" -#include "nsObjCExceptions.h" -#include "nsString.h" -#include "nsCOMPtr.h" -#include "nsIObserver.h" - -using namespace mozilla; - -#define MAX_NOTIFICATION_NAME_LEN 5000 - -#if !defined(MAC_OS_X_VERSION_10_8) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8) -@protocol NSUserNotificationCenterDelegate -@end -static NSString * const NSUserNotificationDefaultSoundName = @"DefaultSoundName"; -enum { - NSUserNotificationActivationTypeNone = 0, - NSUserNotificationActivationTypeContentsClicked = 1, - NSUserNotificationActivationTypeActionButtonClicked = 2, -}; -#endif - -#if !defined(MAC_OS_X_VERSION_10_9) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9) -enum { - NSUserNotificationActivationTypeReplied = 3, -}; -#endif - -#if !defined(MAC_OS_X_VERSION_10_10) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10) -enum { - NSUserNotificationActivationTypeAdditionalActionClicked = 4 -}; -#endif - -@protocol FakeNSUserNotification <NSObject> -@property (copy) NSString* title; -@property (copy) NSString* subtitle; -@property (copy) NSString* informativeText; -@property (copy) NSString* actionButtonTitle; -@property (copy) NSDictionary* userInfo; -@property (copy) NSDate* deliveryDate; -@property (copy) NSTimeZone* deliveryTimeZone; -@property (copy) NSDateComponents* deliveryRepeatInterval; -@property (readonly) NSDate* actualDeliveryDate; -@property (readonly, getter=isPresented) BOOL presented; -@property (readonly, getter=isRemote) BOOL remote; -@property (copy) NSString* soundName; -@property BOOL hasActionButton; -@property (readonly) NSUserNotificationActivationType activationType; -@property (copy) NSString *otherButtonTitle; -@property (copy) NSImage *contentImage; -@end - -@protocol FakeNSUserNotificationCenter <NSObject> -+ (id<FakeNSUserNotificationCenter>)defaultUserNotificationCenter; -@property (assign) id <NSUserNotificationCenterDelegate> delegate; -@property (copy) NSArray *scheduledNotifications; -- (void)scheduleNotification:(id<FakeNSUserNotification>)notification; -- (void)removeScheduledNotification:(id<FakeNSUserNotification>)notification; -@property (readonly) NSArray *deliveredNotifications; -- (void)deliverNotification:(id<FakeNSUserNotification>)notification; -- (void)removeDeliveredNotification:(id<FakeNSUserNotification>)notification; -- (void)removeAllDeliveredNotifications; -- (void)_removeAllDisplayedNotifications; -- (void)_removeDisplayedNotification:(id<FakeNSUserNotification>)notification; -@end - -@interface mozNotificationCenterDelegate : NSObject <NSUserNotificationCenterDelegate> -{ - OSXNotificationCenter *mOSXNC; -} - - (id)initWithOSXNC:(OSXNotificationCenter*)osxnc; -@end - -@implementation mozNotificationCenterDelegate - -- (id)initWithOSXNC:(OSXNotificationCenter*)osxnc -{ - [super init]; - // We should *never* outlive this OSXNotificationCenter. - mOSXNC = osxnc; - return self; -} - -- (void)userNotificationCenter:(id<FakeNSUserNotificationCenter>)center - didDeliverNotification:(id<FakeNSUserNotification>)notification -{ - -} - -- (void)userNotificationCenter:(id<FakeNSUserNotificationCenter>)center - didActivateNotification:(id<FakeNSUserNotification>)notification -{ - unsigned long long additionalActionIndex = ULLONG_MAX; - if ([notification respondsToSelector:@selector(_alternateActionIndex)]) { - NSNumber *alternateActionIndex = [(NSObject*)notification valueForKey:@"_alternateActionIndex"]; - additionalActionIndex = [alternateActionIndex unsignedLongLongValue]; - } - mOSXNC->OnActivate([[notification userInfo] valueForKey:@"name"], - notification.activationType, - additionalActionIndex); -} - -- (BOOL)userNotificationCenter:(id<FakeNSUserNotificationCenter>)center - shouldPresentNotification:(id<FakeNSUserNotification>)notification -{ - return YES; -} - -// This is an undocumented method that we need for parity with Safari. -// Apple bug #15440664. -- (void)userNotificationCenter:(id<FakeNSUserNotificationCenter>)center - didRemoveDeliveredNotifications:(NSArray *)notifications -{ - for (id<FakeNSUserNotification> notification in notifications) { - NSString *name = [[notification userInfo] valueForKey:@"name"]; - mOSXNC->CloseAlertCocoaString(name); - } -} - -// This is an undocumented method that we need to be notified if a user clicks the close button. -- (void)userNotificationCenter:(id<FakeNSUserNotificationCenter>)center - didDismissAlert:(id<FakeNSUserNotification>)notification -{ - NSString *name = [[notification userInfo] valueForKey:@"name"]; - mOSXNC->CloseAlertCocoaString(name); -} - -@end - -namespace mozilla { - -enum { - OSXNotificationActionDisable = 0, - OSXNotificationActionSettings = 1, -}; - -class OSXNotificationInfo final : public nsISupports { -private: - virtual ~OSXNotificationInfo(); - -public: - NS_DECL_ISUPPORTS - OSXNotificationInfo(NSString *name, nsIObserver *observer, - const nsAString & alertCookie); - - NSString *mName; - nsCOMPtr<nsIObserver> mObserver; - nsString mCookie; - RefPtr<nsICancelable> mIconRequest; - id<FakeNSUserNotification> mPendingNotifiction; -}; - -NS_IMPL_ISUPPORTS0(OSXNotificationInfo) - -OSXNotificationInfo::OSXNotificationInfo(NSString *name, nsIObserver *observer, - const nsAString & alertCookie) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - NS_ASSERTION(name, "Cannot create OSXNotificationInfo without a name!"); - mName = [name retain]; - mObserver = observer; - mCookie = alertCookie; - mPendingNotifiction = nil; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -OSXNotificationInfo::~OSXNotificationInfo() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [mName release]; - [mPendingNotifiction release]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -static id<FakeNSUserNotificationCenter> GetNotificationCenter() { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - Class c = NSClassFromString(@"NSUserNotificationCenter"); - return [c performSelector:@selector(defaultUserNotificationCenter)]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -OSXNotificationCenter::OSXNotificationCenter() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - mDelegate = [[mozNotificationCenterDelegate alloc] initWithOSXNC:this]; - GetNotificationCenter().delegate = mDelegate; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -OSXNotificationCenter::~OSXNotificationCenter() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [GetNotificationCenter() removeAllDeliveredNotifications]; - [mDelegate release]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -NS_IMPL_ISUPPORTS(OSXNotificationCenter, nsIAlertsService, nsIAlertsIconData, - nsIAlertNotificationImageListener) - -nsresult OSXNotificationCenter::Init() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - return (!!NSClassFromString(@"NSUserNotification")) ? NS_OK : NS_ERROR_FAILURE; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -OSXNotificationCenter::ShowAlertNotification(const nsAString & aImageUrl, const nsAString & aAlertTitle, - const nsAString & aAlertText, bool aAlertTextClickable, - const nsAString & aAlertCookie, - nsIObserver * aAlertListener, - const nsAString & aAlertName, - const nsAString & aBidi, - const nsAString & aLang, - const nsAString & aData, - nsIPrincipal * aPrincipal, - bool aInPrivateBrowsing, - bool aRequireInteraction) -{ - nsCOMPtr<nsIAlertNotification> alert = - do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID); - NS_ENSURE_TRUE(alert, NS_ERROR_FAILURE); - nsresult rv = alert->Init(aAlertName, aImageUrl, aAlertTitle, - aAlertText, aAlertTextClickable, - aAlertCookie, aBidi, aLang, aData, - aPrincipal, aInPrivateBrowsing, - aRequireInteraction); - NS_ENSURE_SUCCESS(rv, rv); - return ShowAlert(alert, aAlertListener); -} - -NS_IMETHODIMP -OSXNotificationCenter::ShowPersistentNotification(const nsAString& aPersistentData, - nsIAlertNotification* aAlert, - nsIObserver* aAlertListener) -{ - return ShowAlert(aAlert, aAlertListener); -} - -NS_IMETHODIMP -OSXNotificationCenter::ShowAlert(nsIAlertNotification* aAlert, - nsIObserver* aAlertListener) -{ - return ShowAlertWithIconData(aAlert, aAlertListener, 0, nullptr); -} - -NS_IMETHODIMP -OSXNotificationCenter::ShowAlertWithIconData(nsIAlertNotification* aAlert, - nsIObserver* aAlertListener, - uint32_t aIconSize, - const uint8_t* aIconData) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NS_ENSURE_ARG(aAlert); - - Class unClass = NSClassFromString(@"NSUserNotification"); - id<FakeNSUserNotification> notification = [[unClass alloc] init]; - - nsAutoString title; - nsresult rv = aAlert->GetTitle(title); - NS_ENSURE_SUCCESS(rv, rv); - notification.title = nsCocoaUtils::ToNSString(title); - - nsAutoString hostPort; - rv = aAlert->GetSource(hostPort); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr<nsIStringBundle> bundle; - nsCOMPtr<nsIStringBundleService> sbs = do_GetService(NS_STRINGBUNDLE_CONTRACTID); - sbs->CreateBundle("chrome://alerts/locale/alert.properties", getter_AddRefs(bundle)); - - if (!hostPort.IsEmpty() && bundle) { - const char16_t* formatStrings[] = { hostPort.get() }; - nsXPIDLString notificationSource; - bundle->FormatStringFromName(u"source.label", - formatStrings, - ArrayLength(formatStrings), - getter_Copies(notificationSource)); - notification.subtitle = nsCocoaUtils::ToNSString(notificationSource); - } - - nsAutoString text; - rv = aAlert->GetText(text); - NS_ENSURE_SUCCESS(rv, rv); - notification.informativeText = nsCocoaUtils::ToNSString(text); - - notification.soundName = NSUserNotificationDefaultSoundName; - notification.hasActionButton = NO; - - // If this is not an application/extension alert, show additional actions dealing with permissions. - bool isActionable; - if (bundle && NS_SUCCEEDED(aAlert->GetActionable(&isActionable)) && isActionable) { - nsXPIDLString closeButtonTitle, actionButtonTitle, disableButtonTitle, settingsButtonTitle; - bundle->GetStringFromName(u"closeButton.title", - getter_Copies(closeButtonTitle)); - bundle->GetStringFromName(u"actionButton.label", - getter_Copies(actionButtonTitle)); - if (!hostPort.IsEmpty()) { - const char16_t* formatStrings[] = { hostPort.get() }; - bundle->FormatStringFromName(u"webActions.disableForOrigin.label", - formatStrings, - ArrayLength(formatStrings), - getter_Copies(disableButtonTitle)); - } - bundle->GetStringFromName(u"webActions.settings.label", - getter_Copies(settingsButtonTitle)); - - notification.otherButtonTitle = nsCocoaUtils::ToNSString(closeButtonTitle); - - // OS X 10.8 only shows action buttons if the "Alerts" style is set in - // Notification Center preferences, and doesn't support the alternate - // action menu. - if ([notification respondsToSelector:@selector(set_showsButtons:)] && - [notification respondsToSelector:@selector(set_alwaysShowAlternateActionMenu:)] && - [notification respondsToSelector:@selector(set_alternateActionButtonTitles:)]) { - - notification.hasActionButton = YES; - notification.actionButtonTitle = nsCocoaUtils::ToNSString(actionButtonTitle); - - [(NSObject*)notification setValue:@(YES) forKey:@"_showsButtons"]; - [(NSObject*)notification setValue:@(YES) forKey:@"_alwaysShowAlternateActionMenu"]; - [(NSObject*)notification setValue:@[ - nsCocoaUtils::ToNSString(disableButtonTitle), - nsCocoaUtils::ToNSString(settingsButtonTitle) - ] - forKey:@"_alternateActionButtonTitles"]; - } - } - nsAutoString name; - rv = aAlert->GetName(name); - // Don't let an alert name be more than MAX_NOTIFICATION_NAME_LEN characters. - // More than that shouldn't be necessary and userInfo (assigned to below) has - // a length limit of 16k on OS X 10.11. Exception thrown if limit exceeded. - if (name.Length() > MAX_NOTIFICATION_NAME_LEN) { - return NS_ERROR_FAILURE; - } - - NS_ENSURE_SUCCESS(rv, rv); - NSString *alertName = nsCocoaUtils::ToNSString(name); - if (!alertName) { - return NS_ERROR_FAILURE; - } - notification.userInfo = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:alertName, nil] - forKeys:[NSArray arrayWithObjects:@"name", nil]]; - - nsAutoString cookie; - rv = aAlert->GetCookie(cookie); - NS_ENSURE_SUCCESS(rv, rv); - - OSXNotificationInfo *osxni = new OSXNotificationInfo(alertName, aAlertListener, cookie); - - // Show the favicon if supported on this version of OS X. - if (aIconSize > 0 && - [notification respondsToSelector:@selector(set_identityImage:)] && - [notification respondsToSelector:@selector(set_identityImageHasBorder:)]) { - - NSData *iconData = [NSData dataWithBytes:aIconData length:aIconSize]; - NSImage *icon = [[[NSImage alloc] initWithData:iconData] autorelease]; - - [(NSObject*)notification setValue:icon forKey:@"_identityImage"]; - [(NSObject*)notification setValue:@(NO) forKey:@"_identityImageHasBorder"]; - } - - bool inPrivateBrowsing; - rv = aAlert->GetInPrivateBrowsing(&inPrivateBrowsing); - NS_ENSURE_SUCCESS(rv, rv); - - // Show the notification without waiting for an image if there is no icon URL or - // notification icons are not supported on this version of OS X. - if (![unClass instancesRespondToSelector:@selector(setContentImage:)]) { - CloseAlertCocoaString(alertName); - mActiveAlerts.AppendElement(osxni); - [GetNotificationCenter() deliverNotification:notification]; - [notification release]; - if (aAlertListener) { - aAlertListener->Observe(nullptr, "alertshow", cookie.get()); - } - } else { - mPendingAlerts.AppendElement(osxni); - osxni->mPendingNotifiction = notification; - // Wait six seconds for the image to load. - rv = aAlert->LoadImage(6000, this, osxni, - getter_AddRefs(osxni->mIconRequest)); - if (NS_WARN_IF(NS_FAILED(rv))) { - ShowPendingNotification(osxni); - } - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -OSXNotificationCenter::CloseAlert(const nsAString& aAlertName, - nsIPrincipal* aPrincipal) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NSString *alertName = nsCocoaUtils::ToNSString(aAlertName); - CloseAlertCocoaString(alertName); - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -void -OSXNotificationCenter::CloseAlertCocoaString(NSString *aAlertName) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!aAlertName) { - return; // Can't do anything without a name - } - - NSArray *notifications = [GetNotificationCenter() deliveredNotifications]; - for (id<FakeNSUserNotification> notification in notifications) { - NSString *name = [[notification userInfo] valueForKey:@"name"]; - if ([name isEqualToString:aAlertName]) { - [GetNotificationCenter() removeDeliveredNotification:notification]; - [GetNotificationCenter() _removeDisplayedNotification:notification]; - break; - } - } - - for (unsigned int i = 0; i < mActiveAlerts.Length(); i++) { - OSXNotificationInfo *osxni = mActiveAlerts[i]; - if ([aAlertName isEqualToString:osxni->mName]) { - if (osxni->mObserver) { - osxni->mObserver->Observe(nullptr, "alertfinished", osxni->mCookie.get()); - } - if (osxni->mIconRequest) { - osxni->mIconRequest->Cancel(NS_BINDING_ABORTED); - osxni->mIconRequest = nullptr; - } - mActiveAlerts.RemoveElementAt(i); - break; - } - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -OSXNotificationCenter::OnActivate(NSString *aAlertName, - NSUserNotificationActivationType aActivationType, - unsigned long long aAdditionalActionIndex) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!aAlertName) { - return; // Can't do anything without a name - } - - for (unsigned int i = 0; i < mActiveAlerts.Length(); i++) { - OSXNotificationInfo *osxni = mActiveAlerts[i]; - if ([aAlertName isEqualToString:osxni->mName]) { - if (osxni->mObserver) { - switch ((int)aActivationType) { - case NSUserNotificationActivationTypeAdditionalActionClicked: - case NSUserNotificationActivationTypeActionButtonClicked: - switch (aAdditionalActionIndex) { - case OSXNotificationActionDisable: - osxni->mObserver->Observe(nullptr, "alertdisablecallback", osxni->mCookie.get()); - break; - case OSXNotificationActionSettings: - osxni->mObserver->Observe(nullptr, "alertsettingscallback", osxni->mCookie.get()); - break; - default: - NS_WARNING("Unknown NSUserNotification additional action clicked"); - break; - } - break; - default: - osxni->mObserver->Observe(nullptr, "alertclickcallback", osxni->mCookie.get()); - break; - } - } - return; - } - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -OSXNotificationCenter::ShowPendingNotification(OSXNotificationInfo *osxni) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (osxni->mIconRequest) { - osxni->mIconRequest->Cancel(NS_BINDING_ABORTED); - osxni->mIconRequest = nullptr; - } - - CloseAlertCocoaString(osxni->mName); - - for (unsigned int i = 0; i < mPendingAlerts.Length(); i++) { - if (mPendingAlerts[i] == osxni) { - mActiveAlerts.AppendElement(osxni); - mPendingAlerts.RemoveElementAt(i); - break; - } - } - - [GetNotificationCenter() deliverNotification:osxni->mPendingNotifiction]; - - if (osxni->mObserver) { - osxni->mObserver->Observe(nullptr, "alertshow", osxni->mCookie.get()); - } - - [osxni->mPendingNotifiction release]; - osxni->mPendingNotifiction = nil; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -NS_IMETHODIMP -OSXNotificationCenter::OnImageMissing(nsISupports* aUserData) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - OSXNotificationInfo *osxni = static_cast<OSXNotificationInfo*>(aUserData); - if (osxni->mPendingNotifiction) { - // If there was an error getting the image, or the request timed out, show - // the notification without a content image. - ShowPendingNotification(osxni); - } - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -OSXNotificationCenter::OnImageReady(nsISupports* aUserData, - imgIRequest* aRequest) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - nsCOMPtr<imgIContainer> image; - nsresult rv = aRequest->GetImage(getter_AddRefs(image)); - if (NS_WARN_IF(NS_FAILED(rv) || !image)) { - return rv; - } - - OSXNotificationInfo *osxni = static_cast<OSXNotificationInfo*>(aUserData); - if (!osxni->mPendingNotifiction) { - return NS_ERROR_FAILURE; - } - - NSImage *cocoaImage = nil; - nsCocoaUtils::CreateNSImageFromImageContainer(image, imgIContainer::FRAME_FIRST, &cocoaImage, 1.0f); - (osxni->mPendingNotifiction).contentImage = cocoaImage; - [cocoaImage release]; - ShowPendingNotification(osxni); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -} // namespace mozilla diff --git a/widget/cocoa/RectTextureImage.h b/widget/cocoa/RectTextureImage.h deleted file mode 100644 index 022b216c6b..0000000000 --- a/widget/cocoa/RectTextureImage.h +++ /dev/null @@ -1,80 +0,0 @@ -/* -*- 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 RectTextureImage_h_ -#define RectTextureImage_h_ - -#include "mozilla/RefPtr.h" - -class MacIOSurface; - -namespace mozilla { - -namespace gl { -class GLContext; -} // namespace gl - -namespace widget { - -// Manages a texture which can resize dynamically, binds to the -// LOCAL_GL_TEXTURE_RECTANGLE_ARB texture target and is automatically backed -// by a power-of-two size GL texture. The latter two features are used for -// compatibility with older Mac hardware which we block GL layers on. -// RectTextureImages are used both for accelerated GL layers drawing and for -// OMTC BasicLayers drawing. -class RectTextureImage { -public: - RectTextureImage(); - - virtual ~RectTextureImage(); - - already_AddRefed<gfx::DrawTarget> - BeginUpdate(const LayoutDeviceIntSize& aNewSize, - const LayoutDeviceIntRegion& aDirtyRegion = - LayoutDeviceIntRegion()); - void EndUpdate(); - - void UpdateIfNeeded(const LayoutDeviceIntSize& aNewSize, - const LayoutDeviceIntRegion& aDirtyRegion, - void (^aCallback)(gfx::DrawTarget*, - const LayoutDeviceIntRegion&)) - { - RefPtr<gfx::DrawTarget> drawTarget = BeginUpdate(aNewSize, aDirtyRegion); - if (drawTarget) { - aCallback(drawTarget, GetUpdateRegion()); - EndUpdate(); - } - } - - void UpdateFromCGContext(const LayoutDeviceIntSize& aNewSize, - const LayoutDeviceIntRegion& aDirtyRegion, - CGContextRef aCGContext); - - LayoutDeviceIntRegion GetUpdateRegion() { - MOZ_ASSERT(mInUpdate, "update region only valid during update"); - return mUpdateRegion; - } - - void Draw(mozilla::layers::GLManager* aManager, - const LayoutDeviceIntPoint& aLocation, - const gfx::Matrix4x4& aTransform = gfx::Matrix4x4()); - - -protected: - void DeleteTexture(); - void BindIOSurfaceToTexture(gl::GLContext* aGL); - - RefPtr<MacIOSurface> mIOSurface; - gl::GLContext* mGLContext; - LayoutDeviceIntRegion mUpdateRegion; - LayoutDeviceIntSize mBufferSize; - GLuint mTexture; - bool mInUpdate; -}; - -} // namespace widget -} // namespace mozilla - -#endif // RectTextureImage_h_ diff --git a/widget/cocoa/RectTextureImage.mm b/widget/cocoa/RectTextureImage.mm deleted file mode 100644 index c67af97d0a..0000000000 --- a/widget/cocoa/RectTextureImage.mm +++ /dev/null @@ -1,171 +0,0 @@ -/* -*- Mode: objc; 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/. */ - -#include "RectTextureImage.h" - -#include "gfxUtils.h" -#include "GLContextCGL.h" -#include "mozilla/layers/GLManager.h" -#include "mozilla/gfx/MacIOSurface.h" -#include "OGLShaderProgram.h" -#include "ScopedGLHelpers.h" - -namespace mozilla { -namespace widget { - -RectTextureImage::RectTextureImage() - : mGLContext(nullptr) - , mTexture(0) - , mInUpdate(false) -{ -} - -RectTextureImage::~RectTextureImage() -{ - DeleteTexture(); -} - -already_AddRefed<gfx::DrawTarget> -RectTextureImage::BeginUpdate(const LayoutDeviceIntSize& aNewSize, - const LayoutDeviceIntRegion& aDirtyRegion) -{ - MOZ_ASSERT(!mInUpdate, "Beginning update during update!"); - mUpdateRegion = aDirtyRegion; - bool needRecreate = false; - if (aNewSize != mBufferSize) { - mBufferSize = aNewSize; - mUpdateRegion = - LayoutDeviceIntRect(LayoutDeviceIntPoint(0, 0), aNewSize); - needRecreate = true; - } - - if (mUpdateRegion.IsEmpty()) { - return nullptr; - } - - if (!mIOSurface || needRecreate) { - DeleteTexture(); - mIOSurface = MacIOSurface::CreateIOSurface(mBufferSize.width, - mBufferSize.height); - - if (!mIOSurface) { - return nullptr; - } - } - - mInUpdate = true; - - mIOSurface->Lock(false); - unsigned char* ioData = (unsigned char*)mIOSurface->GetBaseAddress(); - gfx::IntSize size(mBufferSize.width, mBufferSize.height); - int32_t stride = mIOSurface->GetBytesPerRow(); - gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8; - RefPtr<gfx::DrawTarget> drawTarget = - gfx::Factory::CreateDrawTargetForData(gfx::BackendType::SKIA, - ioData, size, - stride, format); - return drawTarget.forget(); -} - -void -RectTextureImage::EndUpdate() -{ - MOZ_ASSERT(mInUpdate, "Ending update while not in update"); - mIOSurface->Unlock(false); - mInUpdate = false; -} - -void -RectTextureImage::UpdateFromCGContext(const LayoutDeviceIntSize& aNewSize, - const LayoutDeviceIntRegion& aDirtyRegion, - CGContextRef aCGContext) -{ - gfx::IntSize size = gfx::IntSize(CGBitmapContextGetWidth(aCGContext), - CGBitmapContextGetHeight(aCGContext)); - RefPtr<gfx::DrawTarget> dt = BeginUpdate(aNewSize, aDirtyRegion); - if (dt) { - gfx::Rect rect(0, 0, size.width, size.height); - gfxUtils::ClipToRegion(dt, GetUpdateRegion().ToUnknownRegion()); - RefPtr<gfx::SourceSurface> sourceSurface = - dt->CreateSourceSurfaceFromData(static_cast<uint8_t *>(CGBitmapContextGetData(aCGContext)), - size, - CGBitmapContextGetBytesPerRow(aCGContext), - gfx::SurfaceFormat::B8G8R8A8); - dt->DrawSurface(sourceSurface, rect, rect, - gfx::DrawSurfaceOptions(), - gfx::DrawOptions(1.0, gfx::CompositionOp::OP_SOURCE)); - dt->PopClip(); - EndUpdate(); - } -} - -void -RectTextureImage::Draw(layers::GLManager* aManager, - const LayoutDeviceIntPoint& aLocation, - const gfx::Matrix4x4& aTransform) -{ - gl::GLContext* gl = aManager->gl(); - - BindIOSurfaceToTexture(gl); - - layers::ShaderProgramOGL* program = - aManager->GetProgram(LOCAL_GL_TEXTURE_RECTANGLE_ARB, - gfx::SurfaceFormat::R8G8B8A8); - - gl->fActiveTexture(LOCAL_GL_TEXTURE0); - gl::ScopedBindTexture texture(gl, mTexture, LOCAL_GL_TEXTURE_RECTANGLE_ARB); - - aManager->ActivateProgram(program); - program->SetProjectionMatrix(aManager->GetProjMatrix()); - program->SetLayerTransform(gfx::Matrix4x4(aTransform).PostTranslate(aLocation.x, aLocation.y, 0)); - program->SetTextureTransform(gfx::Matrix4x4()); - program->SetRenderOffset(nsIntPoint(0, 0)); - program->SetTexCoordMultiplier(mBufferSize.width, mBufferSize.height); - program->SetTextureUnit(0); - - aManager->BindAndDrawQuad(program, - gfx::Rect(0.0, 0.0, mBufferSize.width, mBufferSize.height), - gfx::Rect(0.0, 0.0, 1.0f, 1.0f)); -} - -void -RectTextureImage::DeleteTexture() -{ - if (mTexture) { - MOZ_ASSERT(mGLContext); - mGLContext->MakeCurrent(); - mGLContext->fDeleteTextures(1, &mTexture); - mTexture = 0; - } -} - -void -RectTextureImage::BindIOSurfaceToTexture(gl::GLContext* aGL) -{ - if (!mTexture) { - MOZ_ASSERT(aGL); - aGL->fGenTextures(1, &mTexture); - aGL->fActiveTexture(LOCAL_GL_TEXTURE0); - gl::ScopedBindTexture texture(aGL, mTexture, LOCAL_GL_TEXTURE_RECTANGLE_ARB); - aGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, - LOCAL_GL_TEXTURE_MIN_FILTER, - LOCAL_GL_LINEAR); - aGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, - LOCAL_GL_TEXTURE_MAG_FILTER, - LOCAL_GL_LINEAR); - aGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, - LOCAL_GL_TEXTURE_WRAP_T, - LOCAL_GL_CLAMP_TO_EDGE); - aGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, - LOCAL_GL_TEXTURE_WRAP_S, - LOCAL_GL_CLAMP_TO_EDGE); - - mIOSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(aGL)->GetCGLContext()); - mGLContext = aGL; - } -} - -} // namespace widget -} // namespace mozilla diff --git a/widget/cocoa/SwipeTracker.h b/widget/cocoa/SwipeTracker.h deleted file mode 100644 index 78940d15cc..0000000000 --- a/widget/cocoa/SwipeTracker.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; 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 SwipeTracker_h -#define SwipeTracker_h - -#include "EventForwards.h" -#include "mozilla/layers/AxisPhysicsMSDModel.h" -#include "mozilla/RefPtr.h" -#include "mozilla/TimeStamp.h" -#include "nsRefreshDriver.h" -#include "Units.h" - -class nsIPresShell; - -namespace mozilla { - -class PanGestureInput; - -/** - * SwipeTracker turns PanGestureInput events into swipe events - * (WidgetSimpleGestureEvent) and dispatches them into Gecko. - * The swiping behavior mirrors the behavior of the Cocoa API - * -[NSEvent trackSwipeEventWithOptions:dampenAmountThresholdMin:max:usingHandler:]. - * The advantage of using this class over the Cocoa API is that this class - * properly supports submitting queued up events to it, and that it hopefully - * doesn't intermittently break scrolling the way the Cocoa API does (bug 927702). - * - * The swipe direction is either left or right. It is determined before the - * SwipeTracker is created and stays fixed during the swipe. - * During the swipe, the swipe has a current "value" which is between 0 and the - * target value. The target value is either 1 (swiping left) or -1 (swiping - * right) - see SwipeSuccessTargetValue(). - * A swipe can either succeed or fail. If it succeeds, the swipe animation - * animates towards the success target value; if it fails, it animates back to - * a value of 0. A swipe can only succeed if the user is swiping in an allowed - * direction. (Since both the allowed directions and the swipe direction are - * known at swipe start time, it's clear from the beginning whether a swipe is - * doomed to fail. In that case, the purpose of the SwipeTracker is to simulate - * a bounce-back animation.) - */ -class SwipeTracker final : public nsARefreshObserver { -public: - NS_INLINE_DECL_REFCOUNTING(SwipeTracker, override) - - SwipeTracker(nsChildView& aWidget, - const PanGestureInput& aSwipeStartEvent, - uint32_t aAllowedDirections, - uint32_t aSwipeDirection); - - void Destroy(); - - nsEventStatus ProcessEvent(const PanGestureInput& aEvent); - void CancelSwipe(); - - static WidgetSimpleGestureEvent - CreateSwipeGestureEvent(EventMessage aMsg, nsIWidget* aWidget, - const LayoutDeviceIntPoint& aPosition); - - - // nsARefreshObserver - void WillRefresh(mozilla::TimeStamp aTime) override; - -protected: - ~SwipeTracker(); - - bool SwipingInAllowedDirection() const { return mAllowedDirections & mSwipeDirection; } - double SwipeSuccessTargetValue() const; - double ClampToAllowedRange(double aGestureAmount) const; - bool ComputeSwipeSuccess() const; - void StartAnimating(double aTargetValue); - void SwipeFinished(); - void UnregisterFromRefreshDriver(); - bool SendSwipeEvent(EventMessage aMsg, uint32_t aDirection, double aDelta); - - nsChildView& mWidget; - RefPtr<nsRefreshDriver> mRefreshDriver; - layers::AxisPhysicsMSDModel mAxis; - const LayoutDeviceIntPoint mEventPosition; - TimeStamp mLastEventTimeStamp; - TimeStamp mLastAnimationFrameTime; - const uint32_t mAllowedDirections; - const uint32_t mSwipeDirection; - double mGestureAmount; - double mCurrentVelocity; - bool mEventsAreControllingSwipe; - bool mEventsHaveStartedNewGesture; - bool mRegisteredWithRefreshDriver; -}; - -} // namespace mozilla - -#endif // SwipeTracker_h diff --git a/widget/cocoa/SwipeTracker.mm b/widget/cocoa/SwipeTracker.mm deleted file mode 100644 index 51169171a4..0000000000 --- a/widget/cocoa/SwipeTracker.mm +++ /dev/null @@ -1,219 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; 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/. */ - -#include "SwipeTracker.h" - -#include "InputData.h" -#include "mozilla/TimeStamp.h" -#include "mozilla/TouchEvents.h" -#include "nsAlgorithm.h" -#include "nsChildView.h" -#include "UnitTransforms.h" - -// These values were tweaked to make the physics feel similar to the native swipe. -static const double kSpringForce = 250.0; -static const double kVelocityTwitchTolerance = 0.0000001; -static const double kWholePagePixelSize = 1000.0; -static const double kRubberBandResistanceFactor = 4.0; -static const double kSwipeSuccessThreshold = 0.25; -static const double kSwipeSuccessVelocityContribution = 0.3; - -namespace mozilla { - -static already_AddRefed<nsRefreshDriver> -GetRefreshDriver(nsIWidget& aWidget) -{ - nsIWidgetListener* widgetListener = aWidget.GetWidgetListener(); - nsIPresShell* presShell = widgetListener ? widgetListener->GetPresShell() : nullptr; - nsPresContext* presContext = presShell ? presShell->GetPresContext() : nullptr; - RefPtr<nsRefreshDriver> refreshDriver = presContext ? presContext->RefreshDriver() : nullptr; - return refreshDriver.forget(); -} - -SwipeTracker::SwipeTracker(nsChildView& aWidget, - const PanGestureInput& aSwipeStartEvent, - uint32_t aAllowedDirections, - uint32_t aSwipeDirection) - : mWidget(aWidget) - , mRefreshDriver(GetRefreshDriver(mWidget)) - , mAxis(0.0, 0.0, 0.0, kSpringForce, 1.0) - , mEventPosition(RoundedToInt(ViewAs<LayoutDevicePixel>(aSwipeStartEvent.mPanStartPoint, - PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent))) - , mLastEventTimeStamp(aSwipeStartEvent.mTimeStamp) - , mAllowedDirections(aAllowedDirections) - , mSwipeDirection(aSwipeDirection) - , mGestureAmount(0.0) - , mCurrentVelocity(0.0) - , mEventsAreControllingSwipe(true) - , mEventsHaveStartedNewGesture(false) - , mRegisteredWithRefreshDriver(false) -{ - SendSwipeEvent(eSwipeGestureStart, 0, 0.0); - ProcessEvent(aSwipeStartEvent); -} - -void -SwipeTracker::Destroy() -{ - UnregisterFromRefreshDriver(); -} - -SwipeTracker::~SwipeTracker() -{ - MOZ_ASSERT(!mRegisteredWithRefreshDriver, "Destroy needs to be called before deallocating"); -} - -double -SwipeTracker::SwipeSuccessTargetValue() const -{ - return (mSwipeDirection == nsIDOMSimpleGestureEvent::DIRECTION_RIGHT) ? -1.0 : 1.0; -} - -double -SwipeTracker::ClampToAllowedRange(double aGestureAmount) const -{ - // gestureAmount needs to stay between -1 and 0 when swiping right and - // between 0 and 1 when swiping left. - double min = (mSwipeDirection == nsIDOMSimpleGestureEvent::DIRECTION_RIGHT) ? -1.0 : 0.0; - double max = (mSwipeDirection == nsIDOMSimpleGestureEvent::DIRECTION_LEFT) ? 1.0 : 0.0; - return clamped(aGestureAmount, min, max); -} - -bool -SwipeTracker::ComputeSwipeSuccess() const -{ - double targetValue = SwipeSuccessTargetValue(); - - // If the fingers were moving away from the target direction when they were - // lifted from the touchpad, abort the swipe. - if (mCurrentVelocity * targetValue < -kVelocityTwitchTolerance) { - return false; - } - - return (mGestureAmount * targetValue + - mCurrentVelocity * targetValue * kSwipeSuccessVelocityContribution) >= kSwipeSuccessThreshold; -} - -nsEventStatus -SwipeTracker::ProcessEvent(const PanGestureInput& aEvent) -{ - // If the fingers have already been lifted, don't process this event for swiping. - if (!mEventsAreControllingSwipe) { - // Return nsEventStatus_eConsumeNoDefault for events from the swipe gesture - // and nsEventStatus_eIgnore for events of subsequent scroll gestures. - if (aEvent.mType == PanGestureInput::PANGESTURE_MAYSTART || - aEvent.mType == PanGestureInput::PANGESTURE_START) { - mEventsHaveStartedNewGesture = true; - } - return mEventsHaveStartedNewGesture ? nsEventStatus_eIgnore : nsEventStatus_eConsumeNoDefault; - } - - double delta = -aEvent.mPanDisplacement.x / mWidget.BackingScaleFactor() / kWholePagePixelSize; - if (!SwipingInAllowedDirection()) { - delta /= kRubberBandResistanceFactor; - } - mGestureAmount = ClampToAllowedRange(mGestureAmount + delta); - SendSwipeEvent(eSwipeGestureUpdate, 0, mGestureAmount); - - if (aEvent.mType != PanGestureInput::PANGESTURE_END) { - double elapsedSeconds = std::max(0.008, (aEvent.mTimeStamp - mLastEventTimeStamp).ToSeconds()); - mCurrentVelocity = delta / elapsedSeconds; - mLastEventTimeStamp = aEvent.mTimeStamp; - } else { - mEventsAreControllingSwipe = false; - bool didSwipeSucceed = SwipingInAllowedDirection() && ComputeSwipeSuccess(); - double targetValue = 0.0; - if (didSwipeSucceed) { - SendSwipeEvent(eSwipeGesture, mSwipeDirection, 0.0); - targetValue = SwipeSuccessTargetValue(); - } - StartAnimating(targetValue); - } - - return nsEventStatus_eConsumeNoDefault; -} - -void -SwipeTracker::StartAnimating(double aTargetValue) -{ - mAxis.SetPosition(mGestureAmount); - mAxis.SetDestination(aTargetValue); - mAxis.SetVelocity(mCurrentVelocity); - - mLastAnimationFrameTime = TimeStamp::Now(); - - // Add ourselves as a refresh driver observer. The refresh driver - // will call WillRefresh for each animation frame until we - // unregister ourselves. - MOZ_ASSERT(!mRegisteredWithRefreshDriver); - if (mRefreshDriver) { - mRefreshDriver->AddRefreshObserver(this, Flush_Style); - mRegisteredWithRefreshDriver = true; - } -} - -void -SwipeTracker::WillRefresh(mozilla::TimeStamp aTime) -{ - TimeStamp now = TimeStamp::Now(); - mAxis.Simulate(now - mLastAnimationFrameTime); - mLastAnimationFrameTime = now; - - bool isFinished = mAxis.IsFinished(1.0 / kWholePagePixelSize); - mGestureAmount = (isFinished ? mAxis.GetDestination() : mAxis.GetPosition()); - SendSwipeEvent(eSwipeGestureUpdate, 0, mGestureAmount); - - if (isFinished) { - UnregisterFromRefreshDriver(); - SwipeFinished(); - } -} - -void -SwipeTracker::CancelSwipe() -{ - SendSwipeEvent(eSwipeGestureEnd, 0, 0.0); -} - -void SwipeTracker::SwipeFinished() -{ - SendSwipeEvent(eSwipeGestureEnd, 0, 0.0); - mWidget.SwipeFinished(); -} - -void -SwipeTracker::UnregisterFromRefreshDriver() -{ - if (mRegisteredWithRefreshDriver) { - MOZ_ASSERT(mRefreshDriver, "How were we able to register, then?"); - mRefreshDriver->RemoveRefreshObserver(this, Flush_Style); - } - mRegisteredWithRefreshDriver = false; -} - -/* static */ WidgetSimpleGestureEvent -SwipeTracker::CreateSwipeGestureEvent(EventMessage aMsg, nsIWidget* aWidget, - const LayoutDeviceIntPoint& aPosition) -{ - WidgetSimpleGestureEvent geckoEvent(true, aMsg, aWidget); - geckoEvent.mModifiers = 0; - geckoEvent.mTimeStamp = TimeStamp::Now(); - geckoEvent.mRefPoint = aPosition; - geckoEvent.buttons = 0; - return geckoEvent; -} - -bool -SwipeTracker::SendSwipeEvent(EventMessage aMsg, uint32_t aDirection, double aDelta) -{ - WidgetSimpleGestureEvent geckoEvent = - CreateSwipeGestureEvent(aMsg, &mWidget, mEventPosition); - geckoEvent.mDirection = aDirection; - geckoEvent.mDelta = aDelta; - geckoEvent.mAllowedDirections = mAllowedDirections; - return mWidget.DispatchWindowEvent(geckoEvent); -} - -} // namespace mozilla diff --git a/widget/cocoa/TextInputHandler.h b/widget/cocoa/TextInputHandler.h deleted file mode 100644 index 86da354abb..0000000000 --- a/widget/cocoa/TextInputHandler.h +++ /dev/null @@ -1,1194 +0,0 @@ -/* -*- 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 TextInputHandler_h_ -#define TextInputHandler_h_ - -#include "nsCocoaUtils.h" - -#import <Carbon/Carbon.h> -#import <Cocoa/Cocoa.h> -#include "mozView.h" -#include "nsString.h" -#include "nsCOMPtr.h" -#include "nsITimer.h" -#include "nsTArray.h" -#include "mozilla/EventForwards.h" -#include "mozilla/TextEventDispatcherListener.h" -#include "WritingModes.h" - -class nsChildView; - -namespace mozilla { -namespace widget { - -// Key code constants -enum -{ -#if !defined(MAC_OS_X_VERSION_10_12) || \ - MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 - kVK_RightCommand = 0x36, // right command key -#endif - - kVK_PC_PrintScreen = kVK_F13, - kVK_PC_ScrollLock = kVK_F14, - kVK_PC_Pause = kVK_F15, - - kVK_PC_Insert = kVK_Help, - kVK_PC_Backspace = kVK_Delete, - kVK_PC_Delete = kVK_ForwardDelete, - - kVK_PC_ContextMenu = 0x6E, - - kVK_Powerbook_KeypadEnter = 0x34 // Enter on Powerbook's keyboard is different -}; - -/** - * TISInputSourceWrapper is a wrapper for the TISInputSourceRef. If we get the - * TISInputSourceRef from InputSourceID, we need to release the CFArray instance - * which is returned by TISCreateInputSourceList. However, when we release the - * list, we cannot access the TISInputSourceRef. So, it's not usable, and it - * may cause the memory leak bugs. nsTISInputSource automatically releases the - * list when the instance is destroyed. - */ -class TISInputSourceWrapper -{ -public: - static TISInputSourceWrapper& CurrentInputSource(); - /** - * Shutdown() should be called when nobody doesn't need to use this class. - */ - static void Shutdown(); - - TISInputSourceWrapper() - { - mInputSourceList = nullptr; - Clear(); - } - - explicit TISInputSourceWrapper(const char* aID) - { - mInputSourceList = nullptr; - InitByInputSourceID(aID); - } - - explicit TISInputSourceWrapper(SInt32 aLayoutID) - { - mInputSourceList = nullptr; - InitByLayoutID(aLayoutID); - } - - explicit TISInputSourceWrapper(TISInputSourceRef aInputSource) - { - mInputSourceList = nullptr; - InitByTISInputSourceRef(aInputSource); - } - - ~TISInputSourceWrapper() { Clear(); } - - void InitByInputSourceID(const char* aID); - void InitByInputSourceID(const nsAFlatString &aID); - void InitByInputSourceID(const CFStringRef aID); - /** - * InitByLayoutID() initializes the keyboard layout by the layout ID. - * - * @param aLayoutID An ID of keyboard layout. - * 0: US - * 1: Greek - * 2: German - * 3: Swedish-Pro - * 4: Dvorak-Qwerty Cmd - * 5: Thai - * 6: Arabic - * 7: French - * 8: Hebrew - * 9: Lithuanian - * 10: Norwegian - * 11: Spanish - * @param aOverrideKeyboard When testing set to TRUE, otherwise, set to - * FALSE. When TRUE, we use an ANSI keyboard - * instead of the actual keyboard. - */ - void InitByLayoutID(SInt32 aLayoutID, bool aOverrideKeyboard = false); - void InitByCurrentInputSource(); - void InitByCurrentKeyboardLayout(); - void InitByCurrentASCIICapableInputSource(); - void InitByCurrentASCIICapableKeyboardLayout(); - void InitByCurrentInputMethodKeyboardLayoutOverride(); - void InitByTISInputSourceRef(TISInputSourceRef aInputSource); - void InitByLanguage(CFStringRef aLanguage); - - /** - * If the instance is initialized with a keyboard layout input source, - * returns it. - * If the instance is initialized with an IME mode input source, the result - * references the keyboard layout for the IME mode. However, this can be - * initialized only when the IME mode is actually selected. I.e, if IME mode - * input source is initialized with LayoutID or SourceID, this returns null. - */ - TISInputSourceRef GetKeyboardLayoutInputSource() const - { - return mKeyboardLayout; - } - const UCKeyboardLayout* GetUCKeyboardLayout(); - - bool IsOpenedIMEMode(); - bool IsIMEMode(); - bool IsKeyboardLayout(); - - bool IsASCIICapable() - { - NS_ENSURE_TRUE(mInputSource, false); - return GetBoolProperty(kTISPropertyInputSourceIsASCIICapable); - } - - bool IsEnabled() - { - NS_ENSURE_TRUE(mInputSource, false); - return GetBoolProperty(kTISPropertyInputSourceIsEnabled); - } - - bool GetLanguageList(CFArrayRef &aLanguageList); - bool GetPrimaryLanguage(CFStringRef &aPrimaryLanguage); - bool GetPrimaryLanguage(nsAString &aPrimaryLanguage); - - bool GetLocalizedName(CFStringRef &aName) - { - NS_ENSURE_TRUE(mInputSource, false); - return GetStringProperty(kTISPropertyLocalizedName, aName); - } - - bool GetLocalizedName(nsAString &aName) - { - NS_ENSURE_TRUE(mInputSource, false); - return GetStringProperty(kTISPropertyLocalizedName, aName); - } - - bool GetInputSourceID(CFStringRef &aID) - { - NS_ENSURE_TRUE(mInputSource, false); - return GetStringProperty(kTISPropertyInputSourceID, aID); - } - - bool GetInputSourceID(nsAString &aID) - { - NS_ENSURE_TRUE(mInputSource, false); - return GetStringProperty(kTISPropertyInputSourceID, aID); - } - - bool GetBundleID(CFStringRef &aBundleID) - { - NS_ENSURE_TRUE(mInputSource, false); - return GetStringProperty(kTISPropertyBundleID, aBundleID); - } - - bool GetBundleID(nsAString &aBundleID) - { - NS_ENSURE_TRUE(mInputSource, false); - return GetStringProperty(kTISPropertyBundleID, aBundleID); - } - - bool GetInputSourceType(CFStringRef &aType) - { - NS_ENSURE_TRUE(mInputSource, false); - return GetStringProperty(kTISPropertyInputSourceType, aType); - } - - bool GetInputSourceType(nsAString &aType) - { - NS_ENSURE_TRUE(mInputSource, false); - return GetStringProperty(kTISPropertyInputSourceType, aType); - } - - bool IsForRTLLanguage(); - bool IsInitializedByCurrentInputSource(); - - enum { - // 40 is an actual result of the ::LMGetKbdType() when we connect an - // unknown keyboard and set the keyboard type to ANSI manually on the - // set up dialog. - eKbdType_ANSI = 40 - }; - - void Select(); - void Clear(); - - /** - * InitKeyEvent() initializes aKeyEvent for aNativeKeyEvent. - * - * @param aNativeKeyEvent A native key event for which you want to - * dispatch a Gecko key event. - * @param aKeyEvent The result -- a Gecko key event initialized - * from the native key event. - * @param aInsertString If caller expects that the event will cause - * a character to be input (say in an editor), - * the caller should set this. Otherwise, - * if caller sets null to this, this method will - * compute the character to be input from - * characters of aNativeKeyEvent. - */ - void InitKeyEvent(NSEvent *aNativeKeyEvent, WidgetKeyboardEvent& aKeyEvent, - const nsAString *aInsertString = nullptr); - - /** - * WillDispatchKeyboardEvent() computes aKeyEvent.mAlternativeCharCodes and - * recompute aKeyEvent.mCharCode if it's necessary. - * - * @param aNativeKeyEvent A native key event for which you want to - * dispatch a Gecko key event. - * @param aInsertString If caller expects that the event will cause - * a character to be input (say in an editor), - * the caller should set this. Otherwise, - * if caller sets null to this, this method will - * compute the character to be input from - * characters of aNativeKeyEvent. - * @param aKeyEvent The result -- a Gecko key event initialized - * from the native key event. This must be - * eKeyPress event. - */ - void WillDispatchKeyboardEvent(NSEvent* aNativeKeyEvent, - const nsAString* aInsertString, - WidgetKeyboardEvent& aKeyEvent); - - /** - * ComputeGeckoKeyCode() returns Gecko keycode for aNativeKeyCode on current - * keyboard layout. - * - * @param aNativeKeyCode A native keycode. - * @param aKbType A native Keyboard Type value. Typically, - * this is a result of ::LMGetKbdType(). - * @param aCmdIsPressed TRUE if Cmd key is pressed. Otherwise, FALSE. - * @return The computed Gecko keycode. - */ - uint32_t ComputeGeckoKeyCode(UInt32 aNativeKeyCode, UInt32 aKbType, - bool aCmdIsPressed); - - /** - * ComputeGeckoKeyNameIndex() returns Gecko key name index for the key. - * - * @param aNativeKeyCode A native keycode. - */ - static KeyNameIndex ComputeGeckoKeyNameIndex(UInt32 aNativeKeyCode); - - /** - * ComputeGeckoCodeNameIndex() returns Gecko code name index for the key. - * - * @param aNativeKeyCode A native keycode. - */ - static CodeNameIndex ComputeGeckoCodeNameIndex(UInt32 aNativeKeyCode); - -protected: - /** - * TranslateToString() computes the inputted text from the native keyCode, - * modifier flags and keyboard type. - * - * @param aKeyCode A native keyCode. - * @param aModifiers Combination of native modifier flags. - * @param aKbType A native Keyboard Type value. Typically, - * this is a result of ::LMGetKbdType(). - * @param aStr Result, i.e., inputted text. - * The result can be two or more characters. - * @return If succeeded, TRUE. Otherwise, FALSE. - * Even if TRUE, aStr can be empty string. - */ - bool TranslateToString(UInt32 aKeyCode, UInt32 aModifiers, - UInt32 aKbType, nsAString &aStr); - - /** - * TranslateToChar() computes the inputted character from the native keyCode, - * modifier flags and keyboard type. If two or more characters would be - * input, this returns 0. - * - * @param aKeyCode A native keyCode. - * @param aModifiers Combination of native modifier flags. - * @param aKbType A native Keyboard Type value. Typically, - * this is a result of ::LMGetKbdType(). - * @return If succeeded and the result is one character, - * returns the charCode of it. Otherwise, - * returns 0. - */ - uint32_t TranslateToChar(UInt32 aKeyCode, UInt32 aModifiers, UInt32 aKbType); - - /** - * ComputeInsertString() computes string to be inserted with the key event. - * - * @param aNativeKeyEvent The native key event which causes our keyboard - * event(s). - * @param aKeyEvent A Gecko key event which was partially - * initialized with aNativeKeyEvent. - * @param aInsertString The string to be inputting by aNativeKeyEvent. - * This should be specified by InsertText(). - * In other words, if the key event doesn't cause - * a call of InsertText(), this can be nullptr. - * @param aResult The string which should be set to charCode of - * keypress event(s). - */ - void ComputeInsertStringForCharCode(NSEvent* aNativeKeyEvent, - const WidgetKeyboardEvent& aKeyEvent, - const nsAString* aInsertString, - nsAString& aResult); - - /** - * IsPrintableKeyEvent() returns true if aNativeKeyEvent is caused by - * a printable key. Otherwise, returns false. - */ - bool IsPrintableKeyEvent(NSEvent* aNativeKeyEvent) const; - - /** - * GetKbdType() returns physical keyboard type. - */ - UInt32 GetKbdType() const; - - bool GetBoolProperty(const CFStringRef aKey); - bool GetStringProperty(const CFStringRef aKey, CFStringRef &aStr); - bool GetStringProperty(const CFStringRef aKey, nsAString &aStr); - - TISInputSourceRef mInputSource; - TISInputSourceRef mKeyboardLayout; - CFArrayRef mInputSourceList; - const UCKeyboardLayout* mUCKeyboardLayout; - int8_t mIsRTL; - - bool mOverrideKeyboard; - - static TISInputSourceWrapper* sCurrentInputSource; -}; - -/** - * TextInputHandlerBase is a base class of IMEInputHandler and TextInputHandler. - * Utility methods should be implemented this level. - */ - -class TextInputHandlerBase : public TextEventDispatcherListener -{ -public: - /** - * Other TextEventDispatcherListener methods should be implemented in - * IMEInputHandler. - */ - NS_DECL_ISUPPORTS - - /** - * DispatchEvent() dispatches aEvent on mWidget. - * - * @param aEvent An event which you want to dispatch. - * @return TRUE if the event is consumed by web contents - * or chrome contents. Otherwise, FALSE. - */ - bool DispatchEvent(WidgetGUIEvent& aEvent); - - /** - * SetSelection() dispatches eSetSelection event for the aRange. - * - * @param aRange The range which will be selected. - * @return TRUE if setting selection is succeeded and - * the widget hasn't been destroyed. - * Otherwise, FALSE. - */ - bool SetSelection(NSRange& aRange); - - /** - * InitKeyEvent() initializes aKeyEvent for aNativeKeyEvent. - * - * @param aNativeKeyEvent A native key event for which you want to - * dispatch a Gecko key event. - * @param aKeyEvent The result -- a Gecko key event initialized - * from the native key event. - * @param aInsertString If caller expects that the event will cause - * a character to be input (say in an editor), - * the caller should set this. Otherwise, - * if caller sets null to this, this method will - * compute the character to be input from - * characters of aNativeKeyEvent. - */ - void InitKeyEvent(NSEvent *aNativeKeyEvent, WidgetKeyboardEvent& aKeyEvent, - const nsAString *aInsertString = nullptr); - - /** - * SynthesizeNativeKeyEvent() is an implementation of - * nsIWidget::SynthesizeNativeKeyEvent(). See the document in nsIWidget.h - * for the detail. - */ - nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout, - int32_t aNativeKeyCode, - uint32_t aModifierFlags, - const nsAString& aCharacters, - const nsAString& aUnmodifiedCharacters); - - /** - * Utility method intended for testing. Attempts to construct a native key - * event that would have been generated during an actual key press. This - * *does not dispatch* the native event. Instead, it is attached to the - * |mNativeKeyEvent| field of the Gecko event that is passed in. - * @param aKeyEvent Gecko key event to attach the native event to - */ - NS_IMETHOD AttachNativeKeyEvent(WidgetKeyboardEvent& aKeyEvent); - - /** - * GetWindowLevel() returns the window level of current focused (in Gecko) - * window. E.g., if an <input> element in XUL panel has focus, this returns - * the XUL panel's window level. - */ - NSInteger GetWindowLevel(); - - /** - * IsSpecialGeckoKey() checks whether aNativeKeyCode is mapped to a special - * Gecko keyCode. A key is "special" if it isn't used for text input. - * - * @param aNativeKeyCode A native keycode. - * @return If the keycode is mapped to a special key, - * TRUE. Otherwise, FALSE. - */ - static bool IsSpecialGeckoKey(UInt32 aNativeKeyCode); - - - /** - * EnableSecureEventInput() and DisableSecureEventInput() wrap the Carbon - * Event Manager APIs with the same names. In addition they keep track of - * how many times we've called them (in the same process) -- unlike the - * Carbon Event Manager APIs, which only keep track of how many times they've - * been called from any and all processes. - * - * The Carbon Event Manager's IsSecureEventInputEnabled() returns whether - * secure event input mode is enabled (in any process). This class's - * IsSecureEventInputEnabled() returns whether we've made any calls to - * EnableSecureEventInput() that are not (yet) offset by the calls we've - * made to DisableSecureEventInput(). - */ - static void EnableSecureEventInput(); - static void DisableSecureEventInput(); - static bool IsSecureEventInputEnabled(); - - /** - * EnsureSecureEventInputDisabled() calls DisableSecureEventInput() until - * our call count becomes 0. - */ - static void EnsureSecureEventInputDisabled(); - -public: - /** - * mWidget must not be destroyed without OnDestroyWidget being called. - * - * @param aDestroyingWidget Destroying widget. This might not be mWidget. - * @return This result doesn't have any meaning for - * callers. When aDstroyingWidget isn't the same - * as mWidget, FALSE. Then, inherited methods in - * sub classes should return from this method - * without cleaning up. - */ - virtual bool OnDestroyWidget(nsChildView* aDestroyingWidget); - -protected: - // The creator of this instance, client and its text event dispatcher. - // These members must not be nullptr after initialized until - // OnDestroyWidget() is called. - nsChildView* mWidget; // [WEAK] - RefPtr<TextEventDispatcher> mDispatcher; - - // The native view for mWidget. - // This view handles the actual text inputting. - NSView<mozView>* mView; // [STRONG] - - TextInputHandlerBase(nsChildView* aWidget, NSView<mozView> *aNativeView); - virtual ~TextInputHandlerBase(); - - bool Destroyed() { return !mWidget; } - - /** - * mCurrentKeyEvent indicates what key event we are handling. While - * handling a native keydown event, we need to store the event for insertText, - * doCommandBySelector and various action message handlers of NSResponder - * such as [NSResponder insertNewline:sender]. - */ - struct KeyEventState - { - // Handling native key event - NSEvent* mKeyEvent; - // String specified by InsertText(). This is not null only during a - // call of InsertText(). - nsAString* mInsertString; - // String which are included in [mKeyEvent characters] and already handled - // by InsertText() call(s). - nsString mInsertedString; - // Whether keydown event was consumed by web contents or chrome contents. - bool mKeyDownHandled; - // Whether keypress event was dispatched for mKeyEvent. - bool mKeyPressDispatched; - // Whether keypress event was consumed by web contents or chrome contents. - bool mKeyPressHandled; - // Whether the key event causes other key events via IME or something. - bool mCausedOtherKeyEvents; - // Whether the key event causes composition change or committing - // composition. So, even if InsertText() is called, this may be false - // if it dispatches keypress event. - bool mCompositionDispatched; - - KeyEventState() : mKeyEvent(nullptr) - { - Clear(); - } - - explicit KeyEventState(NSEvent* aNativeKeyEvent) : mKeyEvent(nullptr) - { - Clear(); - Set(aNativeKeyEvent); - } - - KeyEventState(const KeyEventState &aOther) = delete; - - ~KeyEventState() - { - Clear(); - } - - void Set(NSEvent* aNativeKeyEvent) - { - NS_PRECONDITION(aNativeKeyEvent, "aNativeKeyEvent must not be NULL"); - Clear(); - mKeyEvent = [aNativeKeyEvent retain]; - } - - void Clear() - { - if (mKeyEvent) { - [mKeyEvent release]; - mKeyEvent = nullptr; - } - mInsertString = nullptr; - mInsertedString.Truncate(); - mKeyDownHandled = false; - mKeyPressDispatched = false; - mKeyPressHandled = false; - mCausedOtherKeyEvents = false; - mCompositionDispatched = false; - } - - bool IsDefaultPrevented() const - { - return mKeyDownHandled || mKeyPressHandled || mCausedOtherKeyEvents || - mCompositionDispatched; - } - - bool CanDispatchKeyPressEvent() const - { - return !mKeyPressDispatched && !IsDefaultPrevented(); - } - - void InitKeyEvent(TextInputHandlerBase* aHandler, - WidgetKeyboardEvent& aKeyEvent); - - /** - * GetUnhandledString() returns characters of the event which have not been - * handled with InsertText() yet. For example, if there is a composition - * caused by a dead key press like '`' and it's committed by some key - * combinations like |Cmd+v|, then, the |v|'s KeyDown event's |characters| - * is |`v|. Then, after |`| is committed with a call of InsertString(), - * this returns only 'v'. - */ - void GetUnhandledString(nsAString& aUnhandledString) const; - }; - - /** - * Helper classes for guaranteeing cleaning mCurrentKeyEvent - */ - class AutoKeyEventStateCleaner - { - public: - explicit AutoKeyEventStateCleaner(TextInputHandlerBase* aHandler) : - mHandler(aHandler) - { - } - - ~AutoKeyEventStateCleaner() - { - mHandler->RemoveCurrentKeyEvent(); - } - private: - RefPtr<TextInputHandlerBase> mHandler; - }; - - class MOZ_STACK_CLASS AutoInsertStringClearer - { - public: - explicit AutoInsertStringClearer(KeyEventState* aState) - : mState(aState) - { - } - ~AutoInsertStringClearer(); - - private: - KeyEventState* mState; - }; - - /** - * mCurrentKeyEvents stores all key events which are being processed. - * When we call interpretKeyEvents, IME may generate other key events. - * mCurrentKeyEvents[0] is the latest key event. - */ - nsTArray<KeyEventState*> mCurrentKeyEvents; - - /** - * mFirstKeyEvent must be used for first key event. This member prevents - * memory fragmentation for most key events. - */ - KeyEventState mFirstKeyEvent; - - /** - * PushKeyEvent() adds the current key event to mCurrentKeyEvents. - */ - KeyEventState* PushKeyEvent(NSEvent* aNativeKeyEvent) - { - uint32_t nestCount = mCurrentKeyEvents.Length(); - for (uint32_t i = 0; i < nestCount; i++) { - // When the key event is caused by another key event, all key events - // which are being handled should be marked as "consumed". - mCurrentKeyEvents[i]->mCausedOtherKeyEvents = true; - } - - KeyEventState* keyEvent = nullptr; - if (nestCount == 0) { - mFirstKeyEvent.Set(aNativeKeyEvent); - keyEvent = &mFirstKeyEvent; - } else { - keyEvent = new KeyEventState(aNativeKeyEvent); - } - return *mCurrentKeyEvents.AppendElement(keyEvent); - } - - /** - * RemoveCurrentKeyEvent() removes the current key event from - * mCurrentKeyEvents. - */ - void RemoveCurrentKeyEvent() - { - NS_ASSERTION(mCurrentKeyEvents.Length() > 0, - "RemoveCurrentKeyEvent() is called unexpectedly"); - KeyEventState* keyEvent = GetCurrentKeyEvent(); - mCurrentKeyEvents.RemoveElementAt(mCurrentKeyEvents.Length() - 1); - if (keyEvent == &mFirstKeyEvent) { - keyEvent->Clear(); - } else { - delete keyEvent; - } - } - - /** - * GetCurrentKeyEvent() returns current processing key event. - */ - KeyEventState* GetCurrentKeyEvent() - { - if (mCurrentKeyEvents.Length() == 0) { - return nullptr; - } - return mCurrentKeyEvents[mCurrentKeyEvents.Length() - 1]; - } - - struct KeyboardLayoutOverride final - { - int32_t mKeyboardLayout; - bool mOverrideEnabled; - - KeyboardLayoutOverride() : - mKeyboardLayout(0), mOverrideEnabled(false) - { - } - }; - - const KeyboardLayoutOverride& KeyboardLayoutOverrideRef() const - { - return mKeyboardOverride; - } - - /** - * IsPrintableChar() checks whether the unicode character is - * a non-printable ASCII character or not. Note that this returns - * TRUE even if aChar is a non-printable UNICODE character. - * - * @param aChar A unicode character. - * @return TRUE if aChar is a printable ASCII character - * or a unicode character. Otherwise, i.e, - * if aChar is a non-printable ASCII character, - * FALSE. - */ - static bool IsPrintableChar(char16_t aChar); - - /** - * IsNormalCharInputtingEvent() checks whether aKeyEvent causes text input. - * - * @param aKeyEvent A key event. - * @return TRUE if the key event causes text input. - * Otherwise, FALSE. - */ - static bool IsNormalCharInputtingEvent(const WidgetKeyboardEvent& aKeyEvent); - - /** - * IsModifierKey() checks whether the native keyCode is for a modifier key. - * - * @param aNativeKeyCode A native keyCode. - * @return TRUE if aNativeKeyCode is for a modifier key. - * Otherwise, FALSE. - */ - static bool IsModifierKey(UInt32 aNativeKeyCode); - -private: - KeyboardLayoutOverride mKeyboardOverride; - - static int32_t sSecureEventInputCount; -}; - -/** - * IMEInputHandler manages: - * 1. The IME/keyboard layout statement of nsChildView. - * 2. The IME composition statement of nsChildView. - * And also provides the methods which controls the current IME transaction of - * the instance. - * - * Note that an nsChildView handles one or more NSView's events. E.g., even if - * a text editor on XUL panel element, the input events handled on the parent - * (or its ancestor) widget handles it (the native focus is set to it). The - * actual focused view is notified by OnFocusChangeInGecko. - */ - -class IMEInputHandler : public TextInputHandlerBase -{ -public: - // TextEventDispatcherListener methods - NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher, - const IMENotification& aNotification) override; - NS_IMETHOD_(void) OnRemovedFrom( - TextEventDispatcher* aTextEventDispatcher) override; - NS_IMETHOD_(void) WillDispatchKeyboardEvent( - TextEventDispatcher* aTextEventDispatcher, - WidgetKeyboardEvent& aKeyboardEvent, - uint32_t aIndexOfKeypress, - void* aData) override; - -public: - virtual bool OnDestroyWidget(nsChildView* aDestroyingWidget) override; - - virtual void OnFocusChangeInGecko(bool aFocus); - - void OnSelectionChange(const IMENotification& aIMENotification); - - /** - * Call [NSTextInputContext handleEvent] for mouse event support of IME - */ - bool OnHandleEvent(NSEvent* aEvent); - - /** - * SetMarkedText() is a handler of setMarkedText of NSTextInput. - * - * @param aAttrString This mut be an instance of NSAttributedString. - * If the aString parameter to - * [ChildView setMarkedText:setSelectedRange:] - * isn't an instance of NSAttributedString, - * create an NSAttributedString from it and pass - * that instead. - * @param aSelectedRange Current selected range (or caret position). - * @param aReplacementRange The range which will be replaced with the - * aAttrString instead of current marked range. - */ - void SetMarkedText(NSAttributedString* aAttrString, - NSRange& aSelectedRange, - NSRange* aReplacementRange = nullptr); - - /** - * GetAttributedSubstringFromRange() returns an NSAttributedString instance - * which is allocated as autorelease for aRange. - * - * @param aRange The range of string which you want. - * @param aActualRange The actual range of the result. - * @return The string in aRange. If the string is empty, - * this returns nil. If succeeded, this returns - * an instance which is allocated as autorelease. - * If this has some troubles, returns nil. - */ - NSAttributedString* GetAttributedSubstringFromRange( - NSRange& aRange, - NSRange* aActualRange = nullptr); - - /** - * SelectedRange() returns current selected range. - * - * @return If an editor has focus, this returns selection - * range in the editor. Otherwise, this returns - * selection range in the focused document. - */ - NSRange SelectedRange(); - - /** - * DrawsVerticallyForCharacterAtIndex() returns whether the character at - * the given index is being rendered vertically. - * - * @param aCharIndex The character offset to query. - * - * @return True if writing-mode is vertical at the given - * character offset; otherwise false. - */ - bool DrawsVerticallyForCharacterAtIndex(uint32_t aCharIndex); - - /** - * FirstRectForCharacterRange() returns first *character* rect in the range. - * Cocoa needs the first line rect in the range, but we cannot compute it - * on current implementation. - * - * @param aRange A range of text to examine. Its position is - * an offset from the beginning of the focused - * editor or document. - * @param aActualRange If this is not null, this returns the actual - * range used for computing the result. - * @return An NSRect containing the first character in - * aRange, in screen coordinates. - * If the length of aRange is 0, the width will - * be 0. - */ - NSRect FirstRectForCharacterRange(NSRange& aRange, - NSRange* aActualRange = nullptr); - - /** - * CharacterIndexForPoint() returns an offset of a character at aPoint. - * XXX This isn't implemented, always returns 0. - * - * @param The point in screen coordinates. - * @return The offset of the character at aPoint from - * the beginning of the focused editor or - * document. - */ - NSUInteger CharacterIndexForPoint(NSPoint& aPoint); - - /** - * GetValidAttributesForMarkedText() returns attributes which we support. - * - * @return Always empty array for now. - */ - NSArray* GetValidAttributesForMarkedText(); - - bool HasMarkedText(); - NSRange MarkedRange(); - - bool IsIMEComposing() { return mIsIMEComposing; } - bool IsIMEOpened(); - bool IsIMEEnabled() { return mIsIMEEnabled; } - bool IsASCIICapableOnly() { return mIsASCIICapableOnly; } - bool IgnoreIMECommit() { return mIgnoreIMECommit; } - - bool IgnoreIMEComposition() - { - // Ignore the IME composition events when we're pending to discard the - // composition and we are not to handle the IME composition now. - return (mPendingMethods & kDiscardIMEComposition) && - (mIsInFocusProcessing || !IsFocused()); - } - - void CommitIMEComposition(); - void CancelIMEComposition(); - - void EnableIME(bool aEnableIME); - void SetIMEOpenState(bool aOpen); - void SetASCIICapableOnly(bool aASCIICapableOnly); - - /** - * True if OSX believes that our view has keyboard focus. - */ - bool IsFocused(); - - static CFArrayRef CreateAllIMEModeList(); - static void DebugPrintAllIMEModes(); - - // Don't use ::TSMGetActiveDocument() API directly, the document may not - // be what you want. - static TSMDocumentID GetCurrentTSMDocumentID(); - -protected: - // We cannot do some jobs in the given stack by some reasons. - // Following flags and the timer provide the execution pending mechanism, - // See the comment in nsCocoaTextInputHandler.mm. - nsCOMPtr<nsITimer> mTimer; - enum { - kNotifyIMEOfFocusChangeInGecko = 1, - kDiscardIMEComposition = 2, - kSyncASCIICapableOnly = 4 - }; - uint32_t mPendingMethods; - - IMEInputHandler(nsChildView* aWidget, NSView<mozView> *aNativeView); - virtual ~IMEInputHandler(); - - void ResetTimer(); - - virtual void ExecutePendingMethods(); - - /** - * InsertTextAsCommittingComposition() commits current composition. If there - * is no composition, this starts a composition and commits it immediately. - * - * @param aAttrString A string which is committed. - * @param aReplacementRange The range which will be replaced with the - * aAttrString instead of current selection. - */ - void InsertTextAsCommittingComposition(NSAttributedString* aAttrString, - NSRange* aReplacementRange); - -private: - // If mIsIMEComposing is true, the composition string is stored here. - NSString* mIMECompositionString; - // If mIsIMEComposing is true, the start offset of the composition string. - uint32_t mIMECompositionStart; - - NSRange mMarkedRange; - NSRange mSelectedRange; - - NSRange mRangeForWritingMode; // range within which mWritingMode applies - mozilla::WritingMode mWritingMode; - - bool mIsIMEComposing; - bool mIsIMEEnabled; - bool mIsASCIICapableOnly; - bool mIgnoreIMECommit; - // This flag is enabled by OnFocusChangeInGecko, and will be cleared by - // ExecutePendingMethods. When this is true, IsFocus() returns TRUE. At - // that time, the focus processing in Gecko might not be finished yet. So, - // you cannot use WidgetQueryContentEvent or something. - bool mIsInFocusProcessing; - bool mIMEHasFocus; - - void KillIMEComposition(); - void SendCommittedText(NSString *aString); - void OpenSystemPreferredLanguageIME(); - - // Pending methods - void NotifyIMEOfFocusChangeInGecko(); - void DiscardIMEComposition(); - void SyncASCIICapableOnly(); - - static bool sStaticMembersInitialized; - static CFStringRef sLatestIMEOpenedModeInputSourceID; - static void InitStaticMembers(); - static void OnCurrentTextInputSourceChange(CFNotificationCenterRef aCenter, - void* aObserver, - CFStringRef aName, - const void* aObject, - CFDictionaryRef aUserInfo); - - static void FlushPendingMethods(nsITimer* aTimer, void* aClosure); - - /** - * ConvertToTextRangeStyle converts the given native underline style to - * our defined text range type. - * - * @param aUnderlineStyle NSUnderlineStyleSingle or - * NSUnderlineStyleThick. - * @param aSelectedRange Current selected range (or caret position). - * @return NS_TEXTRANGE_*. - */ - TextRangeType ConvertToTextRangeType(uint32_t aUnderlineStyle, - NSRange& aSelectedRange); - - /** - * GetRangeCount() computes the range count of aAttrString. - * - * @param aAttrString An NSAttributedString instance whose number of - * NSUnderlineStyleAttributeName ranges you with - * to know. - * @return The count of NSUnderlineStyleAttributeName - * ranges in aAttrString. - */ - uint32_t GetRangeCount(NSAttributedString *aString); - - /** - * CreateTextRangeArray() returns text ranges for clauses and/or caret. - * - * @param aAttrString An NSAttributedString instance which indicates - * current composition string. - * @param aSelectedRange Current selected range (or caret position). - * @return The result is set to the - * NSUnderlineStyleAttributeName ranges in - * aAttrString. - */ - already_AddRefed<mozilla::TextRangeArray> - CreateTextRangeArray(NSAttributedString *aAttrString, - NSRange& aSelectedRange); - - /** - * DispatchCompositionStartEvent() dispatches a compositionstart event and - * initializes the members indicating composition state. - * - * @return true if it can continues handling composition. - * Otherwise, e.g., canceled by the web page, - * this returns false. - */ - bool DispatchCompositionStartEvent(); - - /** - * DispatchCompositionChangeEvent() dispatches a compositionchange event on - * mWidget and modifies the members indicating composition state. - * - * @param aText User text input. - * @param aAttrString An NSAttributedString instance which indicates - * current composition string. - * @param aSelectedRange Current selected range (or caret position). - * - * @return true if it can continues handling composition. - * Otherwise, e.g., canceled by the web page, - * this returns false. - */ - bool DispatchCompositionChangeEvent(const nsString& aText, - NSAttributedString* aAttrString, - NSRange& aSelectedRange); - - /** - * DispatchCompositionCommitEvent() dispatches a compositioncommit event or - * compositioncommitasis event. If aCommitString is null, dispatches - * compositioncommitasis event. I.e., if aCommitString is null, this - * commits the composition with the last data. Otherwise, commits the - * composition with aCommitString value. - * - * @return true if the widget isn't destroyed. - * Otherwise, false. - */ - bool DispatchCompositionCommitEvent(const nsAString* aCommitString = nullptr); - - // The focused IME handler. Please note that the handler might lost the - // actual focus by deactivating the application. If we are active, this - // must have the actual focused handle. - // We cannot access to the NSInputManager during we aren't active, so, the - // focused handler can have an IME transaction even if we are deactive. - static IMEInputHandler* sFocusedIMEHandler; - - static bool sCachedIsForRTLLangage; -}; - -/** - * TextInputHandler implements the NSTextInput protocol. - */ -class TextInputHandler : public IMEInputHandler -{ -public: - static NSUInteger sLastModifierState; - - static CFArrayRef CreateAllKeyboardLayoutList(); - static void DebugPrintAllKeyboardLayouts(); - - TextInputHandler(nsChildView* aWidget, NSView<mozView> *aNativeView); - virtual ~TextInputHandler(); - - /** - * KeyDown event handler. - * - * @param aNativeEvent A native NSKeyDown event. - * @return TRUE if the event is consumed by web contents - * or chrome contents. Otherwise, FALSE. - */ - bool HandleKeyDownEvent(NSEvent* aNativeEvent); - - /** - * KeyUp event handler. - * - * @param aNativeEvent A native NSKeyUp event. - */ - void HandleKeyUpEvent(NSEvent* aNativeEvent); - - /** - * FlagsChanged event handler. - * - * @param aNativeEvent A native NSFlagsChanged event. - */ - void HandleFlagsChanged(NSEvent* aNativeEvent); - - /** - * Insert the string to content. I.e., this is a text input event handler. - * If this is called during keydown event handling, this may dispatch a - * eKeyPress event. If this is called during composition, this commits - * the composition by the aAttrString. - * - * @param aAttrString An inserted string. - * @param aReplacementRange The range which will be replaced with the - * aAttrString instead of current selection. - */ - void InsertText(NSAttributedString *aAttrString, - NSRange* aReplacementRange = nullptr); - - /** - * doCommandBySelector event handler. - * - * @param aSelector A selector of the command. - * @return TRUE if the command is consumed. Otherwise, - * FALSE. - */ - bool DoCommandBySelector(const char* aSelector); - - /** - * KeyPressWasHandled() checks whether keypress event was handled or not. - * - * @return TRUE if keypress event for latest native key - * event was handled. Otherwise, FALSE. - * If this handler isn't handling any key events, - * always returns FALSE. - */ - bool KeyPressWasHandled() - { - KeyEventState* currentKeyEvent = GetCurrentKeyEvent(); - return currentKeyEvent && currentKeyEvent->mKeyPressHandled; - } - -protected: - // Stores the association of device dependent modifier flags with a modifier - // keyCode. Being device dependent, this association may differ from one kind - // of hardware to the next. - struct ModifierKey - { - NSUInteger flags; - unsigned short keyCode; - - ModifierKey(NSUInteger aFlags, unsigned short aKeyCode) : - flags(aFlags), keyCode(aKeyCode) - { - } - - NSUInteger GetDeviceDependentFlags() const - { - return (flags & ~NSDeviceIndependentModifierFlagsMask); - } - - NSUInteger GetDeviceIndependentFlags() const - { - return (flags & NSDeviceIndependentModifierFlagsMask); - } - }; - typedef nsTArray<ModifierKey> ModifierKeyArray; - ModifierKeyArray mModifierKeys; - - /** - * GetModifierKeyForNativeKeyCode() returns the stored ModifierKey for - * the key. - */ - const ModifierKey* - GetModifierKeyForNativeKeyCode(unsigned short aKeyCode) const; - - /** - * GetModifierKeyForDeviceDependentFlags() returns the stored ModifierKey for - * the device dependent flags. - */ - const ModifierKey* - GetModifierKeyForDeviceDependentFlags(NSUInteger aFlags) const; - - /** - * DispatchKeyEventForFlagsChanged() dispatches keydown event or keyup event - * for the aNativeEvent. - * - * @param aNativeEvent A native flagschanged event which you want to - * dispatch our key event for. - * @param aDispatchKeyDown TRUE if you want to dispatch a keydown event. - * Otherwise, i.e., to dispatch keyup event, - * FALSE. - */ - void DispatchKeyEventForFlagsChanged(NSEvent* aNativeEvent, - bool aDispatchKeyDown); -}; - -} // namespace widget -} // namespace mozilla - -#endif // TextInputHandler_h_ diff --git a/widget/cocoa/TextInputHandler.mm b/widget/cocoa/TextInputHandler.mm deleted file mode 100644 index 348d99ab69..0000000000 --- a/widget/cocoa/TextInputHandler.mm +++ /dev/null @@ -1,4533 +0,0 @@ -/* -*- 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/. */ - -#include "TextInputHandler.h" - -#include "mozilla/Logging.h" - -#include "mozilla/ArrayUtils.h" -#include "mozilla/AutoRestore.h" -#include "mozilla/MiscEvents.h" -#include "mozilla/MouseEvents.h" -#include "mozilla/TextEventDispatcher.h" -#include "mozilla/TextEvents.h" - -#include "nsChildView.h" -#include "nsObjCExceptions.h" -#include "nsBidiUtils.h" -#include "nsToolkit.h" -#include "nsCocoaUtils.h" -#include "WidgetUtils.h" -#include "nsPrintfCString.h" -#include "ComplexTextInputPanel.h" - -using namespace mozilla; -using namespace mozilla::widget; - -LazyLogModule gLog("TextInputHandlerWidgets"); - -static const char* -OnOrOff(bool aBool) -{ - return aBool ? "ON" : "off"; -} - -static const char* -TrueOrFalse(bool aBool) -{ - return aBool ? "TRUE" : "FALSE"; -} - -static const char* -GetKeyNameForNativeKeyCode(unsigned short aNativeKeyCode) -{ - switch (aNativeKeyCode) { - case kVK_Escape: return "Escape"; - case kVK_RightCommand: return "Right-Command"; - case kVK_Command: return "Command"; - case kVK_Shift: return "Shift"; - case kVK_CapsLock: return "CapsLock"; - case kVK_Option: return "Option"; - case kVK_Control: return "Control"; - case kVK_RightShift: return "Right-Shift"; - case kVK_RightOption: return "Right-Option"; - case kVK_RightControl: return "Right-Control"; - case kVK_ANSI_KeypadClear: return "Clear"; - - case kVK_F1: return "F1"; - case kVK_F2: return "F2"; - case kVK_F3: return "F3"; - case kVK_F4: return "F4"; - case kVK_F5: return "F5"; - case kVK_F6: return "F6"; - case kVK_F7: return "F7"; - case kVK_F8: return "F8"; - case kVK_F9: return "F9"; - case kVK_F10: return "F10"; - case kVK_F11: return "F11"; - case kVK_F12: return "F12"; - case kVK_F13: return "F13/PrintScreen"; - case kVK_F14: return "F14/ScrollLock"; - case kVK_F15: return "F15/Pause"; - - case kVK_ANSI_Keypad0: return "NumPad-0"; - case kVK_ANSI_Keypad1: return "NumPad-1"; - case kVK_ANSI_Keypad2: return "NumPad-2"; - case kVK_ANSI_Keypad3: return "NumPad-3"; - case kVK_ANSI_Keypad4: return "NumPad-4"; - case kVK_ANSI_Keypad5: return "NumPad-5"; - case kVK_ANSI_Keypad6: return "NumPad-6"; - case kVK_ANSI_Keypad7: return "NumPad-7"; - case kVK_ANSI_Keypad8: return "NumPad-8"; - case kVK_ANSI_Keypad9: return "NumPad-9"; - - case kVK_ANSI_KeypadMultiply: return "NumPad-*"; - case kVK_ANSI_KeypadPlus: return "NumPad-+"; - case kVK_ANSI_KeypadMinus: return "NumPad--"; - case kVK_ANSI_KeypadDecimal: return "NumPad-."; - case kVK_ANSI_KeypadDivide: return "NumPad-/"; - case kVK_ANSI_KeypadEquals: return "NumPad-="; - case kVK_ANSI_KeypadEnter: return "NumPad-Enter"; - case kVK_Return: return "Return"; - case kVK_Powerbook_KeypadEnter: return "NumPad-EnterOnPowerBook"; - - case kVK_PC_Insert: return "Insert/Help"; - case kVK_PC_Delete: return "Delete"; - case kVK_Tab: return "Tab"; - case kVK_PC_Backspace: return "Backspace"; - case kVK_Home: return "Home"; - case kVK_End: return "End"; - case kVK_PageUp: return "PageUp"; - case kVK_PageDown: return "PageDown"; - case kVK_LeftArrow: return "LeftArrow"; - case kVK_RightArrow: return "RightArrow"; - case kVK_UpArrow: return "UpArrow"; - case kVK_DownArrow: return "DownArrow"; - case kVK_PC_ContextMenu: return "ContextMenu"; - - case kVK_Function: return "Function"; - case kVK_VolumeUp: return "VolumeUp"; - case kVK_VolumeDown: return "VolumeDown"; - case kVK_Mute: return "Mute"; - - case kVK_ISO_Section: return "ISO_Section"; - - case kVK_JIS_Yen: return "JIS_Yen"; - case kVK_JIS_Underscore: return "JIS_Underscore"; - case kVK_JIS_KeypadComma: return "JIS_KeypadComma"; - case kVK_JIS_Eisu: return "JIS_Eisu"; - case kVK_JIS_Kana: return "JIS_Kana"; - - case kVK_ANSI_A: return "A"; - case kVK_ANSI_B: return "B"; - case kVK_ANSI_C: return "C"; - case kVK_ANSI_D: return "D"; - case kVK_ANSI_E: return "E"; - case kVK_ANSI_F: return "F"; - case kVK_ANSI_G: return "G"; - case kVK_ANSI_H: return "H"; - case kVK_ANSI_I: return "I"; - case kVK_ANSI_J: return "J"; - case kVK_ANSI_K: return "K"; - case kVK_ANSI_L: return "L"; - case kVK_ANSI_M: return "M"; - case kVK_ANSI_N: return "N"; - case kVK_ANSI_O: return "O"; - case kVK_ANSI_P: return "P"; - case kVK_ANSI_Q: return "Q"; - case kVK_ANSI_R: return "R"; - case kVK_ANSI_S: return "S"; - case kVK_ANSI_T: return "T"; - case kVK_ANSI_U: return "U"; - case kVK_ANSI_V: return "V"; - case kVK_ANSI_W: return "W"; - case kVK_ANSI_X: return "X"; - case kVK_ANSI_Y: return "Y"; - case kVK_ANSI_Z: return "Z"; - - case kVK_ANSI_1: return "1"; - case kVK_ANSI_2: return "2"; - case kVK_ANSI_3: return "3"; - case kVK_ANSI_4: return "4"; - case kVK_ANSI_5: return "5"; - case kVK_ANSI_6: return "6"; - case kVK_ANSI_7: return "7"; - case kVK_ANSI_8: return "8"; - case kVK_ANSI_9: return "9"; - case kVK_ANSI_0: return "0"; - case kVK_ANSI_Equal: return "Equal"; - case kVK_ANSI_Minus: return "Minus"; - case kVK_ANSI_RightBracket: return "RightBracket"; - case kVK_ANSI_LeftBracket: return "LeftBracket"; - case kVK_ANSI_Quote: return "Quote"; - case kVK_ANSI_Semicolon: return "Semicolon"; - case kVK_ANSI_Backslash: return "Backslash"; - case kVK_ANSI_Comma: return "Comma"; - case kVK_ANSI_Slash: return "Slash"; - case kVK_ANSI_Period: return "Period"; - case kVK_ANSI_Grave: return "Grave"; - - default: return "undefined"; - } -} - -static const char* -GetCharacters(const NSString* aString) -{ - nsAutoString str; - nsCocoaUtils::GetStringForNSString(aString, str); - if (str.IsEmpty()) { - return ""; - } - - nsAutoString escapedStr; - for (uint32_t i = 0; i < str.Length(); i++) { - char16_t ch = str[i]; - if (ch < 0x20) { - nsPrintfCString utf8str("(U+%04X)", ch); - escapedStr += NS_ConvertUTF8toUTF16(utf8str); - } else if (ch <= 0x7E) { - escapedStr += ch; - } else { - nsPrintfCString utf8str("(U+%04X)", ch); - escapedStr += ch; - escapedStr += NS_ConvertUTF8toUTF16(utf8str); - } - } - - // the result will be freed automatically by cocoa. - NSString* result = nsCocoaUtils::ToNSString(escapedStr); - return [result UTF8String]; -} - -static const char* -GetCharacters(const CFStringRef aString) -{ - const NSString* str = reinterpret_cast<const NSString*>(aString); - return GetCharacters(str); -} - -static const char* -GetNativeKeyEventType(NSEvent* aNativeEvent) -{ - switch ([aNativeEvent type]) { - case NSKeyDown: return "NSKeyDown"; - case NSKeyUp: return "NSKeyUp"; - case NSFlagsChanged: return "NSFlagsChanged"; - default: return "not key event"; - } -} - -static const char* -GetGeckoKeyEventType(const WidgetEvent& aEvent) -{ - switch (aEvent.mMessage) { - case eKeyDown: return "eKeyDown"; - case eKeyUp: return "eKeyUp"; - case eKeyPress: return "eKeyPress"; - default: return "not key event"; - } -} - -static const char* -GetWindowLevelName(NSInteger aWindowLevel) -{ - switch (aWindowLevel) { - case kCGBaseWindowLevelKey: - return "kCGBaseWindowLevelKey (NSNormalWindowLevel)"; - case kCGMinimumWindowLevelKey: - return "kCGMinimumWindowLevelKey"; - case kCGDesktopWindowLevelKey: - return "kCGDesktopWindowLevelKey"; - case kCGBackstopMenuLevelKey: - return "kCGBackstopMenuLevelKey"; - case kCGNormalWindowLevelKey: - return "kCGNormalWindowLevelKey"; - case kCGFloatingWindowLevelKey: - return "kCGFloatingWindowLevelKey (NSFloatingWindowLevel)"; - case kCGTornOffMenuWindowLevelKey: - return "kCGTornOffMenuWindowLevelKey (NSSubmenuWindowLevel, NSTornOffMenuWindowLevel)"; - case kCGDockWindowLevelKey: - return "kCGDockWindowLevelKey (NSDockWindowLevel)"; - case kCGMainMenuWindowLevelKey: - return "kCGMainMenuWindowLevelKey (NSMainMenuWindowLevel)"; - case kCGStatusWindowLevelKey: - return "kCGStatusWindowLevelKey (NSStatusWindowLevel)"; - case kCGModalPanelWindowLevelKey: - return "kCGModalPanelWindowLevelKey (NSModalPanelWindowLevel)"; - case kCGPopUpMenuWindowLevelKey: - return "kCGPopUpMenuWindowLevelKey (NSPopUpMenuWindowLevel)"; - case kCGDraggingWindowLevelKey: - return "kCGDraggingWindowLevelKey"; - case kCGScreenSaverWindowLevelKey: - return "kCGScreenSaverWindowLevelKey (NSScreenSaverWindowLevel)"; - case kCGMaximumWindowLevelKey: - return "kCGMaximumWindowLevelKey"; - case kCGOverlayWindowLevelKey: - return "kCGOverlayWindowLevelKey"; - case kCGHelpWindowLevelKey: - return "kCGHelpWindowLevelKey"; - case kCGUtilityWindowLevelKey: - return "kCGUtilityWindowLevelKey"; - case kCGDesktopIconWindowLevelKey: - return "kCGDesktopIconWindowLevelKey"; - case kCGCursorWindowLevelKey: - return "kCGCursorWindowLevelKey"; - case kCGNumberOfWindowLevelKeys: - return "kCGNumberOfWindowLevelKeys"; - default: - return "unknown window level"; - } -} - -static bool -IsControlChar(uint32_t aCharCode) -{ - return aCharCode < ' ' || aCharCode == 0x7F; -} - -static uint32_t gHandlerInstanceCount = 0; - -static void -EnsureToLogAllKeyboardLayoutsAndIMEs() -{ - static bool sDone = false; - if (!sDone) { - sDone = true; - TextInputHandler::DebugPrintAllKeyboardLayouts(); - IMEInputHandler::DebugPrintAllIMEModes(); - } -} - -#pragma mark - - - -/****************************************************************************** - * - * TISInputSourceWrapper implementation - * - ******************************************************************************/ - -TISInputSourceWrapper* TISInputSourceWrapper::sCurrentInputSource = nullptr; - -// static -TISInputSourceWrapper& -TISInputSourceWrapper::CurrentInputSource() -{ - if (!sCurrentInputSource) { - sCurrentInputSource = new TISInputSourceWrapper(); - } - if (!sCurrentInputSource->IsInitializedByCurrentInputSource()) { - sCurrentInputSource->InitByCurrentInputSource(); - } - return *sCurrentInputSource; -} - -// static -void -TISInputSourceWrapper::Shutdown() -{ - if (!sCurrentInputSource) { - return; - } - sCurrentInputSource->Clear(); - delete sCurrentInputSource; - sCurrentInputSource = nullptr; -} - -bool -TISInputSourceWrapper::TranslateToString(UInt32 aKeyCode, UInt32 aModifiers, - UInt32 aKbType, nsAString &aStr) -{ - aStr.Truncate(); - - const UCKeyboardLayout* UCKey = GetUCKeyboardLayout(); - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TISInputSourceWrapper::TranslateToString, aKeyCode=0x%X, " - "aModifiers=0x%X, aKbType=0x%X UCKey=%p\n " - "Shift: %s, Ctrl: %s, Opt: %s, Cmd: %s, CapsLock: %s, NumLock: %s", - this, aKeyCode, aModifiers, aKbType, UCKey, - OnOrOff(aModifiers & shiftKey), OnOrOff(aModifiers & controlKey), - OnOrOff(aModifiers & optionKey), OnOrOff(aModifiers & cmdKey), - OnOrOff(aModifiers & alphaLock), - OnOrOff(aModifiers & kEventKeyModifierNumLockMask))); - - NS_ENSURE_TRUE(UCKey, false); - - UInt32 deadKeyState = 0; - UniCharCount len; - UniChar chars[5]; - OSStatus err = ::UCKeyTranslate(UCKey, aKeyCode, - kUCKeyActionDown, aModifiers >> 8, - aKbType, kUCKeyTranslateNoDeadKeysMask, - &deadKeyState, 5, &len, chars); - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TISInputSourceWrapper::TranslateToString, err=0x%X, len=%llu", - this, err, len)); - - NS_ENSURE_TRUE(err == noErr, false); - if (len == 0) { - return true; - } - NS_ENSURE_TRUE(EnsureStringLength(aStr, len), false); - NS_ASSERTION(sizeof(char16_t) == sizeof(UniChar), - "size of char16_t and size of UniChar are different"); - memcpy(aStr.BeginWriting(), chars, len * sizeof(char16_t)); - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TISInputSourceWrapper::TranslateToString, aStr=\"%s\"", - this, NS_ConvertUTF16toUTF8(aStr).get())); - - return true; -} - -uint32_t -TISInputSourceWrapper::TranslateToChar(UInt32 aKeyCode, UInt32 aModifiers, - UInt32 aKbType) -{ - nsAutoString str; - if (!TranslateToString(aKeyCode, aModifiers, aKbType, str) || - str.Length() != 1) { - return 0; - } - return static_cast<uint32_t>(str.CharAt(0)); -} - -void -TISInputSourceWrapper::InitByInputSourceID(const char* aID) -{ - Clear(); - if (!aID) - return; - - CFStringRef idstr = ::CFStringCreateWithCString(kCFAllocatorDefault, aID, - kCFStringEncodingASCII); - InitByInputSourceID(idstr); - ::CFRelease(idstr); -} - -void -TISInputSourceWrapper::InitByInputSourceID(const nsAFlatString &aID) -{ - Clear(); - if (aID.IsEmpty()) - return; - CFStringRef idstr = ::CFStringCreateWithCharacters(kCFAllocatorDefault, - reinterpret_cast<const UniChar*>(aID.get()), - aID.Length()); - InitByInputSourceID(idstr); - ::CFRelease(idstr); -} - -void -TISInputSourceWrapper::InitByInputSourceID(const CFStringRef aID) -{ - Clear(); - if (!aID) - return; - const void* keys[] = { kTISPropertyInputSourceID }; - const void* values[] = { aID }; - CFDictionaryRef filter = - ::CFDictionaryCreate(kCFAllocatorDefault, keys, values, 1, NULL, NULL); - NS_ASSERTION(filter, "failed to create the filter"); - mInputSourceList = ::TISCreateInputSourceList(filter, true); - ::CFRelease(filter); - if (::CFArrayGetCount(mInputSourceList) > 0) { - mInputSource = static_cast<TISInputSourceRef>( - const_cast<void *>(::CFArrayGetValueAtIndex(mInputSourceList, 0))); - if (IsKeyboardLayout()) { - mKeyboardLayout = mInputSource; - } - } -} - -void -TISInputSourceWrapper::InitByLayoutID(SInt32 aLayoutID, - bool aOverrideKeyboard) -{ - // NOTE: Doument new layout IDs in TextInputHandler.h when you add ones. - switch (aLayoutID) { - case 0: - InitByInputSourceID("com.apple.keylayout.US"); - break; - case 1: - InitByInputSourceID("com.apple.keylayout.Greek"); - break; - case 2: - InitByInputSourceID("com.apple.keylayout.German"); - break; - case 3: - InitByInputSourceID("com.apple.keylayout.Swedish-Pro"); - break; - case 4: - InitByInputSourceID("com.apple.keylayout.DVORAK-QWERTYCMD"); - break; - case 5: - InitByInputSourceID("com.apple.keylayout.Thai"); - break; - case 6: - InitByInputSourceID("com.apple.keylayout.Arabic"); - break; - case 7: - InitByInputSourceID("com.apple.keylayout.ArabicPC"); - break; - case 8: - InitByInputSourceID("com.apple.keylayout.French"); - break; - case 9: - InitByInputSourceID("com.apple.keylayout.Hebrew"); - break; - case 10: - InitByInputSourceID("com.apple.keylayout.Lithuanian"); - break; - case 11: - InitByInputSourceID("com.apple.keylayout.Norwegian"); - break; - case 12: - InitByInputSourceID("com.apple.keylayout.Spanish"); - break; - default: - Clear(); - break; - } - mOverrideKeyboard = aOverrideKeyboard; -} - -void -TISInputSourceWrapper::InitByCurrentInputSource() -{ - Clear(); - mInputSource = ::TISCopyCurrentKeyboardInputSource(); - mKeyboardLayout = ::TISCopyInputMethodKeyboardLayoutOverride(); - if (!mKeyboardLayout) { - mKeyboardLayout = ::TISCopyCurrentKeyboardLayoutInputSource(); - } - // If this causes composition, the current keyboard layout may input non-ASCII - // characters such as Japanese Kana characters or Hangul characters. - // However, we need to set ASCII characters to DOM key events for consistency - // with other platforms. - if (IsOpenedIMEMode()) { - TISInputSourceWrapper tis(mKeyboardLayout); - if (!tis.IsASCIICapable()) { - mKeyboardLayout = - ::TISCopyCurrentASCIICapableKeyboardLayoutInputSource(); - } - } -} - -void -TISInputSourceWrapper::InitByCurrentKeyboardLayout() -{ - Clear(); - mInputSource = ::TISCopyCurrentKeyboardLayoutInputSource(); - mKeyboardLayout = mInputSource; -} - -void -TISInputSourceWrapper::InitByCurrentASCIICapableInputSource() -{ - Clear(); - mInputSource = ::TISCopyCurrentASCIICapableKeyboardInputSource(); - mKeyboardLayout = ::TISCopyInputMethodKeyboardLayoutOverride(); - if (mKeyboardLayout) { - TISInputSourceWrapper tis(mKeyboardLayout); - if (!tis.IsASCIICapable()) { - mKeyboardLayout = nullptr; - } - } - if (!mKeyboardLayout) { - mKeyboardLayout = - ::TISCopyCurrentASCIICapableKeyboardLayoutInputSource(); - } -} - -void -TISInputSourceWrapper::InitByCurrentASCIICapableKeyboardLayout() -{ - Clear(); - mInputSource = ::TISCopyCurrentASCIICapableKeyboardLayoutInputSource(); - mKeyboardLayout = mInputSource; -} - -void -TISInputSourceWrapper::InitByCurrentInputMethodKeyboardLayoutOverride() -{ - Clear(); - mInputSource = ::TISCopyInputMethodKeyboardLayoutOverride(); - mKeyboardLayout = mInputSource; -} - -void -TISInputSourceWrapper::InitByTISInputSourceRef(TISInputSourceRef aInputSource) -{ - Clear(); - mInputSource = aInputSource; - if (IsKeyboardLayout()) { - mKeyboardLayout = mInputSource; - } -} - -void -TISInputSourceWrapper::InitByLanguage(CFStringRef aLanguage) -{ - Clear(); - mInputSource = ::TISCopyInputSourceForLanguage(aLanguage); - if (IsKeyboardLayout()) { - mKeyboardLayout = mInputSource; - } -} - -const UCKeyboardLayout* -TISInputSourceWrapper::GetUCKeyboardLayout() -{ - NS_ENSURE_TRUE(mKeyboardLayout, nullptr); - if (mUCKeyboardLayout) { - return mUCKeyboardLayout; - } - CFDataRef uchr = static_cast<CFDataRef>( - ::TISGetInputSourceProperty(mKeyboardLayout, - kTISPropertyUnicodeKeyLayoutData)); - - // We should be always able to get the layout here. - NS_ENSURE_TRUE(uchr, nullptr); - mUCKeyboardLayout = - reinterpret_cast<const UCKeyboardLayout*>(CFDataGetBytePtr(uchr)); - return mUCKeyboardLayout; -} - -bool -TISInputSourceWrapper::GetBoolProperty(const CFStringRef aKey) -{ - CFBooleanRef ret = static_cast<CFBooleanRef>( - ::TISGetInputSourceProperty(mInputSource, aKey)); - return ::CFBooleanGetValue(ret); -} - -bool -TISInputSourceWrapper::GetStringProperty(const CFStringRef aKey, - CFStringRef &aStr) -{ - aStr = static_cast<CFStringRef>( - ::TISGetInputSourceProperty(mInputSource, aKey)); - return aStr != nullptr; -} - -bool -TISInputSourceWrapper::GetStringProperty(const CFStringRef aKey, - nsAString &aStr) -{ - CFStringRef str; - GetStringProperty(aKey, str); - nsCocoaUtils::GetStringForNSString((const NSString*)str, aStr); - return !aStr.IsEmpty(); -} - -bool -TISInputSourceWrapper::IsOpenedIMEMode() -{ - NS_ENSURE_TRUE(mInputSource, false); - if (!IsIMEMode()) - return false; - return !IsASCIICapable(); -} - -bool -TISInputSourceWrapper::IsIMEMode() -{ - NS_ENSURE_TRUE(mInputSource, false); - CFStringRef str; - GetInputSourceType(str); - NS_ENSURE_TRUE(str, false); - return ::CFStringCompare(kTISTypeKeyboardInputMode, - str, 0) == kCFCompareEqualTo; -} - -bool -TISInputSourceWrapper::IsKeyboardLayout() -{ - NS_ENSURE_TRUE(mInputSource, false); - CFStringRef str; - GetInputSourceType(str); - NS_ENSURE_TRUE(str, false); - return ::CFStringCompare(kTISTypeKeyboardLayout, - str, 0) == kCFCompareEqualTo; -} - -bool -TISInputSourceWrapper::GetLanguageList(CFArrayRef &aLanguageList) -{ - NS_ENSURE_TRUE(mInputSource, false); - aLanguageList = static_cast<CFArrayRef>( - ::TISGetInputSourceProperty(mInputSource, - kTISPropertyInputSourceLanguages)); - return aLanguageList != nullptr; -} - -bool -TISInputSourceWrapper::GetPrimaryLanguage(CFStringRef &aPrimaryLanguage) -{ - NS_ENSURE_TRUE(mInputSource, false); - CFArrayRef langList; - NS_ENSURE_TRUE(GetLanguageList(langList), false); - if (::CFArrayGetCount(langList) == 0) - return false; - aPrimaryLanguage = - static_cast<CFStringRef>(::CFArrayGetValueAtIndex(langList, 0)); - return aPrimaryLanguage != nullptr; -} - -bool -TISInputSourceWrapper::GetPrimaryLanguage(nsAString &aPrimaryLanguage) -{ - NS_ENSURE_TRUE(mInputSource, false); - CFStringRef primaryLanguage; - NS_ENSURE_TRUE(GetPrimaryLanguage(primaryLanguage), false); - nsCocoaUtils::GetStringForNSString((const NSString*)primaryLanguage, - aPrimaryLanguage); - return !aPrimaryLanguage.IsEmpty(); -} - -bool -TISInputSourceWrapper::IsForRTLLanguage() -{ - if (mIsRTL < 0) { - // Get the input character of the 'A' key of ANSI keyboard layout. - nsAutoString str; - bool ret = TranslateToString(kVK_ANSI_A, 0, eKbdType_ANSI, str); - NS_ENSURE_TRUE(ret, ret); - char16_t ch = str.IsEmpty() ? char16_t(0) : str.CharAt(0); - mIsRTL = UCS2_CHAR_IS_BIDI(ch); - } - return mIsRTL != 0; -} - -bool -TISInputSourceWrapper::IsInitializedByCurrentInputSource() -{ - return mInputSource == ::TISCopyCurrentKeyboardInputSource(); -} - -void -TISInputSourceWrapper::Select() -{ - if (!mInputSource) - return; - ::TISSelectInputSource(mInputSource); -} - -void -TISInputSourceWrapper::Clear() -{ - // Clear() is always called when TISInputSourceWrappper is created. - EnsureToLogAllKeyboardLayoutsAndIMEs(); - - if (mInputSourceList) { - ::CFRelease(mInputSourceList); - } - mInputSourceList = nullptr; - mInputSource = nullptr; - mKeyboardLayout = nullptr; - mIsRTL = -1; - mUCKeyboardLayout = nullptr; - mOverrideKeyboard = false; -} - -bool -TISInputSourceWrapper::IsPrintableKeyEvent(NSEvent* aNativeKeyEvent) const -{ - UInt32 nativeKeyCode = [aNativeKeyEvent keyCode]; - - bool isPrintableKey = !TextInputHandler::IsSpecialGeckoKey(nativeKeyCode); - if (isPrintableKey && - [aNativeKeyEvent type] != NSKeyDown && - [aNativeKeyEvent type] != NSKeyUp) { - NS_WARNING("Why the printable key doesn't cause NSKeyDown or NSKeyUp?"); - isPrintableKey = false; - } - return isPrintableKey; -} - -UInt32 -TISInputSourceWrapper::GetKbdType() const -{ - // If a keyboard layout override is set, we also need to force the keyboard - // type to something ANSI to avoid test failures on machines with JIS - // keyboards (since the pair of keyboard layout and physical keyboard type - // form the actual key layout). This assumes that the test setting the - // override was written assuming an ANSI keyboard. - return mOverrideKeyboard ? eKbdType_ANSI : ::LMGetKbdType(); -} - -void -TISInputSourceWrapper::ComputeInsertStringForCharCode( - NSEvent* aNativeKeyEvent, - const WidgetKeyboardEvent& aKeyEvent, - const nsAString* aInsertString, - nsAString& aResult) -{ - if (aInsertString) { - // If the caller expects that the aInsertString will be input, we shouldn't - // change it. - aResult = *aInsertString; - } else if (IsPrintableKeyEvent(aNativeKeyEvent)) { - // If IME is open, [aNativeKeyEvent characters] may be a character - // which will be appended to the composition string. However, especially, - // while IME is disabled, most users and developers expect the key event - // works as IME closed. So, we should compute the aResult with - // the ASCII capable keyboard layout. - // NOTE: Such keyboard layouts typically change the layout to its ASCII - // capable layout when Command key is pressed. And we don't worry - // when Control key is pressed too because it causes inputting - // control characters. - // Additionally, if the key event doesn't input any text, the event may be - // dead key event. In this case, the charCode value should be the dead - // character. - UInt32 nativeKeyCode = [aNativeKeyEvent keyCode]; - if ((!aKeyEvent.IsMeta() && !aKeyEvent.IsControl() && IsOpenedIMEMode()) || - ![[aNativeKeyEvent characters] length]) { - UInt32 state = - nsCocoaUtils::ConvertToCarbonModifier([aNativeKeyEvent modifierFlags]); - uint32_t ch = TranslateToChar(nativeKeyCode, state, GetKbdType()); - if (ch) { - aResult = ch; - } - } else { - // If the caller isn't sure what string will be input, let's use - // characters of NSEvent. - nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters], aResult); - } - - // If control key is pressed and the eventChars is a non-printable control - // character, we should convert it to ASCII alphabet. - if (aKeyEvent.IsControl() && - !aResult.IsEmpty() && aResult[0] <= char16_t(26)) { - aResult = (aKeyEvent.IsShift() ^ aKeyEvent.IsCapsLocked()) ? - static_cast<char16_t>(aResult[0] + ('A' - 1)) : - static_cast<char16_t>(aResult[0] + ('a' - 1)); - } - // If Meta key is pressed, it may cause to switch the keyboard layout like - // Arabic, Russian, Hebrew, Greek and Dvorak-QWERTY. - else if (aKeyEvent.IsMeta() && - !(aKeyEvent.IsControl() || aKeyEvent.IsAlt())) { - UInt32 kbType = GetKbdType(); - UInt32 numLockState = - aKeyEvent.IsNumLocked() ? kEventKeyModifierNumLockMask : 0; - UInt32 capsLockState = aKeyEvent.IsCapsLocked() ? alphaLock : 0; - UInt32 shiftState = aKeyEvent.IsShift() ? shiftKey : 0; - uint32_t uncmdedChar = - TranslateToChar(nativeKeyCode, numLockState, kbType); - uint32_t cmdedChar = - TranslateToChar(nativeKeyCode, cmdKey | numLockState, kbType); - // If we can make a good guess at the characters that the user would - // expect this key combination to produce (with and without Shift) then - // use those characters. This also corrects for CapsLock. - uint32_t ch = 0; - if (uncmdedChar == cmdedChar) { - // The characters produced with Command seem similar to those without - // Command. - ch = TranslateToChar(nativeKeyCode, - shiftState | capsLockState | numLockState, kbType); - } else { - TISInputSourceWrapper USLayout("com.apple.keylayout.US"); - uint32_t uncmdedUSChar = - USLayout.TranslateToChar(nativeKeyCode, numLockState, kbType); - // If it looks like characters from US keyboard layout when Command key - // is pressed, we should compute a character in the layout. - if (uncmdedUSChar == cmdedChar) { - ch = USLayout.TranslateToChar(nativeKeyCode, - shiftState | capsLockState | numLockState, kbType); - } - } - - // If there is a more preferred character for the commanded key event, - // we should use it. - if (ch) { - aResult = ch; - } - } - } - - // Remove control characters which shouldn't be inputted on editor. - // XXX Currently, we don't find any cases inserting control characters with - // printable character. So, just checking first character is enough. - if (!aResult.IsEmpty() && IsControlChar(aResult[0])) { - aResult.Truncate(); - } -} - -void -TISInputSourceWrapper::InitKeyEvent(NSEvent *aNativeKeyEvent, - WidgetKeyboardEvent& aKeyEvent, - const nsAString *aInsertString) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TISInputSourceWrapper::InitKeyEvent, aNativeKeyEvent=%p, " - "aKeyEvent.mMessage=%s, aInsertString=%p, IsOpenedIMEMode()=%s", - this, aNativeKeyEvent, GetGeckoKeyEventType(aKeyEvent), aInsertString, - TrueOrFalse(IsOpenedIMEMode()))); - - NS_ENSURE_TRUE(aNativeKeyEvent, ); - - nsCocoaUtils::InitInputEvent(aKeyEvent, aNativeKeyEvent); - - // This is used only while dispatching the event (which is a synchronous - // call), so there is no need to retain and release this data. - aKeyEvent.mNativeKeyEvent = aNativeKeyEvent; - - // Fill in fields used for Cocoa NPAPI plugins - if ([aNativeKeyEvent type] == NSKeyDown || - [aNativeKeyEvent type] == NSKeyUp) { - aKeyEvent.mNativeKeyCode = [aNativeKeyEvent keyCode]; - aKeyEvent.mNativeModifierFlags = [aNativeKeyEvent modifierFlags]; - nsAutoString nativeChars; - nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters], nativeChars); - aKeyEvent.mNativeCharacters.Assign(nativeChars); - nsAutoString nativeCharsIgnoringModifiers; - nsCocoaUtils::GetStringForNSString([aNativeKeyEvent charactersIgnoringModifiers], nativeCharsIgnoringModifiers); - aKeyEvent.mNativeCharactersIgnoringModifiers.Assign(nativeCharsIgnoringModifiers); - } else if ([aNativeKeyEvent type] == NSFlagsChanged) { - aKeyEvent.mNativeKeyCode = [aNativeKeyEvent keyCode]; - aKeyEvent.mNativeModifierFlags = [aNativeKeyEvent modifierFlags]; - } - - aKeyEvent.mRefPoint = LayoutDeviceIntPoint(0, 0); - aKeyEvent.mIsChar = false; // XXX not used in XP level - - UInt32 kbType = GetKbdType(); - UInt32 nativeKeyCode = [aNativeKeyEvent keyCode]; - - aKeyEvent.mKeyCode = - ComputeGeckoKeyCode(nativeKeyCode, kbType, aKeyEvent.IsMeta()); - - switch (nativeKeyCode) { - case kVK_Command: - case kVK_Shift: - case kVK_Option: - case kVK_Control: - aKeyEvent.mLocation = nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT; - break; - - case kVK_RightCommand: - case kVK_RightShift: - case kVK_RightOption: - case kVK_RightControl: - aKeyEvent.mLocation = nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT; - break; - - case kVK_ANSI_Keypad0: - case kVK_ANSI_Keypad1: - case kVK_ANSI_Keypad2: - case kVK_ANSI_Keypad3: - case kVK_ANSI_Keypad4: - case kVK_ANSI_Keypad5: - case kVK_ANSI_Keypad6: - case kVK_ANSI_Keypad7: - case kVK_ANSI_Keypad8: - case kVK_ANSI_Keypad9: - case kVK_ANSI_KeypadMultiply: - case kVK_ANSI_KeypadPlus: - case kVK_ANSI_KeypadMinus: - case kVK_ANSI_KeypadDecimal: - case kVK_ANSI_KeypadDivide: - case kVK_ANSI_KeypadEquals: - case kVK_ANSI_KeypadEnter: - case kVK_JIS_KeypadComma: - case kVK_Powerbook_KeypadEnter: - aKeyEvent.mLocation = nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD; - break; - - default: - aKeyEvent.mLocation = nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD; - break; - } - - aKeyEvent.mIsRepeat = - ([aNativeKeyEvent type] == NSKeyDown) ? [aNativeKeyEvent isARepeat] : false; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TISInputSourceWrapper::InitKeyEvent, " - "shift=%s, ctrl=%s, alt=%s, meta=%s", - this, OnOrOff(aKeyEvent.IsShift()), OnOrOff(aKeyEvent.IsControl()), - OnOrOff(aKeyEvent.IsAlt()), OnOrOff(aKeyEvent.IsMeta()))); - - if (IsPrintableKeyEvent(aNativeKeyEvent)) { - aKeyEvent.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING; - // If insertText calls this method, let's use the string. - if (aInsertString && !aInsertString->IsEmpty() && - !IsControlChar((*aInsertString)[0])) { - aKeyEvent.mKeyValue = *aInsertString; - } - // If meta key is pressed, the printable key layout may be switched from - // non-ASCII capable layout to ASCII capable, or from Dvorak to QWERTY. - // KeyboardEvent.key value should be the switched layout's character. - else if (aKeyEvent.IsMeta()) { - nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters], - aKeyEvent.mKeyValue); - } - // If control key is pressed, some keys may produce printable character via - // [aNativeKeyEvent characters]. Otherwise, translate input character of - // the key without control key. - else if (aKeyEvent.IsControl()) { - nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters], - aKeyEvent.mKeyValue); - if (aKeyEvent.mKeyValue.IsEmpty() || - IsControlChar(aKeyEvent.mKeyValue[0])) { - NSUInteger cocoaState = - [aNativeKeyEvent modifierFlags] & ~NSControlKeyMask; - UInt32 carbonState = nsCocoaUtils::ConvertToCarbonModifier(cocoaState); - aKeyEvent.mKeyValue = - TranslateToChar(nativeKeyCode, carbonState, kbType); - } - } - // Otherwise, KeyboardEvent.key expose - // [aNativeKeyEvent characters] value. However, if IME is open and the - // keyboard layout isn't ASCII capable, exposing the non-ASCII character - // doesn't match with other platform's behavior. For the compatibility - // with other platform's Gecko, we need to set a translated character. - else if (IsOpenedIMEMode()) { - UInt32 state = - nsCocoaUtils::ConvertToCarbonModifier([aNativeKeyEvent modifierFlags]); - aKeyEvent.mKeyValue = TranslateToChar(nativeKeyCode, state, kbType); - } else { - nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters], - aKeyEvent.mKeyValue); - // If the key value is empty, the event may be a dead key event. - // If TranslateToChar() returns non-zero value, that means that - // the key may input a character with different dead key state. - if (aKeyEvent.mKeyValue.IsEmpty()) { - NSUInteger cocoaState = [aNativeKeyEvent modifierFlags]; - UInt32 carbonState = nsCocoaUtils::ConvertToCarbonModifier(cocoaState); - if (TranslateToChar(nativeKeyCode, carbonState, kbType)) { - aKeyEvent.mKeyNameIndex = KEY_NAME_INDEX_Dead; - } - } - } - - // Last resort. If .key value becomes empty string, we should use - // charactersIgnoringModifiers, if it's available. - if (aKeyEvent.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING && - (aKeyEvent.mKeyValue.IsEmpty() || - IsControlChar(aKeyEvent.mKeyValue[0]))) { - nsCocoaUtils::GetStringForNSString( - [aNativeKeyEvent charactersIgnoringModifiers], aKeyEvent.mKeyValue); - // But don't expose it if it's a control character. - if (!aKeyEvent.mKeyValue.IsEmpty() && - IsControlChar(aKeyEvent.mKeyValue[0])) { - aKeyEvent.mKeyValue.Truncate(); - } - } - } else { - // Compute the key for non-printable keys and some special printable keys. - aKeyEvent.mKeyNameIndex = ComputeGeckoKeyNameIndex(nativeKeyCode); - } - - aKeyEvent.mCodeNameIndex = ComputeGeckoCodeNameIndex(nativeKeyCode); - MOZ_ASSERT(aKeyEvent.mCodeNameIndex != CODE_NAME_INDEX_USE_STRING); - - NS_OBJC_END_TRY_ABORT_BLOCK -} - -void -TISInputSourceWrapper::WillDispatchKeyboardEvent( - NSEvent* aNativeKeyEvent, - const nsAString* aInsertString, - WidgetKeyboardEvent& aKeyEvent) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Nothing to do here if the native key event is neither NSKeyDown nor - // NSKeyUp because accessing [aNativeKeyEvent characters] causes throwing - // an exception. - if ([aNativeKeyEvent type] != NSKeyDown && - [aNativeKeyEvent type] != NSKeyUp) { - return; - } - - UInt32 kbType = GetKbdType(); - - if (MOZ_LOG_TEST(gLog, LogLevel::Info)) { - nsAutoString chars; - nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters], chars); - NS_ConvertUTF16toUTF8 utf8Chars(chars); - char16_t uniChar = static_cast<char16_t>(aKeyEvent.mCharCode); - MOZ_LOG(gLog, LogLevel::Info, - ("%p TISInputSourceWrapper::WillDispatchKeyboardEvent, " - "aNativeKeyEvent=%p, [aNativeKeyEvent characters]=\"%s\", " - "aKeyEvent={ mMessage=%s, mCharCode=0x%X(%s) }, kbType=0x%X, " - "IsOpenedIMEMode()=%s", - this, aNativeKeyEvent, utf8Chars.get(), - GetGeckoKeyEventType(aKeyEvent), aKeyEvent.mCharCode, - uniChar ? NS_ConvertUTF16toUTF8(&uniChar, 1).get() : "", - kbType, TrueOrFalse(IsOpenedIMEMode()))); - } - - nsAutoString insertStringForCharCode; - ComputeInsertStringForCharCode(aNativeKeyEvent, aKeyEvent, aInsertString, - insertStringForCharCode); - - // The mCharCode was set from mKeyValue. However, for example, when Ctrl key - // is pressed, its value should indicate an ASCII character for backward - // compatibility rather than inputting character without the modifiers. - // Therefore, we need to modify mCharCode value here. - uint32_t charCode = - insertStringForCharCode.IsEmpty() ? 0 : insertStringForCharCode[0]; - aKeyEvent.SetCharCode(charCode); - // this is not a special key XXX not used in XP - aKeyEvent.mIsChar = (aKeyEvent.mMessage == eKeyPress); - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TISInputSourceWrapper::WillDispatchKeyboardEvent, " - "aKeyEvent.mKeyCode=0x%X, aKeyEvent.mCharCode=0x%X", - this, aKeyEvent.mKeyCode, aKeyEvent.mCharCode)); - - TISInputSourceWrapper USLayout("com.apple.keylayout.US"); - bool isRomanKeyboardLayout = IsASCIICapable(); - - UInt32 key = [aNativeKeyEvent keyCode]; - - // Caps lock and num lock modifier state: - UInt32 lockState = 0; - if ([aNativeKeyEvent modifierFlags] & NSAlphaShiftKeyMask) { - lockState |= alphaLock; - } - if ([aNativeKeyEvent modifierFlags] & NSNumericPadKeyMask) { - lockState |= kEventKeyModifierNumLockMask; - } - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TISInputSourceWrapper::WillDispatchKeyboardEvent, " - "isRomanKeyboardLayout=%s, key=0x%X", - this, TrueOrFalse(isRomanKeyboardLayout), kbType, key)); - - nsString str; - - // normal chars - uint32_t unshiftedChar = TranslateToChar(key, lockState, kbType); - UInt32 shiftLockMod = shiftKey | lockState; - uint32_t shiftedChar = TranslateToChar(key, shiftLockMod, kbType); - - // characters generated with Cmd key - // XXX we should remove CapsLock state, which changes characters from - // Latin to Cyrillic with Russian layout on 10.4 only when Cmd key - // is pressed. - UInt32 numState = (lockState & ~alphaLock); // only num lock state - uint32_t uncmdedChar = TranslateToChar(key, numState, kbType); - UInt32 shiftNumMod = numState | shiftKey; - uint32_t uncmdedShiftChar = TranslateToChar(key, shiftNumMod, kbType); - uint32_t uncmdedUSChar = USLayout.TranslateToChar(key, numState, kbType); - UInt32 cmdNumMod = cmdKey | numState; - uint32_t cmdedChar = TranslateToChar(key, cmdNumMod, kbType); - UInt32 cmdShiftNumMod = shiftKey | cmdNumMod; - uint32_t cmdedShiftChar = TranslateToChar(key, cmdShiftNumMod, kbType); - - // Is the keyboard layout changed by Cmd key? - // E.g., Arabic, Russian, Hebrew, Greek and Dvorak-QWERTY. - bool isCmdSwitchLayout = uncmdedChar != cmdedChar; - // Is the keyboard layout for Latin, but Cmd key switches the layout? - // I.e., Dvorak-QWERTY - bool isDvorakQWERTY = isCmdSwitchLayout && isRomanKeyboardLayout; - - // If the current keyboard is not Dvorak-QWERTY or Cmd is not pressed, - // we should append unshiftedChar and shiftedChar for handling the - // normal characters. These are the characters that the user is most - // likely to associate with this key. - if ((unshiftedChar || shiftedChar) && - (!aKeyEvent.IsMeta() || !isDvorakQWERTY)) { - AlternativeCharCode altCharCodes(unshiftedChar, shiftedChar); - aKeyEvent.mAlternativeCharCodes.AppendElement(altCharCodes); - } - MOZ_LOG(gLog, LogLevel::Info, - ("%p TISInputSourceWrapper::WillDispatchKeyboardEvent, " - "aKeyEvent.isMeta=%s, isDvorakQWERTY=%s, " - "unshiftedChar=U+%X, shiftedChar=U+%X", - this, OnOrOff(aKeyEvent.IsMeta()), TrueOrFalse(isDvorakQWERTY), - unshiftedChar, shiftedChar)); - - // Most keyboard layouts provide the same characters in the NSEvents - // with Command+Shift as with Command. However, with Command+Shift we - // want the character on the second level. e.g. With a US QWERTY - // layout, we want "?" when the "/","?" key is pressed with - // Command+Shift. - - // On a German layout, the OS gives us '/' with Cmd+Shift+SS(eszett) - // even though Cmd+SS is 'SS' and Shift+'SS' is '?'. This '/' seems - // like a hack to make the Cmd+"?" event look the same as the Cmd+"?" - // event on a US keyboard. The user thinks they are typing Cmd+"?", so - // we'll prefer the "?" character, replacing mCharCode with shiftedChar - // when Shift is pressed. However, in case there is a layout where the - // character unique to Cmd+Shift is the character that the user expects, - // we'll send it as an alternative char. - bool hasCmdShiftOnlyChar = - cmdedChar != cmdedShiftChar && uncmdedShiftChar != cmdedShiftChar; - uint32_t originalCmdedShiftChar = cmdedShiftChar; - - // If we can make a good guess at the characters that the user would - // expect this key combination to produce (with and without Shift) then - // use those characters. This also corrects for CapsLock, which was - // ignored above. - if (!isCmdSwitchLayout) { - // The characters produced with Command seem similar to those without - // Command. - if (unshiftedChar) { - cmdedChar = unshiftedChar; - } - if (shiftedChar) { - cmdedShiftChar = shiftedChar; - } - } else if (uncmdedUSChar == cmdedChar) { - // It looks like characters from a US layout are provided when Command - // is down. - uint32_t ch = USLayout.TranslateToChar(key, lockState, kbType); - if (ch) { - cmdedChar = ch; - } - ch = USLayout.TranslateToChar(key, shiftLockMod, kbType); - if (ch) { - cmdedShiftChar = ch; - } - } - - // If the current keyboard layout is switched by the Cmd key, - // we should append cmdedChar and shiftedCmdChar that are - // Latin char for the key. - // If the keyboard layout is Dvorak-QWERTY, we should append them only when - // command key is pressed because when command key isn't pressed, uncmded - // chars have been appended already. - if ((cmdedChar || cmdedShiftChar) && isCmdSwitchLayout && - (aKeyEvent.IsMeta() || !isDvorakQWERTY)) { - AlternativeCharCode altCharCodes(cmdedChar, cmdedShiftChar); - aKeyEvent.mAlternativeCharCodes.AppendElement(altCharCodes); - } - MOZ_LOG(gLog, LogLevel::Info, - ("%p TISInputSourceWrapper::WillDispatchKeyboardEvent, " - "hasCmdShiftOnlyChar=%s, isCmdSwitchLayout=%s, isDvorakQWERTY=%s, " - "cmdedChar=U+%X, cmdedShiftChar=U+%X", - this, TrueOrFalse(hasCmdShiftOnlyChar), TrueOrFalse(isDvorakQWERTY), - TrueOrFalse(isDvorakQWERTY), cmdedChar, cmdedShiftChar)); - // Special case for 'SS' key of German layout. See the comment of - // hasCmdShiftOnlyChar definition for the detail. - if (hasCmdShiftOnlyChar && originalCmdedShiftChar) { - AlternativeCharCode altCharCodes(0, originalCmdedShiftChar); - aKeyEvent.mAlternativeCharCodes.AppendElement(altCharCodes); - } - MOZ_LOG(gLog, LogLevel::Info, - ("%p TISInputSourceWrapper::WillDispatchKeyboardEvent, " - "hasCmdShiftOnlyChar=%s, originalCmdedShiftChar=U+%X", - this, TrueOrFalse(hasCmdShiftOnlyChar), originalCmdedShiftChar)); - - NS_OBJC_END_TRY_ABORT_BLOCK -} - -uint32_t -TISInputSourceWrapper::ComputeGeckoKeyCode(UInt32 aNativeKeyCode, - UInt32 aKbType, - bool aCmdIsPressed) -{ - MOZ_LOG(gLog, LogLevel::Info, - ("%p TISInputSourceWrapper::ComputeGeckoKeyCode, aNativeKeyCode=0x%X, " - "aKbType=0x%X, aCmdIsPressed=%s, IsOpenedIMEMode()=%s, " - "IsASCIICapable()=%s", - this, aNativeKeyCode, aKbType, TrueOrFalse(aCmdIsPressed), - TrueOrFalse(IsOpenedIMEMode()), TrueOrFalse(IsASCIICapable()))); - - switch (aNativeKeyCode) { - case kVK_Space: return NS_VK_SPACE; - case kVK_Escape: return NS_VK_ESCAPE; - - // modifiers - case kVK_RightCommand: - case kVK_Command: return NS_VK_META; - case kVK_RightShift: - case kVK_Shift: return NS_VK_SHIFT; - case kVK_CapsLock: return NS_VK_CAPS_LOCK; - case kVK_RightControl: - case kVK_Control: return NS_VK_CONTROL; - case kVK_RightOption: - case kVK_Option: return NS_VK_ALT; - - case kVK_ANSI_KeypadClear: return NS_VK_CLEAR; - - // function keys - case kVK_F1: return NS_VK_F1; - case kVK_F2: return NS_VK_F2; - case kVK_F3: return NS_VK_F3; - case kVK_F4: return NS_VK_F4; - case kVK_F5: return NS_VK_F5; - case kVK_F6: return NS_VK_F6; - case kVK_F7: return NS_VK_F7; - case kVK_F8: return NS_VK_F8; - case kVK_F9: return NS_VK_F9; - case kVK_F10: return NS_VK_F10; - case kVK_F11: return NS_VK_F11; - case kVK_F12: return NS_VK_F12; - // case kVK_F13: return NS_VK_F13; // clash with the 3 below - // case kVK_F14: return NS_VK_F14; - // case kVK_F15: return NS_VK_F15; - case kVK_F16: return NS_VK_F16; - case kVK_F17: return NS_VK_F17; - case kVK_F18: return NS_VK_F18; - case kVK_F19: return NS_VK_F19; - - case kVK_PC_Pause: return NS_VK_PAUSE; - case kVK_PC_ScrollLock: return NS_VK_SCROLL_LOCK; - case kVK_PC_PrintScreen: return NS_VK_PRINTSCREEN; - - // keypad - case kVK_ANSI_Keypad0: return NS_VK_NUMPAD0; - case kVK_ANSI_Keypad1: return NS_VK_NUMPAD1; - case kVK_ANSI_Keypad2: return NS_VK_NUMPAD2; - case kVK_ANSI_Keypad3: return NS_VK_NUMPAD3; - case kVK_ANSI_Keypad4: return NS_VK_NUMPAD4; - case kVK_ANSI_Keypad5: return NS_VK_NUMPAD5; - case kVK_ANSI_Keypad6: return NS_VK_NUMPAD6; - case kVK_ANSI_Keypad7: return NS_VK_NUMPAD7; - case kVK_ANSI_Keypad8: return NS_VK_NUMPAD8; - case kVK_ANSI_Keypad9: return NS_VK_NUMPAD9; - - case kVK_ANSI_KeypadMultiply: return NS_VK_MULTIPLY; - case kVK_ANSI_KeypadPlus: return NS_VK_ADD; - case kVK_ANSI_KeypadMinus: return NS_VK_SUBTRACT; - case kVK_ANSI_KeypadDecimal: return NS_VK_DECIMAL; - case kVK_ANSI_KeypadDivide: return NS_VK_DIVIDE; - - case kVK_JIS_KeypadComma: return NS_VK_SEPARATOR; - - // IME keys - case kVK_JIS_Eisu: return NS_VK_EISU; - case kVK_JIS_Kana: return NS_VK_KANA; - - // these may clash with forward delete and help - case kVK_PC_Insert: return NS_VK_INSERT; - case kVK_PC_Delete: return NS_VK_DELETE; - - case kVK_PC_Backspace: return NS_VK_BACK; - case kVK_Tab: return NS_VK_TAB; - - case kVK_Home: return NS_VK_HOME; - case kVK_End: return NS_VK_END; - - case kVK_PageUp: return NS_VK_PAGE_UP; - case kVK_PageDown: return NS_VK_PAGE_DOWN; - - case kVK_LeftArrow: return NS_VK_LEFT; - case kVK_RightArrow: return NS_VK_RIGHT; - case kVK_UpArrow: return NS_VK_UP; - case kVK_DownArrow: return NS_VK_DOWN; - - case kVK_PC_ContextMenu: return NS_VK_CONTEXT_MENU; - - case kVK_ANSI_1: return NS_VK_1; - case kVK_ANSI_2: return NS_VK_2; - case kVK_ANSI_3: return NS_VK_3; - case kVK_ANSI_4: return NS_VK_4; - case kVK_ANSI_5: return NS_VK_5; - case kVK_ANSI_6: return NS_VK_6; - case kVK_ANSI_7: return NS_VK_7; - case kVK_ANSI_8: return NS_VK_8; - case kVK_ANSI_9: return NS_VK_9; - case kVK_ANSI_0: return NS_VK_0; - - case kVK_ANSI_KeypadEnter: - case kVK_Return: - case kVK_Powerbook_KeypadEnter: return NS_VK_RETURN; - } - - // If Cmd key is pressed, that causes switching keyboard layout temporarily. - // E.g., Dvorak-QWERTY. Therefore, if Cmd key is pressed, we should honor it. - UInt32 modifiers = aCmdIsPressed ? cmdKey : 0; - - uint32_t charCode = TranslateToChar(aNativeKeyCode, modifiers, aKbType); - - // Special case for Mac. Mac inputs Yen sign (U+00A5) directly instead of - // Back slash (U+005C). We should return NS_VK_BACK_SLASH for compatibility - // with other platforms. - // XXX How about Won sign (U+20A9) which has same problem as Yen sign? - if (charCode == 0x00A5) { - return NS_VK_BACK_SLASH; - } - - uint32_t keyCode = WidgetUtils::ComputeKeyCodeFromChar(charCode); - if (keyCode) { - return keyCode; - } - - // If the unshifed char isn't an ASCII character, use shifted char. - charCode = TranslateToChar(aNativeKeyCode, modifiers | shiftKey, aKbType); - keyCode = WidgetUtils::ComputeKeyCodeFromChar(charCode); - if (keyCode) { - return keyCode; - } - - // If this is ASCII capable, give up to compute it. - if (IsASCIICapable()) { - return 0; - } - - // Retry with ASCII capable keyboard layout. - TISInputSourceWrapper currentKeyboardLayout; - currentKeyboardLayout.InitByCurrentASCIICapableKeyboardLayout(); - NS_ENSURE_TRUE(mInputSource != currentKeyboardLayout.mInputSource, 0); - keyCode = currentKeyboardLayout.ComputeGeckoKeyCode(aNativeKeyCode, aKbType, - aCmdIsPressed); - - // However, if keyCode isn't for an alphabet keys or a numeric key, we should - // ignore it. For example, comma key of Thai layout is same as close-square- - // bracket key of US layout and an unicode character key of Thai layout is - // same as comma key of US layout. If we return NS_VK_COMMA for latter key, - // web application developers cannot distinguish with the former key. - return ((keyCode >= NS_VK_A && keyCode <= NS_VK_Z) || - (keyCode >= NS_VK_0 && keyCode <= NS_VK_9)) ? keyCode : 0; -} - -// static -KeyNameIndex -TISInputSourceWrapper::ComputeGeckoKeyNameIndex(UInt32 aNativeKeyCode) -{ - // NOTE: - // When unsupported keys like Convert, Nonconvert of Japanese keyboard is - // pressed: - // on 10.6.x, 'A' key event is fired (and also actually 'a' is inserted). - // on 10.7.x, Nothing happens. - // on 10.8.x, Nothing happens. - // on 10.9.x, FlagsChanged event is fired with keyCode 0xFF. - switch (aNativeKeyCode) { - -#define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \ - case aNativeKey: return aKeyNameIndex; - -#include "NativeKeyToDOMKeyName.h" - -#undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX - - default: - return KEY_NAME_INDEX_Unidentified; - } -} - -// static -CodeNameIndex -TISInputSourceWrapper::ComputeGeckoCodeNameIndex(UInt32 aNativeKeyCode) -{ - switch (aNativeKeyCode) { - -#define NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX(aNativeKey, aCodeNameIndex) \ - case aNativeKey: return aCodeNameIndex; - -#include "NativeKeyToDOMCodeName.h" - -#undef NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX - - default: - return CODE_NAME_INDEX_UNKNOWN; - } -} - - -#pragma mark - - - -/****************************************************************************** - * - * TextInputHandler implementation (static methods) - * - ******************************************************************************/ - -NSUInteger TextInputHandler::sLastModifierState = 0; - -// static -CFArrayRef -TextInputHandler::CreateAllKeyboardLayoutList() -{ - const void* keys[] = { kTISPropertyInputSourceType }; - const void* values[] = { kTISTypeKeyboardLayout }; - CFDictionaryRef filter = - ::CFDictionaryCreate(kCFAllocatorDefault, keys, values, 1, NULL, NULL); - NS_ASSERTION(filter, "failed to create the filter"); - CFArrayRef list = ::TISCreateInputSourceList(filter, true); - ::CFRelease(filter); - return list; -} - -// static -void -TextInputHandler::DebugPrintAllKeyboardLayouts() -{ - if (MOZ_LOG_TEST(gLog, LogLevel::Info)) { - CFArrayRef list = CreateAllKeyboardLayoutList(); - MOZ_LOG(gLog, LogLevel::Info, ("Keyboard layout configuration:")); - CFIndex idx = ::CFArrayGetCount(list); - TISInputSourceWrapper tis; - for (CFIndex i = 0; i < idx; ++i) { - TISInputSourceRef inputSource = static_cast<TISInputSourceRef>( - const_cast<void *>(::CFArrayGetValueAtIndex(list, i))); - tis.InitByTISInputSourceRef(inputSource); - nsAutoString name, isid; - tis.GetLocalizedName(name); - tis.GetInputSourceID(isid); - MOZ_LOG(gLog, LogLevel::Info, - (" %s\t<%s>%s%s\n", - NS_ConvertUTF16toUTF8(name).get(), - NS_ConvertUTF16toUTF8(isid).get(), - tis.IsASCIICapable() ? "" : "\t(Isn't ASCII capable)", - tis.IsKeyboardLayout() && tis.GetUCKeyboardLayout() ? - "" : "\t(uchr is NOT AVAILABLE)")); - } - ::CFRelease(list); - } -} - - -#pragma mark - - - -/****************************************************************************** - * - * TextInputHandler implementation - * - ******************************************************************************/ - -TextInputHandler::TextInputHandler(nsChildView* aWidget, - NSView<mozView> *aNativeView) : - IMEInputHandler(aWidget, aNativeView) -{ - EnsureToLogAllKeyboardLayoutsAndIMEs(); - [mView installTextInputHandler:this]; -} - -TextInputHandler::~TextInputHandler() -{ - [mView uninstallTextInputHandler]; -} - -bool -TextInputHandler::HandleKeyDownEvent(NSEvent* aNativeEvent) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - if (Destroyed()) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::HandleKeyDownEvent, " - "widget has been already destroyed", this)); - return false; - } - - // Insert empty line to the log for easier to read. - MOZ_LOG(gLog, LogLevel::Info, ("")); - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::HandleKeyDownEvent, aNativeEvent=%p, " - "type=%s, keyCode=%lld (0x%X), modifierFlags=0x%X, characters=\"%s\", " - "charactersIgnoringModifiers=\"%s\"", - this, aNativeEvent, GetNativeKeyEventType(aNativeEvent), - [aNativeEvent keyCode], [aNativeEvent keyCode], - [aNativeEvent modifierFlags], GetCharacters([aNativeEvent characters]), - GetCharacters([aNativeEvent charactersIgnoringModifiers]))); - - // Except when Command key is pressed, we should hide mouse cursor until - // next mousemove. Handling here means that: - // - Don't hide mouse cursor at pressing modifier key - // - Hide mouse cursor even if the key event will be handled by IME (i.e., - // even without dispatching eKeyPress events) - // - Hide mouse cursor even when a plugin has focus - if (!([aNativeEvent modifierFlags] & NSCommandKeyMask)) { - [NSCursor setHiddenUntilMouseMoves:YES]; - } - - RefPtr<nsChildView> widget(mWidget); - - KeyEventState* currentKeyEvent = PushKeyEvent(aNativeEvent); - AutoKeyEventStateCleaner remover(this); - - ComplexTextInputPanel* ctiPanel = ComplexTextInputPanel::GetSharedComplexTextInputPanel(); - if (ctiPanel && ctiPanel->IsInComposition()) { - nsAutoString committed; - ctiPanel->InterpretKeyEvent(aNativeEvent, committed); - if (!committed.IsEmpty()) { - nsresult rv = mDispatcher->BeginNativeInputTransaction(); - if (NS_WARN_IF(NS_FAILED(rv))) { - MOZ_LOG(gLog, LogLevel::Error, - ("%p IMEInputHandler::HandleKeyDownEvent, " - "FAILED, due to BeginNativeInputTransaction() failure " - "at dispatching keydown for ComplexTextInputPanel", this)); - return false; - } - - WidgetKeyboardEvent imeEvent(true, eKeyDown, widget); - currentKeyEvent->InitKeyEvent(this, imeEvent); - imeEvent.mPluginTextEventString.Assign(committed); - nsEventStatus status = nsEventStatus_eIgnore; - mDispatcher->DispatchKeyboardEvent(eKeyDown, imeEvent, status, - currentKeyEvent); - } - return true; - } - - NSResponder* firstResponder = [[mView window] firstResponder]; - - nsresult rv = mDispatcher->BeginNativeInputTransaction(); - if (NS_WARN_IF(NS_FAILED(rv))) { - MOZ_LOG(gLog, LogLevel::Error, - ("%p IMEInputHandler::HandleKeyDownEvent, " - "FAILED, due to BeginNativeInputTransaction() failure " - "at dispatching keydown for ordinal cases", this)); - return false; - } - - WidgetKeyboardEvent keydownEvent(true, eKeyDown, widget); - currentKeyEvent->InitKeyEvent(this, keydownEvent); - - nsEventStatus status = nsEventStatus_eIgnore; - mDispatcher->DispatchKeyboardEvent(eKeyDown, keydownEvent, status, - currentKeyEvent); - currentKeyEvent->mKeyDownHandled = - (status == nsEventStatus_eConsumeNoDefault); - - if (Destroyed()) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::HandleKeyDownEvent, " - "widget was destroyed by keydown event", this)); - return currentKeyEvent->IsDefaultPrevented(); - } - - // The key down event may have shifted the focus, in which - // case we should not fire the key press. - // XXX This is a special code only on Cocoa widget, why is this needed? - if (firstResponder != [[mView window] firstResponder]) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::HandleKeyDownEvent, " - "view lost focus by keydown event", this)); - return currentKeyEvent->IsDefaultPrevented(); - } - - if (currentKeyEvent->IsDefaultPrevented()) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::HandleKeyDownEvent, " - "keydown event's default is prevented", this)); - return true; - } - - // Let Cocoa interpret the key events, caching IsIMEComposing first. - bool wasComposing = IsIMEComposing(); - bool interpretKeyEventsCalled = false; - // Don't call interpretKeyEvents when a plugin has focus. If we call it, - // for example, a character is inputted twice during a composition in e10s - // mode. - if (!widget->IsPluginFocused() && (IsIMEEnabled() || IsASCIICapableOnly())) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::HandleKeyDownEvent, calling interpretKeyEvents", - this)); - [mView interpretKeyEvents:[NSArray arrayWithObject:aNativeEvent]]; - interpretKeyEventsCalled = true; - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::HandleKeyDownEvent, called interpretKeyEvents", - this)); - } - - if (Destroyed()) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::HandleKeyDownEvent, widget was destroyed", - this)); - return currentKeyEvent->IsDefaultPrevented(); - } - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::HandleKeyDownEvent, wasComposing=%s, " - "IsIMEComposing()=%s", - this, TrueOrFalse(wasComposing), TrueOrFalse(IsIMEComposing()))); - - if (currentKeyEvent->CanDispatchKeyPressEvent() && - !wasComposing && !IsIMEComposing()) { - rv = mDispatcher->BeginNativeInputTransaction(); - if (NS_WARN_IF(NS_FAILED(rv))) { - MOZ_LOG(gLog, LogLevel::Error, - ("%p IMEInputHandler::HandleKeyDownEvent, " - "FAILED, due to BeginNativeInputTransaction() failure " - "at dispatching keypress", this)); - return false; - } - - WidgetKeyboardEvent keypressEvent(true, eKeyPress, widget); - currentKeyEvent->InitKeyEvent(this, keypressEvent); - - // If we called interpretKeyEvents and this isn't normal character input - // then IME probably ate the event for some reason. We do not want to - // send a key press event in that case. - // TODO: - // There are some other cases which IME eats the current event. - // 1. If key events were nested during calling interpretKeyEvents, it means - // that IME did something. Then, we should do nothing. - // 2. If one or more commands are called like "deleteBackward", we should - // dispatch keypress event at that time. Note that the command may have - // been a converted or generated action by IME. Then, we shouldn't do - // our default action for this key. - if (!(interpretKeyEventsCalled && - IsNormalCharInputtingEvent(keypressEvent))) { - currentKeyEvent->mKeyPressDispatched = - mDispatcher->MaybeDispatchKeypressEvents(keypressEvent, status, - currentKeyEvent); - currentKeyEvent->mKeyPressHandled = - (status == nsEventStatus_eConsumeNoDefault); - currentKeyEvent->mKeyPressDispatched = true; - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::HandleKeyDownEvent, keypress event dispatched", - this)); - } - } - - // Note: mWidget might have become null here. Don't count on it from here on. - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::HandleKeyDownEvent, " - "keydown handled=%s, keypress handled=%s, causedOtherKeyEvents=%s, " - "compositionDispatched=%s", - this, TrueOrFalse(currentKeyEvent->mKeyDownHandled), - TrueOrFalse(currentKeyEvent->mKeyPressHandled), - TrueOrFalse(currentKeyEvent->mCausedOtherKeyEvents), - TrueOrFalse(currentKeyEvent->mCompositionDispatched))); - // Insert empty line to the log for easier to read. - MOZ_LOG(gLog, LogLevel::Info, ("")); - return currentKeyEvent->IsDefaultPrevented(); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); -} - -void -TextInputHandler::HandleKeyUpEvent(NSEvent* aNativeEvent) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::HandleKeyUpEvent, aNativeEvent=%p, " - "type=%s, keyCode=%lld (0x%X), modifierFlags=0x%X, characters=\"%s\", " - "charactersIgnoringModifiers=\"%s\", " - "IsIMEComposing()=%s", - this, aNativeEvent, GetNativeKeyEventType(aNativeEvent), - [aNativeEvent keyCode], [aNativeEvent keyCode], - [aNativeEvent modifierFlags], GetCharacters([aNativeEvent characters]), - GetCharacters([aNativeEvent charactersIgnoringModifiers]), - TrueOrFalse(IsIMEComposing()))); - - if (Destroyed()) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::HandleKeyUpEvent, " - "widget has been already destroyed", this)); - return; - } - - nsresult rv = mDispatcher->BeginNativeInputTransaction(); - if (NS_WARN_IF(NS_FAILED(rv))) { - MOZ_LOG(gLog, LogLevel::Error, - ("%p IMEInputHandler::HandleKeyUpEvent, " - "FAILED, due to BeginNativeInputTransaction() failure", this)); - return; - } - - WidgetKeyboardEvent keyupEvent(true, eKeyUp, mWidget); - InitKeyEvent(aNativeEvent, keyupEvent); - - KeyEventState currentKeyEvent(aNativeEvent); - nsEventStatus status = nsEventStatus_eIgnore; - mDispatcher->DispatchKeyboardEvent(eKeyUp, keyupEvent, status, - ¤tKeyEvent); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -TextInputHandler::HandleFlagsChanged(NSEvent* aNativeEvent) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (Destroyed()) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::HandleFlagsChanged, " - "widget has been already destroyed", this)); - return; - } - - RefPtr<nsChildView> kungFuDeathGrip(mWidget); - mozilla::Unused << kungFuDeathGrip; // Not referenced within this function - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::HandleFlagsChanged, aNativeEvent=%p, " - "type=%s, keyCode=%s (0x%X), modifierFlags=0x%08X, " - "sLastModifierState=0x%08X, IsIMEComposing()=%s", - this, aNativeEvent, GetNativeKeyEventType(aNativeEvent), - GetKeyNameForNativeKeyCode([aNativeEvent keyCode]), [aNativeEvent keyCode], - [aNativeEvent modifierFlags], sLastModifierState, - TrueOrFalse(IsIMEComposing()))); - - MOZ_ASSERT([aNativeEvent type] == NSFlagsChanged); - - NSUInteger diff = [aNativeEvent modifierFlags] ^ sLastModifierState; - // Device dependent flags for left-control key, both shift keys, both command - // keys and both option keys have been defined in Next's SDK. But we - // shouldn't use it directly as far as possible since Cocoa SDK doesn't - // define them. Fortunately, we need them only when we dispatch keyup - // events. So, we can usually know the actual relation between keyCode and - // device dependent flags. However, we need to remove following flags first - // since the differences don't indicate modifier key state. - // NX_STYLUSPROXIMITYMASK: Probably used for pen like device. - // kCGEventFlagMaskNonCoalesced (= NX_NONCOALSESCEDMASK): See the document for - // Quartz Event Services. - diff &= ~(NX_STYLUSPROXIMITYMASK | kCGEventFlagMaskNonCoalesced); - - switch ([aNativeEvent keyCode]) { - // CapsLock state and other modifier states are different: - // CapsLock state does not revert when the CapsLock key goes up, as the - // modifier state does for other modifier keys on key up. - case kVK_CapsLock: { - // Fire key down event for caps lock. - DispatchKeyEventForFlagsChanged(aNativeEvent, true); - // XXX should we fire keyup event too? The keyup event for CapsLock key - // is never dispatched on Gecko. - // XXX WebKit dispatches keydown event when CapsLock is locked, otherwise, - // keyup event. If we do so, we cannot keep the consistency with other - // platform's behavior... - break; - } - - // If the event is caused by pressing or releasing a modifier key, just - // dispatch the key's event. - case kVK_Shift: - case kVK_RightShift: - case kVK_Command: - case kVK_RightCommand: - case kVK_Control: - case kVK_RightControl: - case kVK_Option: - case kVK_RightOption: - case kVK_Help: { - // We assume that at most one modifier is changed per event if the event - // is caused by pressing or releasing a modifier key. - bool isKeyDown = ([aNativeEvent modifierFlags] & diff) != 0; - DispatchKeyEventForFlagsChanged(aNativeEvent, isKeyDown); - // XXX Some applications might send the event with incorrect device- - // dependent flags. - if (isKeyDown && ((diff & ~NSDeviceIndependentModifierFlagsMask) != 0)) { - unsigned short keyCode = [aNativeEvent keyCode]; - const ModifierKey* modifierKey = - GetModifierKeyForDeviceDependentFlags(diff); - if (modifierKey && modifierKey->keyCode != keyCode) { - // Although, we're not sure the actual cause of this case, the stored - // modifier information and the latest key event information may be - // mismatched. Then, let's reset the stored information. - // NOTE: If this happens, it may fail to handle NSFlagsChanged event - // in the default case (below). However, it's the rare case handler - // and this case occurs rarely. So, we can ignore the edge case bug. - NS_WARNING("Resetting stored modifier key information"); - mModifierKeys.Clear(); - modifierKey = nullptr; - } - if (!modifierKey) { - mModifierKeys.AppendElement(ModifierKey(diff, keyCode)); - } - } - break; - } - - // Currently we don't support Fn key since other browsers don't dispatch - // events for it and we don't have keyCode for this key. - // It should be supported when we implement .key and .char. - case kVK_Function: - break; - - // If the event is caused by something else than pressing or releasing a - // single modifier key (for example by the app having been deactivated - // using command-tab), use the modifiers themselves to determine which - // key's event to dispatch, and whether it's a keyup or keydown event. - // In all cases we assume one or more modifiers are being deactivated - // (never activated) -- otherwise we'd have received one or more events - // corresponding to a single modifier key being pressed. - default: { - NSUInteger modifiers = sLastModifierState; - for (int32_t bit = 0; bit < 32; ++bit) { - NSUInteger flag = 1 << bit; - if (!(diff & flag)) { - continue; - } - - // Given correct information from the application, a flag change here - // will normally be a deactivation (except for some lockable modifiers - // such as CapsLock). But some applications (like VNC) can send an - // activating event with a zero keyCode. So we need to check for that - // here. - bool dispatchKeyDown = ((flag & [aNativeEvent modifierFlags]) != 0); - - unsigned short keyCode = 0; - if (flag & NSDeviceIndependentModifierFlagsMask) { - switch (flag) { - case NSAlphaShiftKeyMask: - keyCode = kVK_CapsLock; - dispatchKeyDown = true; - break; - - case NSNumericPadKeyMask: - // NSNumericPadKeyMask is fired by VNC a lot. But not all of - // these events can really be Clear key events, so we just ignore - // them. - continue; - - case NSHelpKeyMask: - keyCode = kVK_Help; - break; - - case NSFunctionKeyMask: - // An NSFunctionKeyMask change here will normally be a - // deactivation. But sometimes it will be an activation send (by - // VNC for example) with a zero keyCode. - continue; - - // These cases (NSShiftKeyMask, NSControlKeyMask, NSAlternateKeyMask - // and NSCommandKeyMask) should be handled by the other branch of - // the if statement, below (which handles device dependent flags). - // However, some applications (like VNC) can send key events without - // any device dependent flags, so we handle them here instead. - case NSShiftKeyMask: - keyCode = (modifiers & 0x0004) ? kVK_RightShift : kVK_Shift; - break; - case NSControlKeyMask: - keyCode = (modifiers & 0x2000) ? kVK_RightControl : kVK_Control; - break; - case NSAlternateKeyMask: - keyCode = (modifiers & 0x0040) ? kVK_RightOption : kVK_Option; - break; - case NSCommandKeyMask: - keyCode = (modifiers & 0x0010) ? kVK_RightCommand : kVK_Command; - break; - - default: - continue; - } - } else { - const ModifierKey* modifierKey = - GetModifierKeyForDeviceDependentFlags(flag); - if (!modifierKey) { - // See the note above (in the other branch of the if statement) - // about the NSShiftKeyMask, NSControlKeyMask, NSAlternateKeyMask - // and NSCommandKeyMask cases. - continue; - } - keyCode = modifierKey->keyCode; - } - - // Remove flags - modifiers &= ~flag; - switch (keyCode) { - case kVK_Shift: { - const ModifierKey* modifierKey = - GetModifierKeyForNativeKeyCode(kVK_RightShift); - if (!modifierKey || - !(modifiers & modifierKey->GetDeviceDependentFlags())) { - modifiers &= ~NSShiftKeyMask; - } - break; - } - case kVK_RightShift: { - const ModifierKey* modifierKey = - GetModifierKeyForNativeKeyCode(kVK_Shift); - if (!modifierKey || - !(modifiers & modifierKey->GetDeviceDependentFlags())) { - modifiers &= ~NSShiftKeyMask; - } - break; - } - case kVK_Command: { - const ModifierKey* modifierKey = - GetModifierKeyForNativeKeyCode(kVK_RightCommand); - if (!modifierKey || - !(modifiers & modifierKey->GetDeviceDependentFlags())) { - modifiers &= ~NSCommandKeyMask; - } - break; - } - case kVK_RightCommand: { - const ModifierKey* modifierKey = - GetModifierKeyForNativeKeyCode(kVK_Command); - if (!modifierKey || - !(modifiers & modifierKey->GetDeviceDependentFlags())) { - modifiers &= ~NSCommandKeyMask; - } - break; - } - case kVK_Control: { - const ModifierKey* modifierKey = - GetModifierKeyForNativeKeyCode(kVK_RightControl); - if (!modifierKey || - !(modifiers & modifierKey->GetDeviceDependentFlags())) { - modifiers &= ~NSControlKeyMask; - } - break; - } - case kVK_RightControl: { - const ModifierKey* modifierKey = - GetModifierKeyForNativeKeyCode(kVK_Control); - if (!modifierKey || - !(modifiers & modifierKey->GetDeviceDependentFlags())) { - modifiers &= ~NSControlKeyMask; - } - break; - } - case kVK_Option: { - const ModifierKey* modifierKey = - GetModifierKeyForNativeKeyCode(kVK_RightOption); - if (!modifierKey || - !(modifiers & modifierKey->GetDeviceDependentFlags())) { - modifiers &= ~NSAlternateKeyMask; - } - break; - } - case kVK_RightOption: { - const ModifierKey* modifierKey = - GetModifierKeyForNativeKeyCode(kVK_Option); - if (!modifierKey || - !(modifiers & modifierKey->GetDeviceDependentFlags())) { - modifiers &= ~NSAlternateKeyMask; - } - break; - } - case kVK_Help: - modifiers &= ~NSHelpKeyMask; - break; - default: - break; - } - - NSEvent* event = - [NSEvent keyEventWithType:NSFlagsChanged - location:[aNativeEvent locationInWindow] - modifierFlags:modifiers - timestamp:[aNativeEvent timestamp] - windowNumber:[aNativeEvent windowNumber] - context:[aNativeEvent context] - characters:@"" - charactersIgnoringModifiers:@"" - isARepeat:NO - keyCode:keyCode]; - DispatchKeyEventForFlagsChanged(event, dispatchKeyDown); - if (Destroyed()) { - break; - } - - // Stop if focus has changed. - // Check to see if mView is still the first responder. - if (![mView isFirstResponder]) { - break; - } - - } - break; - } - } - - // Be aware, the widget may have been destroyed. - sLastModifierState = [aNativeEvent modifierFlags]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -const TextInputHandler::ModifierKey* -TextInputHandler::GetModifierKeyForNativeKeyCode(unsigned short aKeyCode) const -{ - for (ModifierKeyArray::index_type i = 0; i < mModifierKeys.Length(); ++i) { - if (mModifierKeys[i].keyCode == aKeyCode) { - return &((ModifierKey&)mModifierKeys[i]); - } - } - return nullptr; -} - -const TextInputHandler::ModifierKey* -TextInputHandler::GetModifierKeyForDeviceDependentFlags(NSUInteger aFlags) const -{ - for (ModifierKeyArray::index_type i = 0; i < mModifierKeys.Length(); ++i) { - if (mModifierKeys[i].GetDeviceDependentFlags() == - (aFlags & ~NSDeviceIndependentModifierFlagsMask)) { - return &((ModifierKey&)mModifierKeys[i]); - } - } - return nullptr; -} - -void -TextInputHandler::DispatchKeyEventForFlagsChanged(NSEvent* aNativeEvent, - bool aDispatchKeyDown) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (Destroyed()) { - return; - } - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::DispatchKeyEventForFlagsChanged, aNativeEvent=%p, " - "type=%s, keyCode=%s (0x%X), aDispatchKeyDown=%s, IsIMEComposing()=%s", - this, aNativeEvent, GetNativeKeyEventType(aNativeEvent), - GetKeyNameForNativeKeyCode([aNativeEvent keyCode]), [aNativeEvent keyCode], - TrueOrFalse(aDispatchKeyDown), TrueOrFalse(IsIMEComposing()))); - - if ([aNativeEvent type] != NSFlagsChanged || IsIMEComposing()) { - return; - } - - nsresult rv = mDispatcher->BeginNativeInputTransaction(); - if (NS_WARN_IF(NS_FAILED(rv))) { - MOZ_LOG(gLog, LogLevel::Error, - ("%p IMEInputHandler::DispatchKeyEventForFlagsChanged, " - "FAILED, due to BeginNativeInputTransaction() failure", this)); - return; - } - - EventMessage message = aDispatchKeyDown ? eKeyDown : eKeyUp; - - // Fire a key event. - WidgetKeyboardEvent keyEvent(true, message, mWidget); - InitKeyEvent(aNativeEvent, keyEvent); - - // Attach a plugin event, in case keyEvent gets dispatched to a plugin. Only - // one field is needed -- the type. The other fields can be constructed as - // the need arises. But Gecko doesn't have anything equivalent to the - // NPCocoaEventFlagsChanged type, and this needs to be passed accurately to - // any plugin to which this event is sent. - NPCocoaEvent cocoaEvent; - nsCocoaUtils::InitNPCocoaEvent(&cocoaEvent); - cocoaEvent.type = NPCocoaEventFlagsChanged; - keyEvent.mPluginEvent.Copy(cocoaEvent); - - KeyEventState currentKeyEvent(aNativeEvent); - nsEventStatus status = nsEventStatus_eIgnore; - mDispatcher->DispatchKeyboardEvent(message, keyEvent, status, - ¤tKeyEvent); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -TextInputHandler::InsertText(NSAttributedString* aAttrString, - NSRange* aReplacementRange) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (Destroyed()) { - return; - } - - KeyEventState* currentKeyEvent = GetCurrentKeyEvent(); - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::InsertText, aAttrString=\"%s\", " - "aReplacementRange=%p { location=%llu, length=%llu }, " - "IsIMEComposing()=%s, IgnoreIMEComposition()=%s, " - "keyevent=%p, keydownHandled=%s, keypressDispatched=%s, " - "causedOtherKeyEvents=%s, compositionDispatched=%s", - this, GetCharacters([aAttrString string]), aReplacementRange, - aReplacementRange ? aReplacementRange->location : 0, - aReplacementRange ? aReplacementRange->length : 0, - TrueOrFalse(IsIMEComposing()), TrueOrFalse(IgnoreIMEComposition()), - currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr, - currentKeyEvent ? - TrueOrFalse(currentKeyEvent->mKeyDownHandled) : "N/A", - currentKeyEvent ? - TrueOrFalse(currentKeyEvent->mKeyPressDispatched) : "N/A", - currentKeyEvent ? - TrueOrFalse(currentKeyEvent->mCausedOtherKeyEvents) : "N/A", - currentKeyEvent ? - TrueOrFalse(currentKeyEvent->mCompositionDispatched) : "N/A")); - - if (IgnoreIMEComposition()) { - return; - } - - InputContext context = mWidget->GetInputContext(); - bool isEditable = (context.mIMEState.mEnabled == IMEState::ENABLED || - context.mIMEState.mEnabled == IMEState::PASSWORD); - NSRange selectedRange = SelectedRange(); - - nsAutoString str; - nsCocoaUtils::GetStringForNSString([aAttrString string], str); - - AutoInsertStringClearer clearer(currentKeyEvent); - if (currentKeyEvent) { - currentKeyEvent->mInsertString = &str; - } - - if (!IsIMEComposing() && str.IsEmpty()) { - // nothing to do if there is no content which can be removed. - if (!isEditable) { - return; - } - // If replacement range is specified, we need to remove the range. - // Otherwise, we need to remove the selected range if it's not collapsed. - if (aReplacementRange && aReplacementRange->location != NSNotFound) { - // nothing to do since the range is collapsed. - if (aReplacementRange->length == 0) { - return; - } - // If the replacement range is different from current selected range, - // select the range. - if (!NSEqualRanges(selectedRange, *aReplacementRange)) { - NS_ENSURE_TRUE_VOID(SetSelection(*aReplacementRange)); - } - selectedRange = SelectedRange(); - } - NS_ENSURE_TRUE_VOID(selectedRange.location != NSNotFound); - if (selectedRange.length == 0) { - return; // nothing to do - } - // If this is caused by a key input, the keypress event which will be - // dispatched later should cause the delete. Therefore, nothing to do here. - // Although, we're not sure if such case is actually possible. - if (!currentKeyEvent) { - return; - } - // Delete the selected range. - RefPtr<TextInputHandler> kungFuDeathGrip(this); - WidgetContentCommandEvent deleteCommandEvent(true, eContentCommandDelete, - mWidget); - DispatchEvent(deleteCommandEvent); - NS_ENSURE_TRUE_VOID(deleteCommandEvent.mSucceeded); - // Be aware! The widget might be destroyed here. - return; - } - - bool isReplacingSpecifiedRange = - isEditable && aReplacementRange && - aReplacementRange->location != NSNotFound && - !NSEqualRanges(selectedRange, *aReplacementRange); - - // If this is not caused by pressing a key, there is a composition or - // replacing a range which is different from current selection, let's - // insert the text as committing a composition. - // If InsertText() is called two or more times, we should insert all - // text with composition events. - // XXX When InsertText() is called multiple times, Chromium dispatches - // only one composition event. So, we need to store InsertText() - // calls and flush later. - if (!currentKeyEvent || currentKeyEvent->mCompositionDispatched || - IsIMEComposing() || isReplacingSpecifiedRange) { - InsertTextAsCommittingComposition(aAttrString, aReplacementRange); - if (currentKeyEvent) { - currentKeyEvent->mCompositionDispatched = true; - } - return; - } - - // Don't let the same event be fired twice when hitting - // enter/return for Bug 420502. However, Korean IME (or some other - // simple IME) may work without marked text. For example, composing - // character may be inserted as committed text and it's modified with - // aReplacementRange. When a keydown starts new composition with - // committing previous character, InsertText() may be called twice, - // one is for committing previous character and then, inserting new - // composing character as committed character. In the latter case, - // |CanDispatchKeyPressEvent()| returns true but we need to dispatch - // keypress event for the new character. So, when IME tries to insert - // printable characters, we should ignore current key event state even - // after the keydown has already caused dispatching composition event. - // XXX Anyway, we should sort out around this at fixing bug 1338460. - if (currentKeyEvent && !currentKeyEvent->CanDispatchKeyPressEvent() && - (str.IsEmpty() || (str.Length() == 1 && !IsPrintableChar(str[0])))) { - return; - } - - // XXX Shouldn't we hold mDispatcher instead of mWidget? - RefPtr<nsChildView> widget(mWidget); - nsresult rv = mDispatcher->BeginNativeInputTransaction(); - if (NS_WARN_IF(NS_FAILED(rv))) { - MOZ_LOG(gLog, LogLevel::Error, - ("%p IMEInputHandler::HandleKeyUpEvent, " - "FAILED, due to BeginNativeInputTransaction() failure", this)); - return; - } - - // Dispatch keypress event with char instead of compositionchange event - WidgetKeyboardEvent keypressEvent(true, eKeyPress, widget); - // XXX Why do we need to dispatch keypress event for not inputting any - // string? If it wants to delete the specified range, should we - // dispatch an eContentCommandDelete event instead? Because this - // must not be caused by a key operation, a part of IME's processing. - keypressEvent.mIsChar = IsPrintableChar(str.CharAt(0)); - - // Don't set other modifiers from the current event, because here in - // -insertText: they've already been taken into account in creating - // the input string. - - if (currentKeyEvent) { - currentKeyEvent->InitKeyEvent(this, keypressEvent); - } else { - nsCocoaUtils::InitInputEvent(keypressEvent, static_cast<NSEvent*>(nullptr)); - keypressEvent.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING; - keypressEvent.mKeyValue = str; - // FYI: TextEventDispatcher will set mKeyCode to 0 for printable key's - // keypress events even if they don't cause inputting non-empty string. - } - - // Remove basic modifiers from keypress event because if they are included, - // nsPlaintextEditor ignores the event. - keypressEvent.mModifiers &= ~(MODIFIER_CONTROL | - MODIFIER_ALT | - MODIFIER_META); - - // TODO: - // If mCurrentKeyEvent.mKeyEvent is null, the text should be inputted as - // composition events. - nsEventStatus status = nsEventStatus_eIgnore; - bool keyPressDispatched = - mDispatcher->MaybeDispatchKeypressEvents(keypressEvent, status, - currentKeyEvent); - bool keyPressHandled = (status == nsEventStatus_eConsumeNoDefault); - - // Note: mWidget might have become null here. Don't count on it from here on. - - if (currentKeyEvent) { - currentKeyEvent->mKeyPressHandled = keyPressHandled; - currentKeyEvent->mKeyPressDispatched = keyPressDispatched; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -bool -TextInputHandler::DoCommandBySelector(const char* aSelector) -{ - RefPtr<nsChildView> widget(mWidget); - - KeyEventState* currentKeyEvent = GetCurrentKeyEvent(); - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::DoCommandBySelector, aSelector=\"%s\", " - "Destroyed()=%s, keydownHandled=%s, keypressHandled=%s, " - "causedOtherKeyEvents=%s", - this, aSelector ? aSelector : "", TrueOrFalse(Destroyed()), - currentKeyEvent ? - TrueOrFalse(currentKeyEvent->mKeyDownHandled) : "N/A", - currentKeyEvent ? - TrueOrFalse(currentKeyEvent->mKeyPressHandled) : "N/A", - currentKeyEvent ? - TrueOrFalse(currentKeyEvent->mCausedOtherKeyEvents) : "N/A")); - - if (currentKeyEvent && currentKeyEvent->CanDispatchKeyPressEvent()) { - nsresult rv = mDispatcher->BeginNativeInputTransaction(); - if (NS_WARN_IF(NS_FAILED(rv))) { - MOZ_LOG(gLog, LogLevel::Error, - ("%p IMEInputHandler::DoCommandBySelector, " - "FAILED, due to BeginNativeInputTransaction() failure " - "at dispatching keypress", this)); - return false; - } - - WidgetKeyboardEvent keypressEvent(true, eKeyPress, widget); - currentKeyEvent->InitKeyEvent(this, keypressEvent); - - nsEventStatus status = nsEventStatus_eIgnore; - currentKeyEvent->mKeyPressDispatched = - mDispatcher->MaybeDispatchKeypressEvents(keypressEvent, status, - currentKeyEvent); - currentKeyEvent->mKeyPressHandled = - (status == nsEventStatus_eConsumeNoDefault); - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandler::DoCommandBySelector, keypress event " - "dispatched, Destroyed()=%s, keypressHandled=%s", - this, TrueOrFalse(Destroyed()), - TrueOrFalse(currentKeyEvent->mKeyPressHandled))); - } - - return (!Destroyed() && currentKeyEvent && - currentKeyEvent->IsDefaultPrevented()); -} - - -#pragma mark - - - -/****************************************************************************** - * - * IMEInputHandler implementation (static methods) - * - ******************************************************************************/ - -bool IMEInputHandler::sStaticMembersInitialized = false; -bool IMEInputHandler::sCachedIsForRTLLangage = false; -CFStringRef IMEInputHandler::sLatestIMEOpenedModeInputSourceID = nullptr; -IMEInputHandler* IMEInputHandler::sFocusedIMEHandler = nullptr; - -// static -void -IMEInputHandler::InitStaticMembers() -{ - if (sStaticMembersInitialized) - return; - sStaticMembersInitialized = true; - // We need to check the keyboard layout changes on all applications. - CFNotificationCenterRef center = ::CFNotificationCenterGetDistributedCenter(); - // XXX Don't we need to remove the observer at shut down? - // Mac Dev Center's document doesn't say how to remove the observer if - // the second parameter is NULL. - ::CFNotificationCenterAddObserver(center, NULL, - OnCurrentTextInputSourceChange, - kTISNotifySelectedKeyboardInputSourceChanged, NULL, - CFNotificationSuspensionBehaviorDeliverImmediately); - // Initiailize with the current keyboard layout - OnCurrentTextInputSourceChange(NULL, NULL, - kTISNotifySelectedKeyboardInputSourceChanged, - NULL, NULL); -} - -// static -void -IMEInputHandler::OnCurrentTextInputSourceChange(CFNotificationCenterRef aCenter, - void* aObserver, - CFStringRef aName, - const void* aObject, - CFDictionaryRef aUserInfo) -{ - // Cache the latest IME opened mode to sLatestIMEOpenedModeInputSourceID. - TISInputSourceWrapper tis; - tis.InitByCurrentInputSource(); - if (tis.IsOpenedIMEMode()) { - tis.GetInputSourceID(sLatestIMEOpenedModeInputSourceID); - } - - if (MOZ_LOG_TEST(gLog, LogLevel::Info)) { - static CFStringRef sLastTIS = nullptr; - CFStringRef newTIS; - tis.GetInputSourceID(newTIS); - if (!sLastTIS || - ::CFStringCompare(sLastTIS, newTIS, 0) != kCFCompareEqualTo) { - TISInputSourceWrapper tis1, tis2, tis3, tis4, tis5; - tis1.InitByCurrentKeyboardLayout(); - tis2.InitByCurrentASCIICapableInputSource(); - tis3.InitByCurrentASCIICapableKeyboardLayout(); - tis4.InitByCurrentInputMethodKeyboardLayoutOverride(); - tis5.InitByTISInputSourceRef(tis.GetKeyboardLayoutInputSource()); - CFStringRef is0 = nullptr, is1 = nullptr, is2 = nullptr, is3 = nullptr, - is4 = nullptr, is5 = nullptr, type0 = nullptr, - lang0 = nullptr, bundleID0 = nullptr; - tis.GetInputSourceID(is0); - tis1.GetInputSourceID(is1); - tis2.GetInputSourceID(is2); - tis3.GetInputSourceID(is3); - tis4.GetInputSourceID(is4); - tis5.GetInputSourceID(is5); - tis.GetInputSourceType(type0); - tis.GetPrimaryLanguage(lang0); - tis.GetBundleID(bundleID0); - - MOZ_LOG(gLog, LogLevel::Info, - ("IMEInputHandler::OnCurrentTextInputSourceChange,\n" - " Current Input Source is changed to:\n" - " currentInputContext=%p\n" - " %s\n" - " type=%s %s\n" - " overridden keyboard layout=%s\n" - " used keyboard layout for translation=%s\n" - " primary language=%s\n" - " bundle ID=%s\n" - " current ASCII capable Input Source=%s\n" - " current Keyboard Layout=%s\n" - " current ASCII capable Keyboard Layout=%s", - [NSTextInputContext currentInputContext], GetCharacters(is0), - GetCharacters(type0), tis.IsASCIICapable() ? "- ASCII capable " : "", - GetCharacters(is4), GetCharacters(is5), - GetCharacters(lang0), GetCharacters(bundleID0), - GetCharacters(is2), GetCharacters(is1), GetCharacters(is3))); - } - sLastTIS = newTIS; - } - - /** - * When the direction is changed, all the children are notified. - * No need to treat the initial case separately because it is covered - * by the general case (sCachedIsForRTLLangage is initially false) - */ - if (sCachedIsForRTLLangage != tis.IsForRTLLanguage()) { - WidgetUtils::SendBidiKeyboardInfoToContent(); - sCachedIsForRTLLangage = tis.IsForRTLLanguage(); - } -} - -// static -void -IMEInputHandler::FlushPendingMethods(nsITimer* aTimer, void* aClosure) -{ - NS_ASSERTION(aClosure, "aClosure is null"); - static_cast<IMEInputHandler*>(aClosure)->ExecutePendingMethods(); -} - -// static -CFArrayRef -IMEInputHandler::CreateAllIMEModeList() -{ - const void* keys[] = { kTISPropertyInputSourceType }; - const void* values[] = { kTISTypeKeyboardInputMode }; - CFDictionaryRef filter = - ::CFDictionaryCreate(kCFAllocatorDefault, keys, values, 1, NULL, NULL); - NS_ASSERTION(filter, "failed to create the filter"); - CFArrayRef list = ::TISCreateInputSourceList(filter, true); - ::CFRelease(filter); - return list; -} - -// static -void -IMEInputHandler::DebugPrintAllIMEModes() -{ - if (MOZ_LOG_TEST(gLog, LogLevel::Info)) { - CFArrayRef list = CreateAllIMEModeList(); - MOZ_LOG(gLog, LogLevel::Info, ("IME mode configuration:")); - CFIndex idx = ::CFArrayGetCount(list); - TISInputSourceWrapper tis; - for (CFIndex i = 0; i < idx; ++i) { - TISInputSourceRef inputSource = static_cast<TISInputSourceRef>( - const_cast<void *>(::CFArrayGetValueAtIndex(list, i))); - tis.InitByTISInputSourceRef(inputSource); - nsAutoString name, isid; - tis.GetLocalizedName(name); - tis.GetInputSourceID(isid); - MOZ_LOG(gLog, LogLevel::Info, - (" %s\t<%s>%s%s\n", - NS_ConvertUTF16toUTF8(name).get(), - NS_ConvertUTF16toUTF8(isid).get(), - tis.IsASCIICapable() ? "" : "\t(Isn't ASCII capable)", - tis.IsEnabled() ? "" : "\t(Isn't Enabled)")); - } - ::CFRelease(list); - } -} - -//static -TSMDocumentID -IMEInputHandler::GetCurrentTSMDocumentID() -{ - // At least on Mac OS X 10.6.x and 10.7.x, ::TSMGetActiveDocument() has a bug. - // The result of ::TSMGetActiveDocument() isn't modified for new active text - // input context until [NSTextInputContext currentInputContext] is called. - // Therefore, we need to call it here. - [NSTextInputContext currentInputContext]; - return ::TSMGetActiveDocument(); -} - - -#pragma mark - - - -/****************************************************************************** - * - * IMEInputHandler implementation #1 - * The methods are releated to the pending methods. Some jobs should be - * run after the stack is finished, e.g, some methods cannot run the jobs - * during processing the focus event. And also some other jobs should be - * run at the next focus event is processed. - * The pending methods are recorded in mPendingMethods. They are executed - * by ExecutePendingMethods via FlushPendingMethods. - * - ******************************************************************************/ - -NS_IMETHODIMP -IMEInputHandler::NotifyIME(TextEventDispatcher* aTextEventDispatcher, - const IMENotification& aNotification) -{ - switch (aNotification.mMessage) { - case REQUEST_TO_COMMIT_COMPOSITION: - CommitIMEComposition(); - return NS_OK; - case REQUEST_TO_CANCEL_COMPOSITION: - CancelIMEComposition(); - return NS_OK; - case NOTIFY_IME_OF_FOCUS: - if (IsFocused()) { - nsIWidget* widget = aTextEventDispatcher->GetWidget(); - if (widget && widget->GetInputContext().IsPasswordEditor()) { - EnableSecureEventInput(); - } else { - EnsureSecureEventInputDisabled(); - } - } - OnFocusChangeInGecko(true); - return NS_OK; - case NOTIFY_IME_OF_BLUR: - OnFocusChangeInGecko(false); - return NS_OK; - case NOTIFY_IME_OF_SELECTION_CHANGE: - OnSelectionChange(aNotification); - return NS_OK; - default: - return NS_ERROR_NOT_IMPLEMENTED; - } -} - -NS_IMETHODIMP_(void) -IMEInputHandler::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) -{ - // XXX When input transaction is being stolen by add-on, what should we do? -} - -NS_IMETHODIMP_(void) -IMEInputHandler::WillDispatchKeyboardEvent( - TextEventDispatcher* aTextEventDispatcher, - WidgetKeyboardEvent& aKeyboardEvent, - uint32_t aIndexOfKeypress, - void* aData) -{ - // If the keyboard event is not caused by a native key event, we can do - // nothing here. - if (!aData) { - return; - } - - KeyEventState* currentKeyEvent = static_cast<KeyEventState*>(aData); - NSEvent* nativeEvent = currentKeyEvent->mKeyEvent; - nsAString* insertString = currentKeyEvent->mInsertString; - if (KeyboardLayoutOverrideRef().mOverrideEnabled) { - TISInputSourceWrapper tis; - tis.InitByLayoutID(KeyboardLayoutOverrideRef().mKeyboardLayout, true); - tis.WillDispatchKeyboardEvent(nativeEvent, insertString, aKeyboardEvent); - return; - } - TISInputSourceWrapper::CurrentInputSource(). - WillDispatchKeyboardEvent(nativeEvent, insertString, aKeyboardEvent); -} - -void -IMEInputHandler::NotifyIMEOfFocusChangeInGecko() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::NotifyIMEOfFocusChangeInGecko, " - "Destroyed()=%s, IsFocused()=%s, inputContext=%p", - this, TrueOrFalse(Destroyed()), TrueOrFalse(IsFocused()), - mView ? [mView inputContext] : nullptr)); - - if (Destroyed()) { - return; - } - - if (!IsFocused()) { - // retry at next focus event - mPendingMethods |= kNotifyIMEOfFocusChangeInGecko; - return; - } - - MOZ_ASSERT(mView); - NSTextInputContext* inputContext = [mView inputContext]; - NS_ENSURE_TRUE_VOID(inputContext); - - // When an <input> element on a XUL <panel> element gets focus from an <input> - // element on the opener window of the <panel> element, the owner window - // still has native focus. Therefore, IMEs may store the opener window's - // level at this time because they don't know the actual focus is moved to - // different window. If IMEs try to get the newest window level after the - // focus change, we return the window level of the XUL <panel>'s widget. - // Therefore, let's emulate the native focus change. Then, IMEs can refresh - // the stored window level. - [inputContext deactivate]; - [inputContext activate]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -IMEInputHandler::DiscardIMEComposition() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::DiscardIMEComposition, " - "Destroyed()=%s, IsFocused()=%s, mView=%p, inputContext=%p", - this, TrueOrFalse(Destroyed()), TrueOrFalse(IsFocused()), - mView, mView ? [mView inputContext] : nullptr)); - - if (Destroyed()) { - return; - } - - if (!IsFocused()) { - // retry at next focus event - mPendingMethods |= kDiscardIMEComposition; - return; - } - - NS_ENSURE_TRUE_VOID(mView); - NSTextInputContext* inputContext = [mView inputContext]; - NS_ENSURE_TRUE_VOID(inputContext); - mIgnoreIMECommit = true; - [inputContext discardMarkedText]; - mIgnoreIMECommit = false; - - NS_OBJC_END_TRY_ABORT_BLOCK -} - -void -IMEInputHandler::SyncASCIICapableOnly() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::SyncASCIICapableOnly, " - "Destroyed()=%s, IsFocused()=%s, mIsASCIICapableOnly=%s, " - "GetCurrentTSMDocumentID()=%p", - this, TrueOrFalse(Destroyed()), TrueOrFalse(IsFocused()), - TrueOrFalse(mIsASCIICapableOnly), GetCurrentTSMDocumentID())); - - if (Destroyed()) { - return; - } - - if (!IsFocused()) { - // retry at next focus event - mPendingMethods |= kSyncASCIICapableOnly; - return; - } - - TSMDocumentID doc = GetCurrentTSMDocumentID(); - if (!doc) { - // retry - mPendingMethods |= kSyncASCIICapableOnly; - NS_WARNING("Application is active but there is no active document"); - ResetTimer(); - return; - } - - if (mIsASCIICapableOnly) { - CFArrayRef ASCIICapableTISList = ::TISCreateASCIICapableInputSourceList(); - ::TSMSetDocumentProperty(doc, - kTSMDocumentEnabledInputSourcesPropertyTag, - sizeof(CFArrayRef), - &ASCIICapableTISList); - ::CFRelease(ASCIICapableTISList); - } else { - ::TSMRemoveDocumentProperty(doc, - kTSMDocumentEnabledInputSourcesPropertyTag); - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -IMEInputHandler::ResetTimer() -{ - NS_ASSERTION(mPendingMethods != 0, - "There are not pending methods, why this is called?"); - if (mTimer) { - mTimer->Cancel(); - } else { - mTimer = do_CreateInstance(NS_TIMER_CONTRACTID); - NS_ENSURE_TRUE(mTimer, ); - } - mTimer->InitWithFuncCallback(FlushPendingMethods, this, 0, - nsITimer::TYPE_ONE_SHOT); -} - -void -IMEInputHandler::ExecutePendingMethods() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (mTimer) { - mTimer->Cancel(); - mTimer = nullptr; - } - - if (![[NSApplication sharedApplication] isActive]) { - mIsInFocusProcessing = false; - // If we're not active, we should retry at focus event - return; - } - - uint32_t pendingMethods = mPendingMethods; - // First, reset the pending method flags because if each methods cannot - // run now, they can reentry to the pending flags by theirselves. - mPendingMethods = 0; - - if (pendingMethods & kDiscardIMEComposition) - DiscardIMEComposition(); - if (pendingMethods & kSyncASCIICapableOnly) - SyncASCIICapableOnly(); - if (pendingMethods & kNotifyIMEOfFocusChangeInGecko) { - NotifyIMEOfFocusChangeInGecko(); - } - - mIsInFocusProcessing = false; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -#pragma mark - - - -/****************************************************************************** - * - * IMEInputHandler implementation (native event handlers) - * - ******************************************************************************/ - -TextRangeType -IMEInputHandler::ConvertToTextRangeType(uint32_t aUnderlineStyle, - NSRange& aSelectedRange) -{ - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::ConvertToTextRangeType, " - "aUnderlineStyle=%llu, aSelectedRange.length=%llu,", - this, aUnderlineStyle, aSelectedRange.length)); - - // We assume that aUnderlineStyle is NSUnderlineStyleSingle or - // NSUnderlineStyleThick. NSUnderlineStyleThick should indicate a selected - // clause. Otherwise, should indicate non-selected clause. - - if (aSelectedRange.length == 0) { - switch (aUnderlineStyle) { - case NSUnderlineStyleSingle: - return TextRangeType::eRawClause; - case NSUnderlineStyleThick: - return TextRangeType::eSelectedRawClause; - default: - NS_WARNING("Unexpected line style"); - return TextRangeType::eSelectedRawClause; - } - } - - switch (aUnderlineStyle) { - case NSUnderlineStyleSingle: - return TextRangeType::eConvertedClause; - case NSUnderlineStyleThick: - return TextRangeType::eSelectedClause; - default: - NS_WARNING("Unexpected line style"); - return TextRangeType::eSelectedClause; - } -} - -uint32_t -IMEInputHandler::GetRangeCount(NSAttributedString *aAttrString) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - // Iterate through aAttrString for the NSUnderlineStyleAttributeName and - // count the different segments adjusting limitRange as we go. - uint32_t count = 0; - NSRange effectiveRange; - NSRange limitRange = NSMakeRange(0, [aAttrString length]); - while (limitRange.length > 0) { - [aAttrString attribute:NSUnderlineStyleAttributeName - atIndex:limitRange.location - longestEffectiveRange:&effectiveRange - inRange:limitRange]; - limitRange = - NSMakeRange(NSMaxRange(effectiveRange), - NSMaxRange(limitRange) - NSMaxRange(effectiveRange)); - count++; - } - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::GetRangeCount, aAttrString=\"%s\", count=%llu", - this, GetCharacters([aAttrString string]), count)); - - return count; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); -} - -already_AddRefed<mozilla::TextRangeArray> -IMEInputHandler::CreateTextRangeArray(NSAttributedString *aAttrString, - NSRange& aSelectedRange) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - RefPtr<mozilla::TextRangeArray> textRangeArray = - new mozilla::TextRangeArray(); - - // Note that we shouldn't append ranges when composition string - // is empty because it may cause TextComposition confused. - if (![aAttrString length]) { - return textRangeArray.forget(); - } - - // Convert the Cocoa range into the TextRange Array used in Gecko. - // Iterate through the attributed string and map the underline attribute to - // Gecko IME textrange attributes. We may need to change the code here if - // we change the implementation of validAttributesForMarkedText. - NSRange limitRange = NSMakeRange(0, [aAttrString length]); - uint32_t rangeCount = GetRangeCount(aAttrString); - for (uint32_t i = 0; i < rangeCount && limitRange.length > 0; i++) { - NSRange effectiveRange; - id attributeValue = [aAttrString attribute:NSUnderlineStyleAttributeName - atIndex:limitRange.location - longestEffectiveRange:&effectiveRange - inRange:limitRange]; - - TextRange range; - range.mStartOffset = effectiveRange.location; - range.mEndOffset = NSMaxRange(effectiveRange); - range.mRangeType = - ConvertToTextRangeType([attributeValue intValue], aSelectedRange); - textRangeArray->AppendElement(range); - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::CreateTextRangeArray, " - "range={ mStartOffset=%llu, mEndOffset=%llu, mRangeType=%s }", - this, range.mStartOffset, range.mEndOffset, - ToChar(range.mRangeType))); - - limitRange = - NSMakeRange(NSMaxRange(effectiveRange), - NSMaxRange(limitRange) - NSMaxRange(effectiveRange)); - } - - // Get current caret position. - TextRange range; - range.mStartOffset = aSelectedRange.location + aSelectedRange.length; - range.mEndOffset = range.mStartOffset; - range.mRangeType = TextRangeType::eCaret; - textRangeArray->AppendElement(range); - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::CreateTextRangeArray, " - "range={ mStartOffset=%llu, mEndOffset=%llu, mRangeType=%s }", - this, range.mStartOffset, range.mEndOffset, - ToChar(range.mRangeType))); - - return textRangeArray.forget(); - - NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL; -} - -bool -IMEInputHandler::DispatchCompositionStartEvent() -{ - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::DispatchCompositionStartEvent, " - "mSelectedRange={ location=%llu, length=%llu }, Destroyed()=%s, " - "mView=%p, mWidget=%p, inputContext=%p, mIsIMEComposing=%s", - this, SelectedRange().location, mSelectedRange.length, - TrueOrFalse(Destroyed()), mView, mWidget, - mView ? [mView inputContext] : nullptr, TrueOrFalse(mIsIMEComposing))); - - RefPtr<IMEInputHandler> kungFuDeathGrip(this); - - nsresult rv = mDispatcher->BeginNativeInputTransaction(); - if (NS_WARN_IF(NS_FAILED(rv))) { - MOZ_LOG(gLog, LogLevel::Error, - ("%p IMEInputHandler::DispatchCompositionStartEvent, " - "FAILED, due to BeginNativeInputTransaction() failure", this)); - return false; - } - - NS_ASSERTION(!mIsIMEComposing, "There is a composition already"); - mIsIMEComposing = true; - - nsEventStatus status; - rv = mDispatcher->StartComposition(status); - if (NS_WARN_IF(NS_FAILED(rv))) { - MOZ_LOG(gLog, LogLevel::Error, - ("%p IMEInputHandler::DispatchCompositionStartEvent, " - "FAILED, due to StartComposition() failure", this)); - return false; - } - - if (Destroyed()) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::DispatchCompositionStartEvent, " - "destroyed by compositionstart event", this)); - return false; - } - - // FYI: compositionstart may cause committing composition by the webapp. - if (!mIsIMEComposing) { - return false; - } - - // FYI: The selection range might have been modified by a compositionstart - // event handler. - mIMECompositionStart = SelectedRange().location; - return true; -} - -bool -IMEInputHandler::DispatchCompositionChangeEvent(const nsString& aText, - NSAttributedString* aAttrString, - NSRange& aSelectedRange) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::DispatchCompositionChangeEvent, " - "aText=\"%s\", aAttrString=\"%s\", " - "aSelectedRange={ location=%llu, length=%llu }, Destroyed()=%s, mView=%p, " - "mWidget=%p, inputContext=%p, mIsIMEComposing=%s", - this, NS_ConvertUTF16toUTF8(aText).get(), - GetCharacters([aAttrString string]), - aSelectedRange.location, aSelectedRange.length, - TrueOrFalse(Destroyed()), mView, mWidget, - mView ? [mView inputContext] : nullptr, TrueOrFalse(mIsIMEComposing))); - - NS_ENSURE_TRUE(!Destroyed(), false); - - NS_ASSERTION(mIsIMEComposing, "We're not in composition"); - - RefPtr<IMEInputHandler> kungFuDeathGrip(this); - - nsresult rv = mDispatcher->BeginNativeInputTransaction(); - if (NS_WARN_IF(NS_FAILED(rv))) { - MOZ_LOG(gLog, LogLevel::Error, - ("%p IMEInputHandler::DispatchCompositionChangeEvent, " - "FAILED, due to BeginNativeInputTransaction() failure", this)); - return false; - } - - RefPtr<TextRangeArray> rangeArray = - CreateTextRangeArray(aAttrString, aSelectedRange); - - rv = mDispatcher->SetPendingComposition(aText, rangeArray); - if (NS_WARN_IF(NS_FAILED(rv))) { - MOZ_LOG(gLog, LogLevel::Error, - ("%p IMEInputHandler::DispatchCompositionChangeEvent, " - "FAILED, due to SetPendingComposition() failure", this)); - return false; - } - - mSelectedRange.location = mIMECompositionStart + aSelectedRange.location; - mSelectedRange.length = aSelectedRange.length; - - if (mIMECompositionString) { - [mIMECompositionString release]; - } - mIMECompositionString = [[aAttrString string] retain]; - - nsEventStatus status; - rv = mDispatcher->FlushPendingComposition(status); - if (NS_WARN_IF(NS_FAILED(rv))) { - MOZ_LOG(gLog, LogLevel::Error, - ("%p IMEInputHandler::DispatchCompositionChangeEvent, " - "FAILED, due to FlushPendingComposition() failure", this)); - return false; - } - - if (Destroyed()) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::DispatchCompositionChangeEvent, " - "destroyed by compositionchange event", this)); - return false; - } - - // FYI: compositionstart may cause committing composition by the webapp. - return mIsIMEComposing; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); -} - -bool -IMEInputHandler::DispatchCompositionCommitEvent(const nsAString* aCommitString) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::DispatchCompositionCommitEvent, " - "aCommitString=0x%p (\"%s\"), Destroyed()=%s, mView=%p, mWidget=%p, " - "inputContext=%p, mIsIMEComposing=%s", - this, aCommitString, - aCommitString ? NS_ConvertUTF16toUTF8(*aCommitString).get() : "", - TrueOrFalse(Destroyed()), mView, mWidget, - mView ? [mView inputContext] : nullptr, TrueOrFalse(mIsIMEComposing))); - - NS_ASSERTION(mIsIMEComposing, "We're not in composition"); - - RefPtr<IMEInputHandler> kungFuDeathGrip(this); - - if (!Destroyed()) { - // IME may query selection immediately after this, however, in e10s mode, - // OnSelectionChange() will be called asynchronously. Until then, we - // should emulate expected selection range if the webapp does nothing. - mSelectedRange.location = mIMECompositionStart; - if (aCommitString) { - mSelectedRange.location += aCommitString->Length(); - } else if (mIMECompositionString) { - nsAutoString commitString; - nsCocoaUtils::GetStringForNSString(mIMECompositionString, commitString); - mSelectedRange.location += commitString.Length(); - } - mSelectedRange.length = 0; - - nsresult rv = mDispatcher->BeginNativeInputTransaction(); - if (NS_WARN_IF(NS_FAILED(rv))) { - MOZ_LOG(gLog, LogLevel::Error, - ("%p IMEInputHandler::DispatchCompositionCommitEvent, " - "FAILED, due to BeginNativeInputTransaction() failure", this)); - } else { - nsEventStatus status; - rv = mDispatcher->CommitComposition(status, aCommitString); - if (NS_WARN_IF(NS_FAILED(rv))) { - MOZ_LOG(gLog, LogLevel::Error, - ("%p IMEInputHandler::DispatchCompositionCommitEvent, " - "FAILED, due to BeginNativeInputTransaction() failure", this)); - } - } - } - - mIsIMEComposing = false; - mIMECompositionStart = UINT32_MAX; - if (mIMECompositionString) { - [mIMECompositionString release]; - mIMECompositionString = nullptr; - } - - if (Destroyed()) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::DispatchCompositionCommitEvent, " - "destroyed by compositioncommit event", this)); - return false; - } - - return true; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); -} - -void -IMEInputHandler::InsertTextAsCommittingComposition( - NSAttributedString* aAttrString, - NSRange* aReplacementRange) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::InsertTextAsCommittingComposition, " - "aAttrString=\"%s\", aReplacementRange=%p { location=%llu, length=%llu }, " - "Destroyed()=%s, IsIMEComposing()=%s, " - "mMarkedRange={ location=%llu, length=%llu }", - this, GetCharacters([aAttrString string]), aReplacementRange, - aReplacementRange ? aReplacementRange->location : 0, - aReplacementRange ? aReplacementRange->length : 0, - TrueOrFalse(Destroyed()), TrueOrFalse(IsIMEComposing()), - mMarkedRange.location, mMarkedRange.length)); - - if (IgnoreIMECommit()) { - MOZ_CRASH("IMEInputHandler::InsertTextAsCommittingComposition() must not" - "be called while canceling the composition"); - } - - if (Destroyed()) { - return; - } - - // First, commit current composition with the latest composition string if the - // replacement range is different from marked range. - if (IsIMEComposing() && aReplacementRange && - aReplacementRange->location != NSNotFound && - !NSEqualRanges(MarkedRange(), *aReplacementRange)) { - if (!DispatchCompositionCommitEvent()) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::InsertTextAsCommittingComposition, " - "destroyed by commiting composition for setting replacement range", - this)); - return; - } - } - - RefPtr<IMEInputHandler> kungFuDeathGrip(this); - - nsString str; - nsCocoaUtils::GetStringForNSString([aAttrString string], str); - - if (!IsIMEComposing()) { - // If there is no selection and replacement range is specified, set the - // range as selection. - if (aReplacementRange && aReplacementRange->location != NSNotFound && - !NSEqualRanges(SelectedRange(), *aReplacementRange)) { - NS_ENSURE_TRUE_VOID(SetSelection(*aReplacementRange)); - } - - if (!DispatchCompositionStartEvent()) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::InsertTextAsCommittingComposition, " - "cannot continue handling composition after compositionstart", this)); - return; - } - } - - if (!DispatchCompositionCommitEvent(&str)) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::InsertTextAsCommittingComposition, " - "destroyed by compositioncommit event", this)); - return; - } - - mMarkedRange = NSMakeRange(NSNotFound, 0); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -IMEInputHandler::SetMarkedText(NSAttributedString* aAttrString, - NSRange& aSelectedRange, - NSRange* aReplacementRange) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - KeyEventState* currentKeyEvent = GetCurrentKeyEvent(); - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::SetMarkedText, " - "aAttrString=\"%s\", aSelectedRange={ location=%llu, length=%llu }, " - "aReplacementRange=%p { location=%llu, length=%llu }, " - "Destroyed()=%s, IgnoreIMEComposition()=%s, IsIMEComposing()=%s, " - "mMarkedRange={ location=%llu, length=%llu }, keyevent=%p, " - "keydownHandled=%s, keypressDispatched=%s, causedOtherKeyEvents=%s, " - "compositionDispatched=%s", - this, GetCharacters([aAttrString string]), - aSelectedRange.location, aSelectedRange.length, aReplacementRange, - aReplacementRange ? aReplacementRange->location : 0, - aReplacementRange ? aReplacementRange->length : 0, - TrueOrFalse(Destroyed()), TrueOrFalse(IgnoreIMEComposition()), - TrueOrFalse(IsIMEComposing()), - mMarkedRange.location, mMarkedRange.length, - currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr, - currentKeyEvent ? - TrueOrFalse(currentKeyEvent->mKeyDownHandled) : "N/A", - currentKeyEvent ? - TrueOrFalse(currentKeyEvent->mKeyPressDispatched) : "N/A", - currentKeyEvent ? - TrueOrFalse(currentKeyEvent->mCausedOtherKeyEvents) : "N/A", - currentKeyEvent ? - TrueOrFalse(currentKeyEvent->mCompositionDispatched) : "N/A")); - - // If SetMarkedText() is called during handling a key press, that means that - // the key event caused this composition. So, keypress event shouldn't - // be dispatched later, let's mark the key event causing composition event. - if (currentKeyEvent) { - currentKeyEvent->mCompositionDispatched = true; - } - - if (Destroyed() || IgnoreIMEComposition()) { - return; - } - - RefPtr<IMEInputHandler> kungFuDeathGrip(this); - - // First, commit current composition with the latest composition string if the - // replacement range is different from marked range. - if (IsIMEComposing() && aReplacementRange && - aReplacementRange->location != NSNotFound && - !NSEqualRanges(MarkedRange(), *aReplacementRange)) { - AutoRestore<bool> ignoreIMECommit(mIgnoreIMECommit); - mIgnoreIMECommit = false; - if (!DispatchCompositionCommitEvent()) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::SetMarkedText, " - "destroyed by commiting composition for setting replacement range", - this)); - return; - } - } - - nsString str; - nsCocoaUtils::GetStringForNSString([aAttrString string], str); - - mMarkedRange.length = str.Length(); - - if (!IsIMEComposing() && !str.IsEmpty()) { - // If there is no selection and replacement range is specified, set the - // range as selection. - if (aReplacementRange && aReplacementRange->location != NSNotFound && - !NSEqualRanges(SelectedRange(), *aReplacementRange)) { - NS_ENSURE_TRUE_VOID(SetSelection(*aReplacementRange)); - } - - mMarkedRange.location = SelectedRange().location; - - if (!DispatchCompositionStartEvent()) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::SetMarkedText, cannot continue handling " - "composition after dispatching compositionstart", this)); - return; - } - } - - if (!str.IsEmpty()) { - if (!DispatchCompositionChangeEvent(str, aAttrString, aSelectedRange)) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::SetMarkedText, cannot continue handling " - "composition after dispatching compositionchange", this)); - } - return; - } - - // If the composition string becomes empty string, we should commit - // current composition. - if (!DispatchCompositionCommitEvent(&EmptyString())) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::SetMarkedText, " - "destroyed by compositioncommit event", this)); - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -NSAttributedString* -IMEInputHandler::GetAttributedSubstringFromRange(NSRange& aRange, - NSRange* aActualRange) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::GetAttributedSubstringFromRange, " - "aRange={ location=%llu, length=%llu }, aActualRange=%p, Destroyed()=%s", - this, aRange.location, aRange.length, aActualRange, - TrueOrFalse(Destroyed()))); - - if (aActualRange) { - *aActualRange = NSMakeRange(NSNotFound, 0); - } - - if (Destroyed() || aRange.location == NSNotFound || aRange.length == 0) { - return nil; - } - - RefPtr<IMEInputHandler> kungFuDeathGrip(this); - - // If we're in composing, the queried range may be in the composition string. - // In such case, we should use mIMECompositionString since if the composition - // string is handled by a remote process, the content cache may be out of - // date. - // XXX Should we set composition string attributes? Although, Blink claims - // that some attributes of marked text are supported, but they return - // just marked string without any style. So, let's keep current behavior - // at least for now. - NSUInteger compositionLength = - mIMECompositionString ? [mIMECompositionString length] : 0; - if (mIMECompositionStart != UINT32_MAX && - mIMECompositionStart >= aRange.location && - mIMECompositionStart + compositionLength <= - aRange.location + aRange.length) { - NSRange range = - NSMakeRange(aRange.location - mIMECompositionStart, aRange.length); - NSString* nsstr = [mIMECompositionString substringWithRange:range]; - NSMutableAttributedString* result = - [[[NSMutableAttributedString alloc] initWithString:nsstr - attributes:nil] autorelease]; - // XXX We cannot return font information in this case. However, this - // case must occur only when IME tries to confirm if composing string - // is handled as expected. - if (aActualRange) { - *aActualRange = aRange; - } - - if (MOZ_LOG_TEST(gLog, LogLevel::Info)) { - nsAutoString str; - nsCocoaUtils::GetStringForNSString(nsstr, str); - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::GetAttributedSubstringFromRange, " - "computed with mIMECompositionString (result string=\"%s\")", - this, NS_ConvertUTF16toUTF8(str).get())); - } - return result; - } - - nsAutoString str; - WidgetQueryContentEvent textContent(true, eQueryTextContent, mWidget); - WidgetQueryContentEvent::Options options; - int64_t startOffset = aRange.location; - if (IsIMEComposing()) { - // The composition may be at different offset from the selection start - // offset at dispatching compositionstart because start of composition - // is fixed when composition string becomes non-empty in the editor. - // Therefore, we need to use query event which is relative to insertion - // point. - options.mRelativeToInsertionPoint = true; - startOffset -= mIMECompositionStart; - } - textContent.InitForQueryTextContent(startOffset, aRange.length, options); - textContent.RequestFontRanges(); - DispatchEvent(textContent); - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::GetAttributedSubstringFromRange, " - "textContent={ mSucceeded=%s, mReply={ mString=\"%s\", mOffset=%u } }", - this, TrueOrFalse(textContent.mSucceeded), - NS_ConvertUTF16toUTF8(textContent.mReply.mString).get(), - textContent.mReply.mOffset)); - - if (!textContent.mSucceeded) { - return nil; - } - - // We don't set vertical information at this point. If required, - // OS will calls drawsVerticallyForCharacterAtIndex. - NSMutableAttributedString* result = - nsCocoaUtils::GetNSMutableAttributedString(textContent.mReply.mString, - textContent.mReply.mFontRanges, - false, - mWidget->BackingScaleFactor()); - if (aActualRange) { - aActualRange->location = textContent.mReply.mOffset; - aActualRange->length = textContent.mReply.mString.Length(); - } - return result; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -bool -IMEInputHandler::HasMarkedText() -{ - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::HasMarkedText, " - "mMarkedRange={ location=%llu, length=%llu }", - this, mMarkedRange.location, mMarkedRange.length)); - - return (mMarkedRange.location != NSNotFound) && (mMarkedRange.length != 0); -} - -NSRange -IMEInputHandler::MarkedRange() -{ - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::MarkedRange, " - "mMarkedRange={ location=%llu, length=%llu }", - this, mMarkedRange.location, mMarkedRange.length)); - - if (!HasMarkedText()) { - return NSMakeRange(NSNotFound, 0); - } - return mMarkedRange; -} - -NSRange -IMEInputHandler::SelectedRange() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::SelectedRange, Destroyed()=%s, mSelectedRange={ " - "location=%llu, length=%llu }", - this, TrueOrFalse(Destroyed()), mSelectedRange.location, - mSelectedRange.length)); - - if (Destroyed()) { - return mSelectedRange; - } - - if (mSelectedRange.location != NSNotFound) { - MOZ_ASSERT(mIMEHasFocus); - return mSelectedRange; - } - - RefPtr<IMEInputHandler> kungFuDeathGrip(this); - - WidgetQueryContentEvent selection(true, eQuerySelectedText, mWidget); - DispatchEvent(selection); - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::SelectedRange, selection={ mSucceeded=%s, " - "mReply={ mOffset=%u, mString.Length()=%u } }", - this, TrueOrFalse(selection.mSucceeded), selection.mReply.mOffset, - selection.mReply.mString.Length())); - - if (!selection.mSucceeded) { - return mSelectedRange; - } - - mWritingMode = selection.GetWritingMode(); - mRangeForWritingMode = NSMakeRange(selection.mReply.mOffset, - selection.mReply.mString.Length()); - - if (mIMEHasFocus) { - mSelectedRange = mRangeForWritingMode; - } - - return mRangeForWritingMode; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(mSelectedRange); -} - -bool -IMEInputHandler::DrawsVerticallyForCharacterAtIndex(uint32_t aCharIndex) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - if (Destroyed()) { - return false; - } - - if (mRangeForWritingMode.location == NSNotFound) { - // Update cached writing-mode value for the current selection. - SelectedRange(); - } - - if (aCharIndex < mRangeForWritingMode.location || - aCharIndex > mRangeForWritingMode.location + mRangeForWritingMode.length) { - // It's not clear to me whether this ever happens in practice, but if an - // IME ever wants to query writing mode at an offset outside the current - // selection, the writing-mode value may not be correct for the index. - // In that case, use FirstRectForCharacterRange to get a fresh value. - // This does more work than strictly necessary (we don't need the rect here), - // but should be a rare case. - NS_WARNING("DrawsVerticallyForCharacterAtIndex not using cached writing mode"); - NSRange range = NSMakeRange(aCharIndex, 1); - NSRange actualRange; - FirstRectForCharacterRange(range, &actualRange); - } - - return mWritingMode.IsVertical(); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); -} - -NSRect -IMEInputHandler::FirstRectForCharacterRange(NSRange& aRange, - NSRange* aActualRange) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::FirstRectForCharacterRange, Destroyed()=%s, " - "aRange={ location=%llu, length=%llu }, aActualRange=%p }", - this, TrueOrFalse(Destroyed()), aRange.location, aRange.length, - aActualRange)); - - // XXX this returns first character rect or caret rect, it is limitation of - // now. We need more work for returns first line rect. But current - // implementation is enough for IMEs. - - NSRect rect = NSMakeRect(0.0, 0.0, 0.0, 0.0); - NSRange actualRange = NSMakeRange(NSNotFound, 0); - if (aActualRange) { - *aActualRange = actualRange; - } - if (Destroyed() || aRange.location == NSNotFound) { - return rect; - } - - RefPtr<IMEInputHandler> kungFuDeathGrip(this); - - LayoutDeviceIntRect r; - bool useCaretRect = (aRange.length == 0); - if (!useCaretRect) { - WidgetQueryContentEvent charRect(true, eQueryTextRect, mWidget); - WidgetQueryContentEvent::Options options; - int64_t startOffset = aRange.location; - if (IsIMEComposing()) { - // The composition may be at different offset from the selection start - // offset at dispatching compositionstart because start of composition - // is fixed when composition string becomes non-empty in the editor. - // Therefore, we need to use query event which is relative to insertion - // point. - options.mRelativeToInsertionPoint = true; - startOffset -= mIMECompositionStart; - } - charRect.InitForQueryTextRect(startOffset, 1, options); - DispatchEvent(charRect); - if (charRect.mSucceeded) { - r = charRect.mReply.mRect; - actualRange.location = charRect.mReply.mOffset; - actualRange.length = charRect.mReply.mString.Length(); - mWritingMode = charRect.GetWritingMode(); - mRangeForWritingMode = actualRange; - } else { - useCaretRect = true; - } - } - - if (useCaretRect) { - WidgetQueryContentEvent caretRect(true, eQueryCaretRect, mWidget); - WidgetQueryContentEvent::Options options; - int64_t startOffset = aRange.location; - if (IsIMEComposing()) { - // The composition may be at different offset from the selection start - // offset at dispatching compositionstart because start of composition - // is fixed when composition string becomes non-empty in the editor. - // Therefore, we need to use query event which is relative to insertion - // point. - options.mRelativeToInsertionPoint = true; - startOffset -= mIMECompositionStart; - } - caretRect.InitForQueryCaretRect(startOffset, options); - DispatchEvent(caretRect); - if (!caretRect.mSucceeded) { - return rect; - } - r = caretRect.mReply.mRect; - r.width = 0; - actualRange.location = caretRect.mReply.mOffset; - actualRange.length = 0; - } - - nsIWidget* rootWidget = mWidget->GetTopLevelWidget(); - NSWindow* rootWindow = - static_cast<NSWindow*>(rootWidget->GetNativeData(NS_NATIVE_WINDOW)); - NSView* rootView = - static_cast<NSView*>(rootWidget->GetNativeData(NS_NATIVE_WIDGET)); - if (!rootWindow || !rootView) { - return rect; - } - rect = nsCocoaUtils::DevPixelsToCocoaPoints(r, mWidget->BackingScaleFactor()); - rect = [rootView convertRect:rect toView:nil]; - rect.origin = nsCocoaUtils::ConvertPointToScreen(rootWindow, rect.origin); - - if (aActualRange) { - *aActualRange = actualRange; - } - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::FirstRectForCharacterRange, " - "useCaretRect=%s rect={ x=%f, y=%f, width=%f, height=%f }, " - "actualRange={ location=%llu, length=%llu }", - this, TrueOrFalse(useCaretRect), rect.origin.x, rect.origin.y, - rect.size.width, rect.size.height, actualRange.location, - actualRange.length)); - - return rect; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRect(0.0, 0.0, 0.0, 0.0)); -} - -NSUInteger -IMEInputHandler::CharacterIndexForPoint(NSPoint& aPoint) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::CharacterIndexForPoint, aPoint={ x=%f, y=%f }", - this, aPoint.x, aPoint.y)); - - NSWindow* mainWindow = [NSApp mainWindow]; - if (!mWidget || !mainWindow) { - return NSNotFound; - } - - WidgetQueryContentEvent charAt(true, eQueryCharacterAtPoint, mWidget); - NSPoint ptInWindow = nsCocoaUtils::ConvertPointFromScreen(mainWindow, aPoint); - NSPoint ptInView = [mView convertPoint:ptInWindow fromView:nil]; - charAt.mRefPoint.x = - static_cast<int32_t>(ptInView.x) * mWidget->BackingScaleFactor(); - charAt.mRefPoint.y = - static_cast<int32_t>(ptInView.y) * mWidget->BackingScaleFactor(); - mWidget->DispatchWindowEvent(charAt); - if (!charAt.mSucceeded || - charAt.mReply.mOffset == WidgetQueryContentEvent::NOT_FOUND || - charAt.mReply.mOffset >= static_cast<uint32_t>(NSNotFound)) { - return NSNotFound; - } - - return charAt.mReply.mOffset; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSNotFound); -} - -extern "C" { -extern NSString *NSTextInputReplacementRangeAttributeName; -} - -NSArray* -IMEInputHandler::GetValidAttributesForMarkedText() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::GetValidAttributesForMarkedText", this)); - - // Return same attributes as Chromium (see render_widget_host_view_mac.mm) - // because most IMEs must be tested with Safari (OS default) and Chrome - // (having most market share). Therefore, we need to follow their behavior. - // XXX It might be better to reuse an array instance for this result because - // this may be called a lot. Note that Chromium does so. - return [NSArray arrayWithObjects:NSUnderlineStyleAttributeName, - NSUnderlineColorAttributeName, - NSMarkedClauseSegmentAttributeName, - NSTextInputReplacementRangeAttributeName, - nil]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - - -#pragma mark - - - -/****************************************************************************** - * - * IMEInputHandler implementation #2 - * - ******************************************************************************/ - -IMEInputHandler::IMEInputHandler(nsChildView* aWidget, - NSView<mozView> *aNativeView) - : TextInputHandlerBase(aWidget, aNativeView) - , mPendingMethods(0) - , mIMECompositionString(nullptr) - , mIMECompositionStart(UINT32_MAX) - , mIsIMEComposing(false) - , mIsIMEEnabled(true) - , mIsASCIICapableOnly(false) - , mIgnoreIMECommit(false) - , mIsInFocusProcessing(false) - , mIMEHasFocus(false) -{ - InitStaticMembers(); - - mMarkedRange.location = NSNotFound; - mMarkedRange.length = 0; - mSelectedRange.location = NSNotFound; - mSelectedRange.length = 0; -} - -IMEInputHandler::~IMEInputHandler() -{ - if (mTimer) { - mTimer->Cancel(); - mTimer = nullptr; - } - if (sFocusedIMEHandler == this) { - sFocusedIMEHandler = nullptr; - } - if (mIMECompositionString) { - [mIMECompositionString release]; - mIMECompositionString = nullptr; - } -} - -void -IMEInputHandler::OnFocusChangeInGecko(bool aFocus) -{ - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::OnFocusChangeInGecko, aFocus=%s, Destroyed()=%s, " - "sFocusedIMEHandler=%p", - this, TrueOrFalse(aFocus), TrueOrFalse(Destroyed()), sFocusedIMEHandler)); - - mSelectedRange.location = NSNotFound; // Marking dirty - mIMEHasFocus = aFocus; - - // This is called when the native focus is changed and when the native focus - // isn't changed but the focus is changed in Gecko. - if (!aFocus) { - if (sFocusedIMEHandler == this) - sFocusedIMEHandler = nullptr; - return; - } - - sFocusedIMEHandler = this; - mIsInFocusProcessing = true; - - // We need to notify IME of focus change in Gecko as native focus change - // because the window level of the focused element in Gecko may be changed. - mPendingMethods |= kNotifyIMEOfFocusChangeInGecko; - ResetTimer(); -} - -bool -IMEInputHandler::OnDestroyWidget(nsChildView* aDestroyingWidget) -{ - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::OnDestroyWidget, aDestroyingWidget=%p, " - "sFocusedIMEHandler=%p, IsIMEComposing()=%s", - this, aDestroyingWidget, sFocusedIMEHandler, - TrueOrFalse(IsIMEComposing()))); - - // If we're not focused, the focused IMEInputHandler may have been - // created by another widget/nsChildView. - if (sFocusedIMEHandler && sFocusedIMEHandler != this) { - sFocusedIMEHandler->OnDestroyWidget(aDestroyingWidget); - } - - if (!TextInputHandlerBase::OnDestroyWidget(aDestroyingWidget)) { - return false; - } - - if (IsIMEComposing()) { - // If our view is in the composition, we should clean up it. - CancelIMEComposition(); - } - - mSelectedRange.location = NSNotFound; // Marking dirty - mIMEHasFocus = false; - - return true; -} - -void -IMEInputHandler::SendCommittedText(NSString *aString) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::SendCommittedText, mView=%p, mWidget=%p, " - "inputContext=%p, mIsIMEComposing=%s", - this, mView, mWidget, mView ? [mView inputContext] : nullptr, - TrueOrFalse(mIsIMEComposing), mWidget)); - - NS_ENSURE_TRUE(mWidget, ); - // XXX We should send the string without mView. - if (!mView) { - return; - } - - NSAttributedString* attrStr = - [[NSAttributedString alloc] initWithString:aString]; - if ([mView conformsToProtocol:@protocol(NSTextInputClient)]) { - NSObject<NSTextInputClient>* textInputClient = - static_cast<NSObject<NSTextInputClient>*>(mView); - [textInputClient insertText:attrStr - replacementRange:NSMakeRange(NSNotFound, 0)]; - } - - // Last resort. If we cannot retrieve NSTextInputProtocol from mView - // or blocking to call our InsertText(), we should call InsertText() - // directly to commit composition forcibly. - if (mIsIMEComposing) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::SendCommittedText, trying to insert text directly " - "due to IME not calling our InsertText()", this)); - static_cast<TextInputHandler*>(this)->InsertText(attrStr); - MOZ_ASSERT(!mIsIMEComposing); - } - - [attrStr release]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -IMEInputHandler::KillIMEComposition() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::KillIMEComposition, mView=%p, mWidget=%p, " - "inputContext=%p, mIsIMEComposing=%s, " - "Destroyed()=%s, IsFocused()=%s", - this, mView, mWidget, mView ? [mView inputContext] : nullptr, - TrueOrFalse(mIsIMEComposing), TrueOrFalse(Destroyed()), - TrueOrFalse(IsFocused()))); - - if (Destroyed()) { - return; - } - - if (IsFocused()) { - NS_ENSURE_TRUE_VOID(mView); - NSTextInputContext* inputContext = [mView inputContext]; - NS_ENSURE_TRUE_VOID(inputContext); - [inputContext discardMarkedText]; - return; - } - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::KillIMEComposition, Pending...", this)); - - // Commit the composition internally. - SendCommittedText(mIMECompositionString); - NS_ASSERTION(!mIsIMEComposing, "We're still in a composition"); - // The pending method will be fired by the next focus event. - mPendingMethods |= kDiscardIMEComposition; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -IMEInputHandler::CommitIMEComposition() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!IsIMEComposing()) - return; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::CommitIMEComposition, mIMECompositionString=%s", - this, GetCharacters(mIMECompositionString))); - - KillIMEComposition(); - - if (!IsIMEComposing()) - return; - - // If the composition is still there, KillIMEComposition only kills the - // composition in TSM. We also need to finish the our composition too. - SendCommittedText(mIMECompositionString); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -IMEInputHandler::CancelIMEComposition() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!IsIMEComposing()) - return; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::CancelIMEComposition, mIMECompositionString=%s", - this, GetCharacters(mIMECompositionString))); - - // For canceling the current composing, we need to ignore the param of - // insertText. But this code is ugly... - mIgnoreIMECommit = true; - KillIMEComposition(); - mIgnoreIMECommit = false; - - if (!IsIMEComposing()) - return; - - // If the composition is still there, KillIMEComposition only kills the - // composition in TSM. We also need to kill the our composition too. - SendCommittedText(@""); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -bool -IMEInputHandler::IsFocused() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - NS_ENSURE_TRUE(!Destroyed(), false); - NSWindow* window = [mView window]; - NS_ENSURE_TRUE(window, false); - return [window firstResponder] == mView && - [window isKeyWindow] && - [[NSApplication sharedApplication] isActive]; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); -} - -bool -IMEInputHandler::IsIMEOpened() -{ - TISInputSourceWrapper tis; - tis.InitByCurrentInputSource(); - return tis.IsOpenedIMEMode(); -} - -void -IMEInputHandler::SetASCIICapableOnly(bool aASCIICapableOnly) -{ - if (aASCIICapableOnly == mIsASCIICapableOnly) - return; - - CommitIMEComposition(); - mIsASCIICapableOnly = aASCIICapableOnly; - SyncASCIICapableOnly(); -} - -void -IMEInputHandler::EnableIME(bool aEnableIME) -{ - if (aEnableIME == mIsIMEEnabled) - return; - - CommitIMEComposition(); - mIsIMEEnabled = aEnableIME; -} - -void -IMEInputHandler::SetIMEOpenState(bool aOpenIME) -{ - if (!IsFocused() || IsIMEOpened() == aOpenIME) - return; - - if (!aOpenIME) { - TISInputSourceWrapper tis; - tis.InitByCurrentASCIICapableInputSource(); - tis.Select(); - return; - } - - // If we know the latest IME opened mode, we should select it. - if (sLatestIMEOpenedModeInputSourceID) { - TISInputSourceWrapper tis; - tis.InitByInputSourceID(sLatestIMEOpenedModeInputSourceID); - tis.Select(); - return; - } - - // XXX If the current input source is a mode of IME, we should turn on it, - // but we haven't found such way... - - // Finally, we should refer the system locale but this is a little expensive, - // we shouldn't retry this (if it was succeeded, we already set - // sLatestIMEOpenedModeInputSourceID at that time). - static bool sIsPrefferredIMESearched = false; - if (sIsPrefferredIMESearched) - return; - sIsPrefferredIMESearched = true; - OpenSystemPreferredLanguageIME(); -} - -void -IMEInputHandler::OpenSystemPreferredLanguageIME() -{ - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::OpenSystemPreferredLanguageIME", this)); - - CFArrayRef langList = ::CFLocaleCopyPreferredLanguages(); - if (!langList) { - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::OpenSystemPreferredLanguageIME, langList is NULL", - this)); - return; - } - CFIndex count = ::CFArrayGetCount(langList); - for (CFIndex i = 0; i < count; i++) { - CFLocaleRef locale = - ::CFLocaleCreate(kCFAllocatorDefault, - static_cast<CFStringRef>(::CFArrayGetValueAtIndex(langList, i))); - if (!locale) { - continue; - } - - bool changed = false; - CFStringRef lang = static_cast<CFStringRef>( - ::CFLocaleGetValue(locale, kCFLocaleLanguageCode)); - NS_ASSERTION(lang, "lang is null"); - if (lang) { - TISInputSourceWrapper tis; - tis.InitByLanguage(lang); - if (tis.IsOpenedIMEMode()) { - if (MOZ_LOG_TEST(gLog, LogLevel::Info)) { - CFStringRef foundTIS; - tis.GetInputSourceID(foundTIS); - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::OpenSystemPreferredLanguageIME, " - "foundTIS=%s, lang=%s", - this, GetCharacters(foundTIS), GetCharacters(lang))); - } - tis.Select(); - changed = true; - } - } - ::CFRelease(locale); - if (changed) { - break; - } - } - ::CFRelease(langList); -} - -void -IMEInputHandler::OnSelectionChange(const IMENotification& aIMENotification) -{ - MOZ_LOG(gLog, LogLevel::Info, - ("%p IMEInputHandler::OnSelectionChange", this)); - - if (aIMENotification.mSelectionChangeData.mOffset == UINT32_MAX) { - mSelectedRange.location = NSNotFound; - mSelectedRange.length = 0; - mRangeForWritingMode.location = NSNotFound; - mRangeForWritingMode.length = 0; - return; - } - - mWritingMode = aIMENotification.mSelectionChangeData.GetWritingMode(); - mRangeForWritingMode = - NSMakeRange(aIMENotification.mSelectionChangeData.mOffset, - aIMENotification.mSelectionChangeData.Length()); - if (mIMEHasFocus) { - mSelectedRange = mRangeForWritingMode; - } -} - -bool -IMEInputHandler::OnHandleEvent(NSEvent* aEvent) -{ - if (!IsFocused()) { - return false; - } - NSTextInputContext* inputContext = [mView inputContext]; - return [inputContext handleEvent:aEvent]; -} - -#pragma mark - - - -/****************************************************************************** - * - * TextInputHandlerBase implementation - * - ******************************************************************************/ - -int32_t TextInputHandlerBase::sSecureEventInputCount = 0; - -NS_IMPL_ISUPPORTS(TextInputHandlerBase, - TextEventDispatcherListener, - nsISupportsWeakReference) - -TextInputHandlerBase::TextInputHandlerBase(nsChildView* aWidget, - NSView<mozView> *aNativeView) - : mWidget(aWidget) - , mDispatcher(aWidget->GetTextEventDispatcher()) -{ - gHandlerInstanceCount++; - mView = [aNativeView retain]; -} - -TextInputHandlerBase::~TextInputHandlerBase() -{ - [mView release]; - if (--gHandlerInstanceCount == 0) { - TISInputSourceWrapper::Shutdown(); - } -} - -bool -TextInputHandlerBase::OnDestroyWidget(nsChildView* aDestroyingWidget) -{ - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandlerBase::OnDestroyWidget, " - "aDestroyingWidget=%p, mWidget=%p", - this, aDestroyingWidget, mWidget)); - - if (aDestroyingWidget != mWidget) { - return false; - } - - mWidget = nullptr; - mDispatcher = nullptr; - return true; -} - -bool -TextInputHandlerBase::DispatchEvent(WidgetGUIEvent& aEvent) -{ - return mWidget->DispatchWindowEvent(aEvent); -} - -void -TextInputHandlerBase::InitKeyEvent(NSEvent *aNativeKeyEvent, - WidgetKeyboardEvent& aKeyEvent, - const nsAString* aInsertString) -{ - NS_ASSERTION(aNativeKeyEvent, "aNativeKeyEvent must not be NULL"); - - if (mKeyboardOverride.mOverrideEnabled) { - TISInputSourceWrapper tis; - tis.InitByLayoutID(mKeyboardOverride.mKeyboardLayout, true); - tis.InitKeyEvent(aNativeKeyEvent, aKeyEvent, aInsertString); - return; - } - TISInputSourceWrapper::CurrentInputSource(). - InitKeyEvent(aNativeKeyEvent, aKeyEvent, aInsertString); -} - -nsresult -TextInputHandlerBase::SynthesizeNativeKeyEvent( - int32_t aNativeKeyboardLayout, - int32_t aNativeKeyCode, - uint32_t aModifierFlags, - const nsAString& aCharacters, - const nsAString& aUnmodifiedCharacters) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - static const uint32_t sModifierFlagMap[][2] = { - { nsIWidget::CAPS_LOCK, NSAlphaShiftKeyMask }, - { nsIWidget::SHIFT_L, NSShiftKeyMask | 0x0002 }, - { nsIWidget::SHIFT_R, NSShiftKeyMask | 0x0004 }, - { nsIWidget::CTRL_L, NSControlKeyMask | 0x0001 }, - { nsIWidget::CTRL_R, NSControlKeyMask | 0x2000 }, - { nsIWidget::ALT_L, NSAlternateKeyMask | 0x0020 }, - { nsIWidget::ALT_R, NSAlternateKeyMask | 0x0040 }, - { nsIWidget::COMMAND_L, NSCommandKeyMask | 0x0008 }, - { nsIWidget::COMMAND_R, NSCommandKeyMask | 0x0010 }, - { nsIWidget::NUMERIC_KEY_PAD, NSNumericPadKeyMask }, - { nsIWidget::HELP, NSHelpKeyMask }, - { nsIWidget::FUNCTION, NSFunctionKeyMask } - }; - - uint32_t modifierFlags = 0; - for (uint32_t i = 0; i < ArrayLength(sModifierFlagMap); ++i) { - if (aModifierFlags & sModifierFlagMap[i][0]) { - modifierFlags |= sModifierFlagMap[i][1]; - } - } - - NSInteger windowNumber = [[mView window] windowNumber]; - bool sendFlagsChangedEvent = IsModifierKey(aNativeKeyCode); - NSEventType eventType = sendFlagsChangedEvent ? NSFlagsChanged : NSKeyDown; - NSEvent* downEvent = - [NSEvent keyEventWithType:eventType - location:NSMakePoint(0,0) - modifierFlags:modifierFlags - timestamp:0 - windowNumber:windowNumber - context:[NSGraphicsContext currentContext] - characters:nsCocoaUtils::ToNSString(aCharacters) - charactersIgnoringModifiers:nsCocoaUtils::ToNSString(aUnmodifiedCharacters) - isARepeat:NO - keyCode:aNativeKeyCode]; - - NSEvent* upEvent = sendFlagsChangedEvent ? - nil : nsCocoaUtils::MakeNewCocoaEventWithType(NSKeyUp, downEvent); - - if (downEvent && (sendFlagsChangedEvent || upEvent)) { - KeyboardLayoutOverride currentLayout = mKeyboardOverride; - mKeyboardOverride.mKeyboardLayout = aNativeKeyboardLayout; - mKeyboardOverride.mOverrideEnabled = true; - [NSApp sendEvent:downEvent]; - if (upEvent) { - [NSApp sendEvent:upEvent]; - } - // processKeyDownEvent and keyUp block exceptions so we're sure to - // reach here to restore mKeyboardOverride - mKeyboardOverride = currentLayout; - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NSInteger -TextInputHandlerBase::GetWindowLevel() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandlerBase::GetWindowLevel, Destryoed()=%s", - this, TrueOrFalse(Destroyed()))); - - if (Destroyed()) { - return NSNormalWindowLevel; - } - - // When an <input> element on a XUL <panel> is focused, the actual focused view - // is the panel's parent view (mView). But the editor is displayed on the - // popped-up widget's view (editorView). We want the latter's window level. - NSView<mozView>* editorView = mWidget->GetEditorView(); - NS_ENSURE_TRUE(editorView, NSNormalWindowLevel); - NSInteger windowLevel = [[editorView window] level]; - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandlerBase::GetWindowLevel, windowLevel=%s (%X)", - this, GetWindowLevelName(windowLevel), windowLevel)); - - return windowLevel; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSNormalWindowLevel); -} - -NS_IMETHODIMP -TextInputHandlerBase::AttachNativeKeyEvent(WidgetKeyboardEvent& aKeyEvent) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // Don't try to replace a native event if one already exists. - // OS X doesn't have an OS modifier, can't make a native event. - if (aKeyEvent.mNativeKeyEvent || aKeyEvent.mModifiers & MODIFIER_OS) { - return NS_OK; - } - - MOZ_LOG(gLog, LogLevel::Info, - ("%p TextInputHandlerBase::AttachNativeKeyEvent, key=0x%X, char=0x%X, " - "mod=0x%X", this, aKeyEvent.mKeyCode, aKeyEvent.mCharCode, - aKeyEvent.mModifiers)); - - NSEventType eventType; - if (aKeyEvent.mMessage == eKeyUp) { - eventType = NSKeyUp; - } else { - eventType = NSKeyDown; - } - - static const uint32_t sModifierFlagMap[][2] = { - { MODIFIER_SHIFT, NSShiftKeyMask }, - { MODIFIER_CONTROL, NSControlKeyMask }, - { MODIFIER_ALT, NSAlternateKeyMask }, - { MODIFIER_ALTGRAPH, NSAlternateKeyMask }, - { MODIFIER_META, NSCommandKeyMask }, - { MODIFIER_CAPSLOCK, NSAlphaShiftKeyMask }, - { MODIFIER_NUMLOCK, NSNumericPadKeyMask } - }; - - NSUInteger modifierFlags = 0; - for (uint32_t i = 0; i < ArrayLength(sModifierFlagMap); ++i) { - if (aKeyEvent.mModifiers & sModifierFlagMap[i][0]) { - modifierFlags |= sModifierFlagMap[i][1]; - } - } - - NSInteger windowNumber = [[mView window] windowNumber]; - - NSString* characters; - if (aKeyEvent.mCharCode) { - characters = [NSString stringWithCharacters: - reinterpret_cast<const unichar*>(&(aKeyEvent.mCharCode)) length:1]; - } else { - uint32_t cocoaCharCode = - nsCocoaUtils::ConvertGeckoKeyCodeToMacCharCode(aKeyEvent.mKeyCode); - characters = [NSString stringWithCharacters: - reinterpret_cast<const unichar*>(&cocoaCharCode) length:1]; - } - - aKeyEvent.mNativeKeyEvent = - [NSEvent keyEventWithType:eventType - location:NSMakePoint(0,0) - modifierFlags:modifierFlags - timestamp:0 - windowNumber:windowNumber - context:[NSGraphicsContext currentContext] - characters:characters - charactersIgnoringModifiers:characters - isARepeat:NO - keyCode:0]; // Native key code not currently needed - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -bool -TextInputHandlerBase::SetSelection(NSRange& aRange) -{ - MOZ_ASSERT(!Destroyed()); - - RefPtr<TextInputHandlerBase> kungFuDeathGrip(this); - WidgetSelectionEvent selectionEvent(true, eSetSelection, mWidget); - selectionEvent.mOffset = aRange.location; - selectionEvent.mLength = aRange.length; - selectionEvent.mReversed = false; - selectionEvent.mExpandToClusterBoundary = false; - DispatchEvent(selectionEvent); - NS_ENSURE_TRUE(selectionEvent.mSucceeded, false); - return !Destroyed(); -} - -/* static */ bool -TextInputHandlerBase::IsPrintableChar(char16_t aChar) -{ - return (aChar >= 0x20 && aChar <= 0x7E) || aChar >= 0xA0; -} - - -/* static */ bool -TextInputHandlerBase::IsSpecialGeckoKey(UInt32 aNativeKeyCode) -{ - // this table is used to determine which keys are special and should not - // generate a charCode - switch (aNativeKeyCode) { - // modifiers - we don't get separate events for these yet - case kVK_Escape: - case kVK_Shift: - case kVK_RightShift: - case kVK_Command: - case kVK_RightCommand: - case kVK_CapsLock: - case kVK_Control: - case kVK_RightControl: - case kVK_Option: - case kVK_RightOption: - case kVK_ANSI_KeypadClear: - case kVK_Function: - - // function keys - case kVK_F1: - case kVK_F2: - case kVK_F3: - case kVK_F4: - case kVK_F5: - case kVK_F6: - case kVK_F7: - case kVK_F8: - case kVK_F9: - case kVK_F10: - case kVK_F11: - case kVK_F12: - case kVK_PC_Pause: - case kVK_PC_ScrollLock: - case kVK_PC_PrintScreen: - case kVK_F16: - case kVK_F17: - case kVK_F18: - case kVK_F19: - - case kVK_PC_Insert: - case kVK_PC_Delete: - case kVK_Tab: - case kVK_PC_Backspace: - case kVK_PC_ContextMenu: - - case kVK_JIS_Eisu: - case kVK_JIS_Kana: - - case kVK_Home: - case kVK_End: - case kVK_PageUp: - case kVK_PageDown: - case kVK_LeftArrow: - case kVK_RightArrow: - case kVK_UpArrow: - case kVK_DownArrow: - case kVK_Return: - case kVK_ANSI_KeypadEnter: - case kVK_Powerbook_KeypadEnter: - return true; - } - return false; -} - -/* static */ bool -TextInputHandlerBase::IsNormalCharInputtingEvent( - const WidgetKeyboardEvent& aKeyEvent) -{ - // this is not character inputting event, simply. - if (aKeyEvent.mNativeCharacters.IsEmpty() || - aKeyEvent.IsMeta()) { - return false; - } - return !IsControlChar(aKeyEvent.mNativeCharacters[0]); -} - -/* static */ bool -TextInputHandlerBase::IsModifierKey(UInt32 aNativeKeyCode) -{ - switch (aNativeKeyCode) { - case kVK_CapsLock: - case kVK_RightCommand: - case kVK_Command: - case kVK_Shift: - case kVK_Option: - case kVK_Control: - case kVK_RightShift: - case kVK_RightOption: - case kVK_RightControl: - case kVK_Function: - return true; - } - return false; -} - -/* static */ void -TextInputHandlerBase::EnableSecureEventInput() -{ - sSecureEventInputCount++; - ::EnableSecureEventInput(); -} - -/* static */ void -TextInputHandlerBase::DisableSecureEventInput() -{ - if (!sSecureEventInputCount) { - return; - } - sSecureEventInputCount--; - ::DisableSecureEventInput(); -} - -/* static */ bool -TextInputHandlerBase::IsSecureEventInputEnabled() -{ - NS_ASSERTION(!!sSecureEventInputCount == !!::IsSecureEventInputEnabled(), - "Some other process has enabled secure event input"); - return !!sSecureEventInputCount; -} - -/* static */ void -TextInputHandlerBase::EnsureSecureEventInputDisabled() -{ - while (sSecureEventInputCount) { - TextInputHandlerBase::DisableSecureEventInput(); - } -} - -#pragma mark - - - -/****************************************************************************** - * - * TextInputHandlerBase::KeyEventState implementation - * - ******************************************************************************/ - -void -TextInputHandlerBase::KeyEventState::InitKeyEvent( - TextInputHandlerBase* aHandler, - WidgetKeyboardEvent& aKeyEvent) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - MOZ_ASSERT(aHandler); - MOZ_RELEASE_ASSERT(mKeyEvent); - - NSEvent* nativeEvent = mKeyEvent; - if (!mInsertedString.IsEmpty()) { - nsAutoString unhandledString; - GetUnhandledString(unhandledString); - NSString* unhandledNSString = - nsCocoaUtils::ToNSString(unhandledString); - // If the key event's some characters were already handled by - // InsertString() calls, we need to create a dummy event which doesn't - // include the handled characters. - nativeEvent = - [NSEvent keyEventWithType:[mKeyEvent type] - location:[mKeyEvent locationInWindow] - modifierFlags:[mKeyEvent modifierFlags] - timestamp:[mKeyEvent timestamp] - windowNumber:[mKeyEvent windowNumber] - context:[mKeyEvent context] - characters:unhandledNSString - charactersIgnoringModifiers:[mKeyEvent charactersIgnoringModifiers] - isARepeat:[mKeyEvent isARepeat] - keyCode:[mKeyEvent keyCode]]; - } - - aHandler->InitKeyEvent(nativeEvent, aKeyEvent, mInsertString); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -TextInputHandlerBase::KeyEventState::GetUnhandledString( - nsAString& aUnhandledString) const -{ - aUnhandledString.Truncate(); - if (NS_WARN_IF(!mKeyEvent)) { - return; - } - nsAutoString characters; - nsCocoaUtils::GetStringForNSString([mKeyEvent characters], - characters); - if (characters.IsEmpty()) { - return; - } - if (mInsertedString.IsEmpty()) { - aUnhandledString = characters; - return; - } - - // The insertes string must match with the start of characters. - MOZ_ASSERT(StringBeginsWith(characters, mInsertedString)); - - aUnhandledString = nsDependentSubstring(characters, mInsertedString.Length()); -} - -#pragma mark - - - -/****************************************************************************** - * - * TextInputHandlerBase::AutoInsertStringClearer implementation - * - ******************************************************************************/ - -TextInputHandlerBase::AutoInsertStringClearer::~AutoInsertStringClearer() -{ - if (mState && mState->mInsertString) { - // If inserting string is a part of characters of the event, - // we should record it as inserted string. - nsAutoString characters; - nsCocoaUtils::GetStringForNSString([mState->mKeyEvent characters], - characters); - nsAutoString insertedString(mState->mInsertedString); - insertedString += *mState->mInsertString; - if (StringBeginsWith(characters, insertedString)) { - mState->mInsertedString = insertedString; - } - } - if (mState) { - mState->mInsertString = nullptr; - } -} diff --git a/widget/cocoa/VibrancyManager.h b/widget/cocoa/VibrancyManager.h deleted file mode 100644 index 04b1ad1cda..0000000000 --- a/widget/cocoa/VibrancyManager.h +++ /dev/null @@ -1,120 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; 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 VibrancyManager_h -#define VibrancyManager_h - -#include "mozilla/Assertions.h" -#include "nsClassHashtable.h" -#include "nsRegion.h" -#include "nsTArray.h" -#include "ViewRegion.h" - -#import <Foundation/NSGeometry.h> - -@class NSColor; -@class NSView; -class nsChildView; - -namespace mozilla { - -enum class VibrancyType { - LIGHT, - DARK, - TOOLTIP, - MENU, - HIGHLIGHTED_MENUITEM, - SHEET, - SOURCE_LIST, - SOURCE_LIST_SELECTION, - ACTIVE_SOURCE_LIST_SELECTION -}; - -/** - * VibrancyManager takes care of updating the vibrant regions of a window. - * Vibrancy is a visual look that was introduced on OS X starting with 10.10. - * An app declares vibrant window regions to the window server, and the window - * server will display a blurred rendering of the screen contents from behind - * the window in these areas, behind the actual window contents. Consequently, - * the effect is only visible in areas where the window contents are not - * completely opaque. Usually this is achieved by clearing the background of - * the window prior to drawing in the vibrant areas. This is possible even if - * the window is declared as opaque. - */ -class VibrancyManager { -public: - /** - * Create a new VibrancyManager instance and provide it with an NSView - * to attach NSVisualEffectViews to. - * - * @param aCoordinateConverter The nsChildView to use for converting - * nsIntRect device pixel coordinates into Cocoa NSRect coordinates. Must - * outlive this VibrancyManager instance. - * @param aContainerView The view that's going to be the superview of the - * NSVisualEffectViews which will be created for vibrant regions. - */ - VibrancyManager(const nsChildView& aCoordinateConverter, - NSView* aContainerView) - : mCoordinateConverter(aCoordinateConverter) - , mContainerView(aContainerView) - { - MOZ_ASSERT(SystemSupportsVibrancy(), - "Don't instantiate this if !SystemSupportsVibrancy()"); - } - - /** - * Update the placement of the NSVisualEffectViews inside the container - * NSView so that they cover aRegion, and create new NSVisualEffectViews - * or remove existing ones as needed. - * @param aType The vibrancy type to use in the region. - * @param aRegion The vibrant area, in device pixels. - */ - void UpdateVibrantRegion(VibrancyType aType, - const LayoutDeviceIntRegion& aRegion); - - bool HasVibrantRegions() { return !mVibrantRegions.IsEmpty(); } - - /** - * Return the fill color that should be drawn on top of the cleared window - * parts. Usually this would be drawn by -[NSVisualEffectView drawRect:]. - * The returned color is opaque if the system-wide "Reduce transparency" - * preference is set. - */ - NSColor* VibrancyFillColorForType(VibrancyType aType); - - /** - * Return the font smoothing background color that should be used for text - * drawn on top of the vibrant window parts. - */ - NSColor* VibrancyFontSmoothingBackgroundColorForType(VibrancyType aType); - - /** - * Check whether the operating system supports vibrancy at all. - * You may only create a VibrancyManager instance if this returns true. - * @return Whether VibrancyManager can be used on this OS. - */ - static bool SystemSupportsVibrancy(); - - /** - * Create an NSVisualEffectView for the specified vibrancy type. The return - * value is not autoreleased. We return an object of type NSView* because we - * compile with an SDK that does not contain a definition for - * NSVisualEffectView. - * @param aIsContainer Whether this NSView will have child views. This value - * affects hit testing: Container views will pass through - * hit testing requests to their children, and leaf views - * will be transparent to hit testing. - */ - static NSView* CreateEffectView(VibrancyType aType, BOOL aIsContainer = NO); - -protected: - const nsChildView& mCoordinateConverter; - NSView* mContainerView; - nsClassHashtable<nsUint32HashKey, ViewRegion> mVibrantRegions; -}; - -} // namespace mozilla - -#endif // VibrancyManager_h diff --git a/widget/cocoa/VibrancyManager.mm b/widget/cocoa/VibrancyManager.mm deleted file mode 100644 index d02338eb6b..0000000000 --- a/widget/cocoa/VibrancyManager.mm +++ /dev/null @@ -1,244 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; 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/. */ - -#include "VibrancyManager.h" -#include "nsChildView.h" -#import <objc/message.h> - -using namespace mozilla; - -void -VibrancyManager::UpdateVibrantRegion(VibrancyType aType, - const LayoutDeviceIntRegion& aRegion) -{ - if (aRegion.IsEmpty()) { - mVibrantRegions.Remove(uint32_t(aType)); - return; - } - auto& vr = *mVibrantRegions.LookupOrAdd(uint32_t(aType)); - vr.UpdateRegion(aRegion, mCoordinateConverter, mContainerView, ^() { - return this->CreateEffectView(aType); - }); -} - -@interface NSView(CurrentFillColor) -- (NSColor*)_currentFillColor; -@end - -static NSColor* -AdjustedColor(NSColor* aFillColor, VibrancyType aType) -{ - if (aType == VibrancyType::MENU && [aFillColor alphaComponent] == 1.0) { - // The opaque fill color that's used for the menu background when "Reduce - // vibrancy" is checked in the system accessibility prefs is too dark. - // This is probably because we're not using the right material for menus, - // see VibrancyManager::CreateEffectView. - return [NSColor colorWithDeviceWhite:0.96 alpha:1.0]; - } - return aFillColor; -} - -NSColor* -VibrancyManager::VibrancyFillColorForType(VibrancyType aType) -{ - NSView* view = mVibrantRegions.LookupOrAdd(uint32_t(aType))->GetAnyView(); - - if (view && [view respondsToSelector:@selector(_currentFillColor)]) { - // -[NSVisualEffectView _currentFillColor] is the color that the view - // draws in its drawRect implementation. - return AdjustedColor([view _currentFillColor], aType); - } - return [NSColor whiteColor]; -} - -@interface NSView(FontSmoothingBackgroundColor) -- (NSColor*)fontSmoothingBackgroundColor; -@end - -NSColor* -VibrancyManager::VibrancyFontSmoothingBackgroundColorForType(VibrancyType aType) -{ - NSView* view = mVibrantRegions.LookupOrAdd(uint32_t(aType))->GetAnyView(); - - if (view && [view respondsToSelector:@selector(fontSmoothingBackgroundColor)]) { - return [view fontSmoothingBackgroundColor]; - } - return [NSColor clearColor]; -} - -static NSView* -HitTestNil(id self, SEL _cmd, NSPoint aPoint) -{ - // This view must be transparent to mouse events. - return nil; -} - -static BOOL -AllowsVibrancyYes(id self, SEL _cmd) -{ - // Means that the foreground is blended using a vibrant blend mode. - return YES; -} - -static Class -CreateEffectViewClass(BOOL aForegroundVibrancy, BOOL aIsContainer) -{ - // Create a class that inherits from NSVisualEffectView and overrides the - // methods -[NSView hitTest:] and -[NSVisualEffectView allowsVibrancy]. - Class NSVisualEffectViewClass = NSClassFromString(@"NSVisualEffectView"); - const char* className = aForegroundVibrancy - ? "EffectViewWithForegroundVibrancy" : "EffectViewWithoutForegroundVibrancy"; - Class EffectViewClass = objc_allocateClassPair(NSVisualEffectViewClass, className, 0); - if (!aIsContainer) { - class_addMethod(EffectViewClass, @selector(hitTest:), (IMP)HitTestNil, - "@@:{CGPoint=dd}"); - } - if (aForegroundVibrancy) { - // Override the -[NSView allowsVibrancy] method to return YES. - class_addMethod(EffectViewClass, @selector(allowsVibrancy), (IMP)AllowsVibrancyYes, "I@:"); - } - return EffectViewClass; -} - -static id -AppearanceForVibrancyType(VibrancyType aType) -{ - Class NSAppearanceClass = NSClassFromString(@"NSAppearance"); - switch (aType) { - case VibrancyType::LIGHT: - case VibrancyType::TOOLTIP: - case VibrancyType::MENU: - case VibrancyType::HIGHLIGHTED_MENUITEM: - case VibrancyType::SHEET: - case VibrancyType::SOURCE_LIST: - case VibrancyType::SOURCE_LIST_SELECTION: - case VibrancyType::ACTIVE_SOURCE_LIST_SELECTION: - return [NSAppearanceClass performSelector:@selector(appearanceNamed:) - withObject:@"NSAppearanceNameVibrantLight"]; - case VibrancyType::DARK: - return [NSAppearanceClass performSelector:@selector(appearanceNamed:) - withObject:@"NSAppearanceNameVibrantDark"]; - } -} - -#if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10 -enum { - NSVisualEffectStateFollowsWindowActiveState, - NSVisualEffectStateActive, - NSVisualEffectStateInactive -}; - -enum { - NSVisualEffectMaterialTitlebar = 3 -}; -#endif - -#if !defined(MAC_OS_X_VERSION_10_11) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11 -enum { - NSVisualEffectMaterialMenu = 5, - NSVisualEffectMaterialSidebar = 7 -}; -#endif - -static NSUInteger -VisualEffectStateForVibrancyType(VibrancyType aType) -{ - switch (aType) { - case VibrancyType::TOOLTIP: - case VibrancyType::MENU: - case VibrancyType::HIGHLIGHTED_MENUITEM: - case VibrancyType::SHEET: - // Tooltip and menu windows are never "key" and sheets always looks - // active, so we need to tell the vibrancy effect to look active - // regardless of window state. - return NSVisualEffectStateActive; - default: - return NSVisualEffectStateFollowsWindowActiveState; - } -} - -static BOOL -HasVibrantForeground(VibrancyType aType) -{ - switch (aType) { - case VibrancyType::MENU: - return YES; - default: - return NO; - } -} - -#if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 -enum { - NSVisualEffectMaterialSelection = 4 -}; -#endif - -@interface NSView(NSVisualEffectViewMethods) -- (void)setState:(NSUInteger)state; -- (void)setMaterial:(NSUInteger)material; -- (void)setEmphasized:(BOOL)emphasized; -@end - -NSView* -VibrancyManager::CreateEffectView(VibrancyType aType, BOOL aIsContainer) -{ - static Class EffectViewWithoutForegroundVibrancy = CreateEffectViewClass(NO, NO); - static Class EffectViewWithForegroundVibrancy = CreateEffectViewClass(YES, NO); - static Class EffectViewContainer = CreateEffectViewClass(NO, YES); - - // Pick the right NSVisualEffectView subclass for the desired vibrancy mode. - // For "container" views, never use foreground vibrancy, because returning - // YES from allowsVibrancy forces on foreground vibrancy for all descendant - // views which can have unintended effects. - Class EffectViewClass = aIsContainer - ? EffectViewContainer - : (HasVibrantForeground(aType) ? EffectViewWithForegroundVibrancy - : EffectViewWithoutForegroundVibrancy); - NSView* effectView = [[EffectViewClass alloc] initWithFrame:NSZeroRect]; - [effectView performSelector:@selector(setAppearance:) - withObject:AppearanceForVibrancyType(aType)]; - [effectView setState:VisualEffectStateForVibrancyType(aType)]; - - BOOL canUseElCapitanMaterials = nsCocoaFeatures::OnElCapitanOrLater(); - if (aType == VibrancyType::MENU) { - // Before 10.11 there is no material that perfectly matches the menu - // look. Of all available material types, NSVisualEffectMaterialTitlebar - // is the one that comes closest. - [effectView setMaterial:canUseElCapitanMaterials ? NSVisualEffectMaterialMenu - : NSVisualEffectMaterialTitlebar]; - } else if (aType == VibrancyType::SOURCE_LIST && canUseElCapitanMaterials) { - [effectView setMaterial:NSVisualEffectMaterialSidebar]; - } else if (aType == VibrancyType::HIGHLIGHTED_MENUITEM || - aType == VibrancyType::SOURCE_LIST_SELECTION || - aType == VibrancyType::ACTIVE_SOURCE_LIST_SELECTION) { - [effectView setMaterial:NSVisualEffectMaterialSelection]; - if ([effectView respondsToSelector:@selector(setEmphasized:)] && - aType != VibrancyType::SOURCE_LIST_SELECTION) { - [effectView setEmphasized:YES]; - } - } - - return effectView; -} - -static bool -ComputeSystemSupportsVibrancy() -{ -#ifdef __x86_64__ - return NSClassFromString(@"NSAppearance") && - NSClassFromString(@"NSVisualEffectView"); -#else - // objc_allocateClassPair doesn't work in 32 bit mode, so turn off vibrancy. - return false; -#endif -} - -/* static */ bool -VibrancyManager::SystemSupportsVibrancy() -{ - static bool supportsVibrancy = ComputeSystemSupportsVibrancy(); - return supportsVibrancy; -} diff --git a/widget/cocoa/ViewRegion.h b/widget/cocoa/ViewRegion.h deleted file mode 100644 index 9ff7e6d091..0000000000 --- a/widget/cocoa/ViewRegion.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; 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 ViewRegion_h -#define ViewRegion_h - -#include "Units.h" -#include "nsTArray.h" - -@class NSView; - -namespace mozilla { - -/** - * Manages a set of NSViews to cover a LayoutDeviceIntRegion. - */ -class ViewRegion { -public: - ~ViewRegion(); - - mozilla::LayoutDeviceIntRegion Region() { return mRegion; } - - /** - * Update the region. - * @param aRegion The new region. - * @param aCoordinateConverter The nsChildView to use for converting - * LayoutDeviceIntRect device pixel coordinates into Cocoa NSRect coordinates. - * @param aContainerView The view that's going to be the superview of the - * NSViews which will be created for this region. - * @param aViewCreationCallback A block that instantiates new NSViews. - * @return Whether or not the region changed. - */ - bool UpdateRegion(const mozilla::LayoutDeviceIntRegion& aRegion, - const nsChildView& aCoordinateConverter, - NSView* aContainerView, - NSView* (^aViewCreationCallback)()); - - /** - * Return an NSView from the region, if there is any. - */ - NSView* GetAnyView() { return mViews.Length() > 0 ? mViews[0] : nil; } - -private: - mozilla::LayoutDeviceIntRegion mRegion; - nsTArray<NSView*> mViews; -}; - -} // namespace mozilla - -#endif // ViewRegion_h diff --git a/widget/cocoa/ViewRegion.mm b/widget/cocoa/ViewRegion.mm deleted file mode 100644 index 455d54fdad..0000000000 --- a/widget/cocoa/ViewRegion.mm +++ /dev/null @@ -1,70 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; 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/. */ - -#include "ViewRegion.h" -#import <Cocoa/Cocoa.h> - -using namespace mozilla; - -ViewRegion::~ViewRegion() -{ - for (size_t i = 0; i < mViews.Length(); i++) { - [mViews[i] removeFromSuperview]; - } -} - -bool -ViewRegion::UpdateRegion(const LayoutDeviceIntRegion& aRegion, - const nsChildView& aCoordinateConverter, - NSView* aContainerView, - NSView* (^aViewCreationCallback)()) -{ - if (mRegion == aRegion) { - return false; - } - - // We need to construct the required region using as many EffectViews - // as necessary. We try to update the geometry of existing views if - // possible, or create new ones or remove old ones if the number of - // rects in the region has changed. - - nsTArray<NSView*> viewsToRecycle; - mViews.SwapElements(viewsToRecycle); - // The mViews array is now empty. - - size_t i = 0; - for (auto iter = aRegion.RectIter(); - !iter.Done() || i < viewsToRecycle.Length(); - i++) { - if (!iter.Done()) { - NSView* view = nil; - NSRect rect = aCoordinateConverter.DevPixelsToCocoaPoints(iter.Get()); - if (i < viewsToRecycle.Length()) { - view = viewsToRecycle[i]; - } else { - view = aViewCreationCallback(); - [aContainerView addSubview:view]; - - // Now that the view is in the view hierarchy, it'll be kept alive by - // its superview, so we can drop our reference. - [view release]; - } - if (!NSEqualRects(rect, [view frame])) { - [view setFrame:rect]; - } - [view setNeedsDisplay:YES]; - mViews.AppendElement(view); - iter.Next(); - } else { - // Our new region is made of fewer rects than the old region, so we can - // remove this view. We only have a weak reference to it, so removing it - // from the view hierarchy will release it. - [viewsToRecycle[i] removeFromSuperview]; - } - } - - mRegion = aRegion; - return true; -} diff --git a/widget/cocoa/WidgetTraceEvent.mm b/widget/cocoa/WidgetTraceEvent.mm deleted file mode 100644 index 7023a17ba0..0000000000 --- a/widget/cocoa/WidgetTraceEvent.mm +++ /dev/null @@ -1,85 +0,0 @@ -/* 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/. */ - -#include <Cocoa/Cocoa.h> -#include "CustomCocoaEvents.h" -#include <Foundation/NSAutoreleasePool.h> -#include <mozilla/CondVar.h> -#include <mozilla/Mutex.h> -#include "mozilla/WidgetTraceEvent.h" - -using mozilla::CondVar; -using mozilla::Mutex; -using mozilla::MutexAutoLock; - -namespace { - -Mutex* sMutex = NULL; -CondVar* sCondVar = NULL; -bool sTracerProcessed = false; - -} // namespace - -namespace mozilla { - -bool InitWidgetTracing() -{ - sMutex = new Mutex("Event tracer thread mutex"); - sCondVar = new CondVar(*sMutex, "Event tracer thread condvar"); - return sMutex && sCondVar; -} - -void CleanUpWidgetTracing() -{ - delete sMutex; - delete sCondVar; - sMutex = NULL; - sCondVar = NULL; -} - -// This function is called from the main (UI) thread. -void SignalTracerThread() -{ - if (!sMutex || !sCondVar) - return; - MutexAutoLock lock(*sMutex); - if (!sTracerProcessed) { - sTracerProcessed = true; - sCondVar->Notify(); - } -} - -// This function is called from the background tracer thread. -bool FireAndWaitForTracerEvent() -{ - MOZ_ASSERT(sMutex && sCondVar, "Tracing not initialized!"); - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - MutexAutoLock lock(*sMutex); - if (sTracerProcessed) { - // Things are out of sync. This is likely because we're in - // the middle of shutting down. Just return false and hope the - // tracer thread is quitting anyway. - return false; - } - - // Post an application-defined event to the main thread's event queue - // and wait for it to get processed. - [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined - location:NSMakePoint(0,0) - modifierFlags:0 - timestamp:0 - windowNumber:0 - context:NULL - subtype:kEventSubtypeTrace - data1:0 - data2:0] - atStart:NO]; - while (!sTracerProcessed) - sCondVar->Wait(); - sTracerProcessed = false; - [pool release]; - return true; -} - -} // namespace mozilla diff --git a/widget/cocoa/crashtests/373122-1-inner.html b/widget/cocoa/crashtests/373122-1-inner.html deleted file mode 100644 index 5c14166b75..0000000000 --- a/widget/cocoa/crashtests/373122-1-inner.html +++ /dev/null @@ -1,39 +0,0 @@ -<html> -<head> - -<script> -function boom() -{ - document.body.style.position = "fixed" - - setTimeout(boom2, 1); -} - -function boom2() -{ - lappy = document.getElementById("lappy"); - lappy.style.display = "none" - - setTimeout(boom3, 200); -} - -function boom3() -{ - dump("Reloading\n"); - location.reload(); -} - -</script> - - -</head> - - -<body bgcolor="black" onload="boom()"> - - <span style="overflow: scroll; display: -moz-box;"></span> - - <embed id="lappy" src="" width=550 height=400 TYPE="application/x-shockwave-flash" ></embed> - -</body> -</html> diff --git a/widget/cocoa/crashtests/373122-1.html b/widget/cocoa/crashtests/373122-1.html deleted file mode 100644 index a57e5f4249..0000000000 --- a/widget/cocoa/crashtests/373122-1.html +++ /dev/null @@ -1,9 +0,0 @@ -<html class="reftest-wait"> -<head> -<script> -setTimeout('document.documentElement.className = ""', 1000); -</script> -<body> -<iframe src="373122-1-inner.html"></iframe> -</body> -</html> diff --git a/widget/cocoa/crashtests/397209-1.html b/widget/cocoa/crashtests/397209-1.html deleted file mode 100644 index 554b2dac72..0000000000 --- a/widget/cocoa/crashtests/397209-1.html +++ /dev/null @@ -1,7 +0,0 @@ -<html> -<head> -</head> -<body> -<button style="width: 8205em;"></button> -</body> -</html> diff --git a/widget/cocoa/crashtests/403296-1.xhtml b/widget/cocoa/crashtests/403296-1.xhtml deleted file mode 100644 index 800eaa3558..0000000000 --- a/widget/cocoa/crashtests/403296-1.xhtml +++ /dev/null @@ -1,10 +0,0 @@ -<html xmlns="http://www.w3.org/1999/xhtml" - class="reftest-wait" - style="margin: 12em; padding: 20px 10em; opacity: 0.2; font-size: 11.2px; -moz-appearance: toolbar; white-space: nowrap;"><body - style="position: absolute;" - onload="setTimeout(function() { document.body.removeChild(document.getElementById('tr')); document.documentElement.removeAttribute('class'); }, 30);"> - -xxx -yyy - -<tr id="tr">300</tr></body></html> diff --git a/widget/cocoa/crashtests/419737-1.html b/widget/cocoa/crashtests/419737-1.html deleted file mode 100644 index fe6e4532b4..0000000000 --- a/widget/cocoa/crashtests/419737-1.html +++ /dev/null @@ -1,8 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -</head> -<body> -<div><span style="-moz-appearance: radio; padding: 15000px;"></span></div> -</body> -</html> diff --git a/widget/cocoa/crashtests/435223-1.html b/widget/cocoa/crashtests/435223-1.html deleted file mode 100644 index 1bbc27ba01..0000000000 --- a/widget/cocoa/crashtests/435223-1.html +++ /dev/null @@ -1,8 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -</head> -<body> -<div style="min-width: -moz-max-content;"><div style="-moz-appearance: button;"><div style="margin: 0 100%;"></div></div></div> -</body> -</html> diff --git a/widget/cocoa/crashtests/444260-1.xul b/widget/cocoa/crashtests/444260-1.xul deleted file mode 100644 index f1a84023df..0000000000 --- a/widget/cocoa/crashtests/444260-1.xul +++ /dev/null @@ -1,3 +0,0 @@ -<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> -<hbox><button width="7788025414616">S</button></hbox> -</window> diff --git a/widget/cocoa/crashtests/444864-1.html b/widget/cocoa/crashtests/444864-1.html deleted file mode 100644 index f8bac76e6a..0000000000 --- a/widget/cocoa/crashtests/444864-1.html +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE html> -<html> -<body> -<div style="padding: 10px;"><input type="button" value="Go" style="letter-spacing: 331989pt;"></div> -</body> -</html> diff --git a/widget/cocoa/crashtests/449111-1.html b/widget/cocoa/crashtests/449111-1.html deleted file mode 100644 index 4494591803..0000000000 --- a/widget/cocoa/crashtests/449111-1.html +++ /dev/null @@ -1,4 +0,0 @@ -<html> -<head></head> -<body><div style="display: -moz-box; word-spacing: 549755813889px;"><button>T </button></div></body> -</html> diff --git a/widget/cocoa/crashtests/460349-1.xhtml b/widget/cocoa/crashtests/460349-1.xhtml deleted file mode 100644 index cc9b9700c7..0000000000 --- a/widget/cocoa/crashtests/460349-1.xhtml +++ /dev/null @@ -1,4 +0,0 @@ -<html xmlns="http://www.w3.org/1999/xhtml"> -<head></head> -<body><div><mstyle xmlns="http://www.w3.org/1998/Math/MathML" style="-moz-appearance: button;"/></div></body> -</html> diff --git a/widget/cocoa/crashtests/460387-1.html b/widget/cocoa/crashtests/460387-1.html deleted file mode 100644 index cab7e7eb32..0000000000 --- a/widget/cocoa/crashtests/460387-1.html +++ /dev/null @@ -1,2 +0,0 @@ -<!DOCTYPE html> -<html><head></head><body><div style="display: table; padding: 625203mm; -moz-appearance: menulist;"></div></body></html> diff --git a/widget/cocoa/crashtests/464589-1.html b/widget/cocoa/crashtests/464589-1.html deleted file mode 100644 index d25d92315d..0000000000 --- a/widget/cocoa/crashtests/464589-1.html +++ /dev/null @@ -1,20 +0,0 @@ -<html> -<head> -<script type="text/javascript"> - -function boom() -{ - var o2 = document.createElement("option"); - document.getElementById("o1").appendChild(o2); - o2.style.padding = "131072cm"; -} - -</script> -</head> - -<body onload="boom();"> - -<select><option id="o1" style="height: 0cm;"></option></select> - -</body> -</html> diff --git a/widget/cocoa/crashtests/crashtests.list b/widget/cocoa/crashtests/crashtests.list deleted file mode 100644 index b65fe01394..0000000000 --- a/widget/cocoa/crashtests/crashtests.list +++ /dev/null @@ -1,11 +0,0 @@ -skip-if(!cocoaWidget) load 373122-1.html # bug 1300017 -load 397209-1.html -load 403296-1.xhtml -load 419737-1.html -load 435223-1.html -load 444260-1.xul -load 444864-1.html -load 449111-1.html -load 460349-1.xhtml -load 460387-1.html -load 464589-1.html diff --git a/widget/cocoa/cursors/arrowN.png b/widget/cocoa/cursors/arrowN.png Binary files differdeleted file mode 100644 index 5ca8ec5ac6..0000000000 --- a/widget/cocoa/cursors/arrowN.png +++ /dev/null diff --git a/widget/cocoa/cursors/arrowN@2x.png b/widget/cocoa/cursors/arrowN@2x.png Binary files differdeleted file mode 100644 index d00e87636c..0000000000 --- a/widget/cocoa/cursors/arrowN@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/arrowS.png b/widget/cocoa/cursors/arrowS.png Binary files differdeleted file mode 100644 index 9b2d19e0fd..0000000000 --- a/widget/cocoa/cursors/arrowS.png +++ /dev/null diff --git a/widget/cocoa/cursors/arrowS@2x.png b/widget/cocoa/cursors/arrowS@2x.png Binary files differdeleted file mode 100644 index 5d011c1fd1..0000000000 --- a/widget/cocoa/cursors/arrowS@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/cell.png b/widget/cocoa/cursors/cell.png Binary files differdeleted file mode 100644 index 5284eaec57..0000000000 --- a/widget/cocoa/cursors/cell.png +++ /dev/null diff --git a/widget/cocoa/cursors/cell@2x.png b/widget/cocoa/cursors/cell@2x.png Binary files differdeleted file mode 100644 index 5e6738cff7..0000000000 --- a/widget/cocoa/cursors/cell@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/colResize.png b/widget/cocoa/cursors/colResize.png Binary files differdeleted file mode 100644 index 4e3e19e223..0000000000 --- a/widget/cocoa/cursors/colResize.png +++ /dev/null diff --git a/widget/cocoa/cursors/colResize@2x.png b/widget/cocoa/cursors/colResize@2x.png Binary files differdeleted file mode 100644 index 6a92cf6806..0000000000 --- a/widget/cocoa/cursors/colResize@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/help.png b/widget/cocoa/cursors/help.png Binary files differdeleted file mode 100644 index 5e5416b4e3..0000000000 --- a/widget/cocoa/cursors/help.png +++ /dev/null diff --git a/widget/cocoa/cursors/help@2x.png b/widget/cocoa/cursors/help@2x.png Binary files differdeleted file mode 100644 index 0ac53a9733..0000000000 --- a/widget/cocoa/cursors/help@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/move.png b/widget/cocoa/cursors/move.png Binary files differdeleted file mode 100644 index 1360f82277..0000000000 --- a/widget/cocoa/cursors/move.png +++ /dev/null diff --git a/widget/cocoa/cursors/move@2x.png b/widget/cocoa/cursors/move@2x.png Binary files differdeleted file mode 100644 index ad146e4863..0000000000 --- a/widget/cocoa/cursors/move@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/rowResize.png b/widget/cocoa/cursors/rowResize.png Binary files differdeleted file mode 100644 index 4c16bb8bd6..0000000000 --- a/widget/cocoa/cursors/rowResize.png +++ /dev/null diff --git a/widget/cocoa/cursors/rowResize@2x.png b/widget/cocoa/cursors/rowResize@2x.png Binary files differdeleted file mode 100644 index b48f03ae0d..0000000000 --- a/widget/cocoa/cursors/rowResize@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/sizeNE.png b/widget/cocoa/cursors/sizeNE.png Binary files differdeleted file mode 100644 index f62c046575..0000000000 --- a/widget/cocoa/cursors/sizeNE.png +++ /dev/null diff --git a/widget/cocoa/cursors/sizeNE@2x.png b/widget/cocoa/cursors/sizeNE@2x.png Binary files differdeleted file mode 100644 index 98d19e9efa..0000000000 --- a/widget/cocoa/cursors/sizeNE@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/sizeNESW.png b/widget/cocoa/cursors/sizeNESW.png Binary files differdeleted file mode 100644 index 0a077fa674..0000000000 --- a/widget/cocoa/cursors/sizeNESW.png +++ /dev/null diff --git a/widget/cocoa/cursors/sizeNESW@2x.png b/widget/cocoa/cursors/sizeNESW@2x.png Binary files differdeleted file mode 100644 index 31bca3c901..0000000000 --- a/widget/cocoa/cursors/sizeNESW@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/sizeNS.png b/widget/cocoa/cursors/sizeNS.png Binary files differdeleted file mode 100644 index 0419be0af7..0000000000 --- a/widget/cocoa/cursors/sizeNS.png +++ /dev/null diff --git a/widget/cocoa/cursors/sizeNS@2x.png b/widget/cocoa/cursors/sizeNS@2x.png Binary files differdeleted file mode 100644 index e48fd0cb3a..0000000000 --- a/widget/cocoa/cursors/sizeNS@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/sizeNW.png b/widget/cocoa/cursors/sizeNW.png Binary files differdeleted file mode 100644 index 8f5faee5f7..0000000000 --- a/widget/cocoa/cursors/sizeNW.png +++ /dev/null diff --git a/widget/cocoa/cursors/sizeNW@2x.png b/widget/cocoa/cursors/sizeNW@2x.png Binary files differdeleted file mode 100644 index 3a80e7ce91..0000000000 --- a/widget/cocoa/cursors/sizeNW@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/sizeNWSE.png b/widget/cocoa/cursors/sizeNWSE.png Binary files differdeleted file mode 100644 index 0574a584c1..0000000000 --- a/widget/cocoa/cursors/sizeNWSE.png +++ /dev/null diff --git a/widget/cocoa/cursors/sizeNWSE@2x.png b/widget/cocoa/cursors/sizeNWSE@2x.png Binary files differdeleted file mode 100644 index 9a0a276c34..0000000000 --- a/widget/cocoa/cursors/sizeNWSE@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/sizeSE.png b/widget/cocoa/cursors/sizeSE.png Binary files differdeleted file mode 100644 index 6a1948f521..0000000000 --- a/widget/cocoa/cursors/sizeSE.png +++ /dev/null diff --git a/widget/cocoa/cursors/sizeSE@2x.png b/widget/cocoa/cursors/sizeSE@2x.png Binary files differdeleted file mode 100644 index 7d637f4be6..0000000000 --- a/widget/cocoa/cursors/sizeSE@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/sizeSW.png b/widget/cocoa/cursors/sizeSW.png Binary files differdeleted file mode 100644 index 5dd054dd46..0000000000 --- a/widget/cocoa/cursors/sizeSW.png +++ /dev/null diff --git a/widget/cocoa/cursors/sizeSW@2x.png b/widget/cocoa/cursors/sizeSW@2x.png Binary files differdeleted file mode 100644 index 5ac63c25c6..0000000000 --- a/widget/cocoa/cursors/sizeSW@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/vtIBeam.png b/widget/cocoa/cursors/vtIBeam.png Binary files differdeleted file mode 100644 index ee7528c595..0000000000 --- a/widget/cocoa/cursors/vtIBeam.png +++ /dev/null diff --git a/widget/cocoa/cursors/vtIBeam@2x.png b/widget/cocoa/cursors/vtIBeam@2x.png Binary files differdeleted file mode 100644 index 41c47af116..0000000000 --- a/widget/cocoa/cursors/vtIBeam@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/zoomIn.png b/widget/cocoa/cursors/zoomIn.png Binary files differdeleted file mode 100644 index 275bf1c69d..0000000000 --- a/widget/cocoa/cursors/zoomIn.png +++ /dev/null diff --git a/widget/cocoa/cursors/zoomIn@2x.png b/widget/cocoa/cursors/zoomIn@2x.png Binary files differdeleted file mode 100644 index fdd3f8e71d..0000000000 --- a/widget/cocoa/cursors/zoomIn@2x.png +++ /dev/null diff --git a/widget/cocoa/cursors/zoomOut.png b/widget/cocoa/cursors/zoomOut.png Binary files differdeleted file mode 100644 index 19d2d89125..0000000000 --- a/widget/cocoa/cursors/zoomOut.png +++ /dev/null diff --git a/widget/cocoa/cursors/zoomOut@2x.png b/widget/cocoa/cursors/zoomOut@2x.png Binary files differdeleted file mode 100644 index 0ed46ce75e..0000000000 --- a/widget/cocoa/cursors/zoomOut@2x.png +++ /dev/null diff --git a/widget/cocoa/moz.build b/widget/cocoa/moz.build deleted file mode 100644 index c4f887297d..0000000000 --- a/widget/cocoa/moz.build +++ /dev/null @@ -1,140 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# 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/. - -XPIDL_SOURCES += [ - 'nsPIWidgetCocoa.idl', -] - -XPIDL_MODULE = 'widget_cocoa' - -EXPORTS += [ - 'mozView.h', - 'nsBidiKeyboard.h', - 'nsChangeObserver.h', - 'nsCocoaDebugUtils.h', - 'nsCocoaFeatures.h', - 'nsCocoaUtils.h', -] - -UNIFIED_SOURCES += [ - 'ComplexTextInputPanel.mm', - 'GfxInfo.mm', - 'NativeKeyBindings.mm', - 'nsAppShell.mm', - 'nsBidiKeyboard.mm', - 'nsCocoaFeatures.mm', - 'nsCocoaUtils.mm', - 'nsCocoaWindow.mm', - 'nsColorPicker.mm', - 'nsCursorManager.mm', - 'nsDeviceContextSpecX.mm', - 'nsFilePicker.mm', - 'nsIdleServiceX.mm', - 'nsLookAndFeel.mm', - 'nsMacCursor.mm', - 'nsMacDockSupport.mm', - 'nsMacWebAppUtils.mm', - 'nsMenuBarX.mm', - 'nsMenuGroupOwnerX.mm', - 'nsMenuItemIconX.mm', - 'nsMenuItemX.mm', - 'nsMenuUtilsX.mm', - 'nsMenuX.mm', - 'nsPrintDialogX.mm', - 'nsPrintOptionsX.mm', - 'nsPrintSettingsX.mm', - 'nsScreenCocoa.mm', - 'nsScreenManagerCocoa.mm', - 'nsSound.mm', - 'nsStandaloneNativeMenu.mm', - 'nsSystemStatusBarCocoa.mm', - 'nsToolkit.mm', - 'nsWidgetFactory.mm', - 'nsWindowMap.mm', - 'OSXNotificationCenter.mm', - 'RectTextureImage.mm', - 'SwipeTracker.mm', - 'TextInputHandler.mm', - 'VibrancyManager.mm', - 'ViewRegion.mm', - 'WidgetTraceEvent.mm', -] - -# These files cannot be built in unified mode because they cause symbol conflicts -SOURCES += [ - 'nsChildView.mm', - 'nsClipboard.mm', - 'nsCocoaDebugUtils.mm', - 'nsDragService.mm', - 'nsNativeThemeCocoa.mm', -] - -if not CONFIG['RELEASE_OR_BETA'] or CONFIG['MOZ_DEBUG']: - SOURCES += [ - 'nsSandboxViolationSink.mm', - ] - -include('/ipc/chromium/chromium-config.mozbuild') - -# XXX: We should fix these warnings. -ALLOW_COMPILER_WARNINGS = True - -FINAL_LIBRARY = 'xul' -LOCAL_INCLUDES += [ - '/layout/forms', - '/layout/generic', - '/layout/style', - '/layout/xul', - '/widget', -] - -RESOURCE_FILES.cursors += [ - 'cursors/arrowN.png', - 'cursors/arrowN@2x.png', - 'cursors/arrowS.png', - 'cursors/arrowS@2x.png', - 'cursors/cell.png', - 'cursors/cell@2x.png', - 'cursors/colResize.png', - 'cursors/colResize@2x.png', - 'cursors/help.png', - 'cursors/help@2x.png', - 'cursors/move.png', - 'cursors/move@2x.png', - 'cursors/rowResize.png', - 'cursors/rowResize@2x.png', - 'cursors/sizeNE.png', - 'cursors/sizeNE@2x.png', - 'cursors/sizeNESW.png', - 'cursors/sizeNESW@2x.png', - 'cursors/sizeNS.png', - 'cursors/sizeNS@2x.png', - 'cursors/sizeNW.png', - 'cursors/sizeNW@2x.png', - 'cursors/sizeNWSE.png', - 'cursors/sizeNWSE@2x.png', - 'cursors/sizeSE.png', - 'cursors/sizeSE@2x.png', - 'cursors/sizeSW.png', - 'cursors/sizeSW@2x.png', - 'cursors/vtIBeam.png', - 'cursors/vtIBeam@2x.png', - 'cursors/zoomIn.png', - 'cursors/zoomIn@2x.png', - 'cursors/zoomOut.png', - 'cursors/zoomOut@2x.png', -] - -# These resources go in $(DIST)/bin/res/MainMenu.nib, but we can't use a magic -# RESOURCE_FILES.MainMenu.nib attribute, since that would put the files in -# $(DIST)/bin/res/MainMenu/nib. Instead, we call __setattr__ directly to create -# an attribute with the correct name. -RESOURCE_FILES.__setattr__('MainMenu.nib', [ - 'resources/MainMenu.nib/classes.nib', - 'resources/MainMenu.nib/info.nib', - 'resources/MainMenu.nib/keyedobjects.nib', -]) - -CXXFLAGS += CONFIG['TK_CFLAGS'] diff --git a/widget/cocoa/mozView.h b/widget/cocoa/mozView.h deleted file mode 100644 index bd054893ba..0000000000 --- a/widget/cocoa/mozView.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- 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 mozView_h_ -#define mozView_h_ - -#undef DARWIN -#import <Cocoa/Cocoa.h> -class nsIWidget; - -namespace mozilla { -namespace widget{ -class TextInputHandler; -} // namespace widget -} // namespace mozilla - -// A protocol listing all the methods that an object which wants -// to live in gecko's widget hierarchy must implement. |nsChildView| -// makes assumptions that any NSView with which it comes in contact will -// implement this protocol. -@protocol mozView - - // aHandler is Gecko's default text input handler: It implements the - // NSTextInput protocol to handle key events. Don't make aHandler a - // strong reference -- that causes a memory leak. -- (void)installTextInputHandler:(mozilla::widget::TextInputHandler*)aHandler; -- (void)uninstallTextInputHandler; - - // access the nsIWidget associated with this view. DOES NOT ADDREF. -- (nsIWidget*)widget; - - // return a context menu for this view -- (NSMenu*)contextMenu; - - // called when our corresponding Gecko view goes away -- (void)widgetDestroyed; - -- (BOOL)isDragInProgress; - - // Checks whether the view is first responder or not -- (BOOL)isFirstResponder; - - // Call when you dispatch an event which may cause to open context menu. -- (void)maybeInitContextMenuTracking; - -@end - -// An informal protocol implemented by the NSWindow of the host application. -// -// It's used to prevent re-entrant calls to -makeKeyAndOrderFront: when gecko -// focus/activate events propagate out to the embedder's -// nsIEmbeddingSiteWindow::SetFocus implementation. -@interface NSObject(mozWindow) - -- (BOOL)suppressMakeKeyFront; -- (void)setSuppressMakeKeyFront:(BOOL)inSuppress; - -@end - -#endif // mozView_h_ diff --git a/widget/cocoa/nsAppShell.h b/widget/cocoa/nsAppShell.h deleted file mode 100644 index b7836b6391..0000000000 --- a/widget/cocoa/nsAppShell.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ -/* 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/. */ - -/* - * Runs the main native Cocoa run loop, interrupting it as needed to process - * Gecko events. - */ - -#ifndef nsAppShell_h_ -#define nsAppShell_h_ - -#include "nsBaseAppShell.h" -#include "nsTArray.h" - -// GeckoNSApplication -// -// Subclass of NSApplication for filtering out certain events. -@interface GeckoNSApplication : NSApplication -{ -} -@end - -@class AppShellDelegate; - -class nsAppShell : public nsBaseAppShell -{ -public: - NS_IMETHOD ResumeNative(void); - - nsAppShell(); - - nsresult Init(); - - NS_IMETHOD Run(void); - NS_IMETHOD Exit(void); - NS_IMETHOD OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait); - NS_IMETHOD AfterProcessNextEvent(nsIThreadInternal *aThread, - bool aEventWasProcessed); - - // public only to be visible to Objective-C code that must call it - void WillTerminate(); - -protected: - virtual ~nsAppShell(); - - virtual void ScheduleNativeEventCallback(); - virtual bool ProcessNextNativeEvent(bool aMayWait); - - static void ProcessGeckoEvents(void* aInfo); - -protected: - CFMutableArrayRef mAutoreleasePools; - - AppShellDelegate* mDelegate; - CFRunLoopRef mCFRunLoop; - CFRunLoopSourceRef mCFRunLoopSource; - - bool mRunningEventLoop; - bool mStarted; - bool mTerminated; - bool mSkippedNativeCallback; - bool mRunningCocoaEmbedded; - - int32_t mNativeEventCallbackDepth; - // Can be set from different threads, so must be modified atomically - int32_t mNativeEventScheduledDepth; -}; - -#endif // nsAppShell_h_ diff --git a/widget/cocoa/nsAppShell.mm b/widget/cocoa/nsAppShell.mm deleted file mode 100644 index 33ce8e742a..0000000000 --- a/widget/cocoa/nsAppShell.mm +++ /dev/null @@ -1,907 +0,0 @@ -/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ -/* 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/. */ - -/* - * Runs the main native Cocoa run loop, interrupting it as needed to process - * Gecko events. - */ - -#import <Cocoa/Cocoa.h> - -#include "CustomCocoaEvents.h" -#include "mozilla/WidgetTraceEvent.h" -#include "nsAppShell.h" -#include "nsCOMPtr.h" -#include "nsIFile.h" -#include "nsDirectoryServiceDefs.h" -#include "nsString.h" -#include "nsIRollupListener.h" -#include "nsIWidget.h" -#include "nsThreadUtils.h" -#include "nsIWindowMediator.h" -#include "nsServiceManagerUtils.h" -#include "nsIInterfaceRequestor.h" -#include "nsIWebBrowserChrome.h" -#include "nsObjCExceptions.h" -#include "nsCocoaFeatures.h" -#include "nsCocoaUtils.h" -#include "nsChildView.h" -#include "nsToolkit.h" -#include "TextInputHandler.h" -#include "mozilla/HangMonitor.h" -#include "GeckoProfiler.h" -#include "pratom.h" -#if !defined(RELEASE_OR_BETA) || defined(DEBUG) -#include "nsSandboxViolationSink.h" -#endif - -#include <IOKit/pwr_mgt/IOPMLib.h> -#include "nsIDOMWakeLockListener.h" -#include "nsIPowerManagerService.h" - -using namespace mozilla::widget; - -// A wake lock listener that disables screen saver when requested by -// Gecko. For example when we're playing video in a foreground tab we -// don't want the screen saver to turn on. - -class MacWakeLockListener final : public nsIDOMMozWakeLockListener { -public: - NS_DECL_ISUPPORTS; - -private: - ~MacWakeLockListener() {} - - IOPMAssertionID mAssertionID = kIOPMNullAssertionID; - - NS_IMETHOD Callback(const nsAString& aTopic, const nsAString& aState) override { - if (!aTopic.EqualsASCII("screen")) { - return NS_OK; - } - // Note the wake lock code ensures that we're not sent duplicate - // "locked-foreground" notifications when multiple wake locks are held. - if (aState.EqualsASCII("locked-foreground")) { - // Prevent screen saver. - CFStringRef cf_topic = - ::CFStringCreateWithCharacters(kCFAllocatorDefault, - reinterpret_cast<const UniChar*> - (aTopic.Data()), - aTopic.Length()); - IOReturn success = - ::IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, - kIOPMAssertionLevelOn, - cf_topic, - &mAssertionID); - CFRelease(cf_topic); - if (success != kIOReturnSuccess) { - NS_WARNING("failed to disable screensaver"); - } - } else { - // Re-enable screen saver. - NS_WARNING("Releasing screensaver"); - if (mAssertionID != kIOPMNullAssertionID) { - IOReturn result = ::IOPMAssertionRelease(mAssertionID); - if (result != kIOReturnSuccess) { - NS_WARNING("failed to release screensaver"); - } - } - } - return NS_OK; - } -}; // MacWakeLockListener - -// defined in nsCocoaWindow.mm -extern int32_t gXULModalLevel; - -static bool gAppShellMethodsSwizzled = false; - -@implementation GeckoNSApplication - -- (void)sendEvent:(NSEvent *)anEvent -{ - mozilla::HangMonitor::NotifyActivity(); - if ([anEvent type] == NSApplicationDefined && - [anEvent subtype] == kEventSubtypeTrace) { - mozilla::SignalTracerThread(); - return; - } - [super sendEvent:anEvent]; -} - -#if defined(MAC_OS_X_VERSION_10_12) && \ - MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 && \ - __LP64__ -// 10.12 changed `mask` to NSEventMask (unsigned long long) for x86_64 builds. -- (NSEvent*)nextEventMatchingMask:(NSEventMask)mask -#else -- (NSEvent*)nextEventMatchingMask:(NSUInteger)mask -#endif - untilDate:(NSDate*)expiration - inMode:(NSString*)mode - dequeue:(BOOL)flag -{ - if (expiration) { - mozilla::HangMonitor::Suspend(); - } - NSEvent* nextEvent = [super nextEventMatchingMask:mask - untilDate:expiration inMode:mode dequeue:flag]; - if (expiration) { - mozilla::HangMonitor::NotifyActivity(); - } - return nextEvent; -} - -@end - -// AppShellDelegate -// -// Cocoa bridge class. An object of this class is registered to receive -// notifications. -// -@interface AppShellDelegate : NSObject -{ - @private - nsAppShell* mAppShell; -} - -- (id)initWithAppShell:(nsAppShell*)aAppShell; -- (void)applicationWillTerminate:(NSNotification*)aNotification; -- (void)beginMenuTracking:(NSNotification*)aNotification; -@end - -// nsAppShell implementation - -NS_IMETHODIMP -nsAppShell::ResumeNative(void) -{ - nsresult retval = nsBaseAppShell::ResumeNative(); - if (NS_SUCCEEDED(retval) && (mSuspendNativeCount == 0) && - mSkippedNativeCallback) - { - mSkippedNativeCallback = false; - ScheduleNativeEventCallback(); - } - return retval; -} - -nsAppShell::nsAppShell() -: mAutoreleasePools(nullptr) -, mDelegate(nullptr) -, mCFRunLoop(NULL) -, mCFRunLoopSource(NULL) -, mRunningEventLoop(false) -, mStarted(false) -, mTerminated(false) -, mSkippedNativeCallback(false) -, mNativeEventCallbackDepth(0) -, mNativeEventScheduledDepth(0) -{ - // A Cocoa event loop is running here if (and only if) we've been embedded - // by a Cocoa app. - mRunningCocoaEmbedded = [NSApp isRunning] ? true : false; -} - -nsAppShell::~nsAppShell() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (mCFRunLoop) { - if (mCFRunLoopSource) { - ::CFRunLoopRemoveSource(mCFRunLoop, mCFRunLoopSource, - kCFRunLoopCommonModes); - ::CFRelease(mCFRunLoopSource); - } - ::CFRelease(mCFRunLoop); - } - - if (mAutoreleasePools) { - NS_ASSERTION(::CFArrayGetCount(mAutoreleasePools) == 0, - "nsAppShell destroyed without popping all autorelease pools"); - ::CFRelease(mAutoreleasePools); - } - - [mDelegate release]; - - NS_OBJC_END_TRY_ABORT_BLOCK -} - -NS_IMPL_ISUPPORTS(MacWakeLockListener, nsIDOMMozWakeLockListener) -mozilla::StaticRefPtr<MacWakeLockListener> sWakeLockListener; - -static void -AddScreenWakeLockListener() -{ - nsCOMPtr<nsIPowerManagerService> sPowerManagerService = do_GetService( - POWERMANAGERSERVICE_CONTRACTID); - if (sPowerManagerService) { - sWakeLockListener = new MacWakeLockListener(); - sPowerManagerService->AddWakeLockListener(sWakeLockListener); - } else { - NS_WARNING("Failed to retrieve PowerManagerService, wakelocks will be broken!"); - } -} - -static void -RemoveScreenWakeLockListener() -{ - nsCOMPtr<nsIPowerManagerService> sPowerManagerService = do_GetService( - POWERMANAGERSERVICE_CONTRACTID); - if (sPowerManagerService) { - sPowerManagerService->RemoveWakeLockListener(sWakeLockListener); - sPowerManagerService = nullptr; - sWakeLockListener = nullptr; - } -} - -// An undocumented CoreGraphics framework method, present in the same form -// since at least OS X 10.5. -extern "C" CGError CGSSetDebugOptions(int options); - -// Init -// -// Loads the nib (see bug 316076c21) and sets up the CFRunLoopSource used to -// interrupt the main native run loop. -// -// public -nsresult -nsAppShell::Init() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // No event loop is running yet (unless an embedding app that uses - // NSApplicationMain() is running). - NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init]; - - // mAutoreleasePools is used as a stack of NSAutoreleasePool objects created - // by |this|. CFArray is used instead of NSArray because NSArray wants to - // retain each object you add to it, and you can't retain an - // NSAutoreleasePool. - mAutoreleasePools = ::CFArrayCreateMutable(nullptr, 0, nullptr); - NS_ENSURE_STATE(mAutoreleasePools); - - // Get the path of the nib file, which lives in the GRE location - nsCOMPtr<nsIFile> nibFile; - nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(nibFile)); - NS_ENSURE_SUCCESS(rv, rv); - - nibFile->AppendNative(NS_LITERAL_CSTRING("res")); - nibFile->AppendNative(NS_LITERAL_CSTRING("MainMenu.nib")); - - nsAutoCString nibPath; - rv = nibFile->GetNativePath(nibPath); - NS_ENSURE_SUCCESS(rv, rv); - - // This call initializes NSApplication unless: - // 1) we're using xre -- NSApp's already been initialized by - // MacApplicationDelegate.mm's EnsureUseCocoaDockAPI(). - // 2) an embedding app that uses NSApplicationMain() is running -- NSApp's - // already been initialized and its main run loop is already running. - [NSBundle loadNibFile: - [NSString stringWithUTF8String:(const char*)nibPath.get()] - externalNameTable: - [NSDictionary dictionaryWithObject:[GeckoNSApplication sharedApplication] - forKey:@"NSOwner"] - withZone:NSDefaultMallocZone()]; - - mDelegate = [[AppShellDelegate alloc] initWithAppShell:this]; - NS_ENSURE_STATE(mDelegate); - - // Add a CFRunLoopSource to the main native run loop. The source is - // responsible for interrupting the run loop when Gecko events are ready. - - mCFRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop]; - NS_ENSURE_STATE(mCFRunLoop); - ::CFRetain(mCFRunLoop); - - CFRunLoopSourceContext context; - bzero(&context, sizeof(context)); - // context.version = 0; - context.info = this; - context.perform = ProcessGeckoEvents; - - mCFRunLoopSource = ::CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); - NS_ENSURE_STATE(mCFRunLoopSource); - - ::CFRunLoopAddSource(mCFRunLoop, mCFRunLoopSource, kCFRunLoopCommonModes); - - rv = nsBaseAppShell::Init(); - - if (!gAppShellMethodsSwizzled) { - // We should only replace the original terminate: method if we're not - // running in a Cocoa embedder. See bug 604901. - if (!mRunningCocoaEmbedded) { - nsToolkit::SwizzleMethods([NSApplication class], @selector(terminate:), - @selector(nsAppShell_NSApplication_terminate:)); - } - gAppShellMethodsSwizzled = true; - } - - if (nsCocoaFeatures::OnYosemiteOrLater()) { - // Explicitly turn off CGEvent logging. This works around bug 1092855. - // If there are already CGEvents in the log, turning off logging also - // causes those events to be written to disk. But at this point no - // CGEvents have yet been processed. CGEvents are events (usually - // input events) pulled from the WindowServer. An option of 0x80000008 - // turns on CGEvent logging. - CGSSetDebugOptions(0x80000007); - } - -#if !defined(RELEASE_OR_BETA) || defined(DEBUG) - if (Preferences::GetBool("security.sandbox.mac.track.violations", false)) { - nsSandboxViolationSink::Start(); - } -#endif - - [localPool release]; - - return rv; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// ProcessGeckoEvents -// -// The "perform" target of mCFRunLoop, called when mCFRunLoopSource is -// signalled from ScheduleNativeEventCallback. -// -// Arrange for Gecko events to be processed on demand (in response to a call -// to ScheduleNativeEventCallback(), if processing of Gecko events via "native -// methods" hasn't been suspended). This happens in NativeEventCallback(). -// -// protected static -void -nsAppShell::ProcessGeckoEvents(void* aInfo) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - PROFILER_LABEL("Events", "ProcessGeckoEvents", - js::ProfileEntry::Category::EVENTS); - - nsAppShell* self = static_cast<nsAppShell*> (aInfo); - - if (self->mRunningEventLoop) { - self->mRunningEventLoop = false; - - // The run loop may be sleeping -- [NSRunLoop runMode:...] - // won't return until it's given a reason to wake up. Awaken it by - // posting a bogus event. There's no need to make the event - // presentable. - // - // But _don't_ set windowNumber to '-1' -- that can lead to nasty - // weirdness like bmo bug 397039 (a crash in [NSApp sendEvent:] on one of - // these fake events, because the -1 has gotten changed into the number - // of an actual NSWindow object, and that NSWindow object has just been - // destroyed). Setting windowNumber to '0' seems to work fine -- this - // seems to prevent the OS from ever trying to associate our bogus event - // with a particular NSWindow object. - [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined - location:NSMakePoint(0,0) - modifierFlags:0 - timestamp:0 - windowNumber:0 - context:NULL - subtype:kEventSubtypeNone - data1:0 - data2:0] - atStart:NO]; - } - - if (self->mSuspendNativeCount <= 0) { - ++self->mNativeEventCallbackDepth; - self->NativeEventCallback(); - --self->mNativeEventCallbackDepth; - } else { - self->mSkippedNativeCallback = true; - } - - // Still needed to avoid crashes on quit in most Mochitests. - [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined - location:NSMakePoint(0,0) - modifierFlags:0 - timestamp:0 - windowNumber:0 - context:NULL - subtype:kEventSubtypeNone - data1:0 - data2:0] - atStart:NO]; - - // Normally every call to ScheduleNativeEventCallback() results in - // exactly one call to ProcessGeckoEvents(). So each Release() here - // normally balances exactly one AddRef() in ScheduleNativeEventCallback(). - // But if Exit() is called just after ScheduleNativeEventCallback(), the - // corresponding call to ProcessGeckoEvents() will never happen. We check - // for this possibility in two different places -- here and in Exit() - // itself. If we find here that Exit() has been called (that mTerminated - // is true), it's because we've been called recursively, that Exit() was - // called from self->NativeEventCallback() above, and that we're unwinding - // the recursion. In this case we'll never be called again, and we balance - // here any extra calls to ScheduleNativeEventCallback(). - // - // When ProcessGeckoEvents() is called recursively, it's because of a - // call to ScheduleNativeEventCallback() from NativeEventCallback(). We - // balance the "extra" AddRefs here (rather than always in Exit()) in order - // to ensure that 'self' stays alive until the end of this method. We also - // make sure not to finish the balancing until all the recursion has been - // unwound. - if (self->mTerminated) { - int32_t releaseCount = 0; - if (self->mNativeEventScheduledDepth > self->mNativeEventCallbackDepth) { - releaseCount = PR_ATOMIC_SET(&self->mNativeEventScheduledDepth, - self->mNativeEventCallbackDepth); - } - while (releaseCount-- > self->mNativeEventCallbackDepth) - self->Release(); - } else { - // As best we can tell, every call to ProcessGeckoEvents() is triggered - // by a call to ScheduleNativeEventCallback(). But we've seen a few - // (non-reproducible) cases of double-frees that *might* have been caused - // by spontaneous calls (from the OS) to ProcessGeckoEvents(). So we - // deal with that possibility here. - if (PR_ATOMIC_DECREMENT(&self->mNativeEventScheduledDepth) < 0) { - PR_ATOMIC_SET(&self->mNativeEventScheduledDepth, 0); - NS_WARNING("Spontaneous call to ProcessGeckoEvents()!"); - } else { - self->Release(); - } - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// WillTerminate -// -// Called by the AppShellDelegate when an NSApplicationWillTerminate -// notification is posted. After this method is called, native events should -// no longer be processed. The NSApplicationWillTerminate notification is -// only posted when [NSApp terminate:] is called, which doesn't happen on a -// "normal" application quit. -// -// public -void -nsAppShell::WillTerminate() -{ - if (mTerminated) - return; - - // Make sure that the nsAppExitEvent posted by nsAppStartup::Quit() (called - // from [MacApplicationDelegate applicationShouldTerminate:]) gets run. - NS_ProcessPendingEvents(NS_GetCurrentThread()); - - mTerminated = true; -} - -// ScheduleNativeEventCallback -// -// Called (possibly on a non-main thread) when Gecko has an event that -// needs to be processed. The Gecko event needs to be processed on the -// main thread, so the native run loop must be interrupted. -// -// In nsBaseAppShell.cpp, the mNativeEventPending variable is used to -// ensure that ScheduleNativeEventCallback() is called no more than once -// per call to NativeEventCallback(). ProcessGeckoEvents() can skip its -// call to NativeEventCallback() if processing of Gecko events by native -// means is suspended (using nsIAppShell::SuspendNative()), which will -// suspend calls from nsBaseAppShell::OnDispatchedEvent() to -// ScheduleNativeEventCallback(). But when Gecko event processing by -// native means is resumed (in ResumeNative()), an extra call is made to -// ScheduleNativeEventCallback() (from ResumeNative()). This triggers -// another call to ProcessGeckoEvents(), which calls NativeEventCallback(), -// and nsBaseAppShell::OnDispatchedEvent() resumes calling -// ScheduleNativeEventCallback(). -// -// protected virtual -void -nsAppShell::ScheduleNativeEventCallback() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (mTerminated) - return; - - // Each AddRef() here is normally balanced by exactly one Release() in - // ProcessGeckoEvents(). But there are exceptions, for which see - // ProcessGeckoEvents() and Exit(). - NS_ADDREF_THIS(); - PR_ATOMIC_INCREMENT(&mNativeEventScheduledDepth); - - // This will invoke ProcessGeckoEvents on the main thread. - ::CFRunLoopSourceSignal(mCFRunLoopSource); - ::CFRunLoopWakeUp(mCFRunLoop); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// Undocumented Cocoa Event Manager function, present in the same form since -// at least OS X 10.6. -extern "C" EventAttributes GetEventAttributes(EventRef inEvent); - -// ProcessNextNativeEvent -// -// If aMayWait is false, process a single native event. If it is true, run -// the native run loop until stopped by ProcessGeckoEvents. -// -// Returns true if more events are waiting in the native event queue. -// -// protected virtual -bool -nsAppShell::ProcessNextNativeEvent(bool aMayWait) -{ - bool moreEvents = false; - - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - bool eventProcessed = false; - NSString* currentMode = nil; - - if (mTerminated) - return false; - - bool wasRunningEventLoop = mRunningEventLoop; - mRunningEventLoop = aMayWait; - NSDate* waitUntil = nil; - if (aMayWait) - waitUntil = [NSDate distantFuture]; - - NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop]; - - EventQueueRef currentEventQueue = GetCurrentEventQueue(); - EventTargetRef eventDispatcherTarget = GetEventDispatcherTarget(); - - if (aMayWait) { - mozilla::HangMonitor::Suspend(); - } - - // Only call -[NSApp sendEvent:] (and indirectly send user-input events to - // Gecko) if aMayWait is true. Tbis ensures most calls to -[NSApp - // sendEvent:] happen under nsAppShell::Run(), at the lowest level of - // recursion -- thereby making it less likely Gecko will process user-input - // events in the wrong order or skip some of them. It also avoids eating - // too much CPU in nsBaseAppShell::OnProcessNextEvent() (which calls - // us) -- thereby avoiding the starvation of nsIRunnable events in - // nsThread::ProcessNextEvent(). For more information see bug 996848. - do { - // No autorelease pool is provided here, because OnProcessNextEvent - // and AfterProcessNextEvent are responsible for maintaining it. - NS_ASSERTION(mAutoreleasePools && ::CFArrayGetCount(mAutoreleasePools), - "No autorelease pool for native event"); - - if (aMayWait) { - currentMode = [currentRunLoop currentMode]; - if (!currentMode) - currentMode = NSDefaultRunLoopMode; - NSEvent *nextEvent = [NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:waitUntil - inMode:currentMode - dequeue:YES]; - if (nextEvent) { - mozilla::HangMonitor::NotifyActivity(); - [NSApp sendEvent:nextEvent]; - eventProcessed = true; - } - } else { - // AcquireFirstMatchingEventInQueue() doesn't spin the (native) event - // loop, though it does queue up any newly available events from the - // window server. - EventRef currentEvent = AcquireFirstMatchingEventInQueue(currentEventQueue, 0, NULL, - kEventQueueOptionsNone); - if (!currentEvent) { - continue; - } - EventAttributes attrs = GetEventAttributes(currentEvent); - UInt32 eventKind = GetEventKind(currentEvent); - UInt32 eventClass = GetEventClass(currentEvent); - bool osCocoaEvent = - ((eventClass == 'appl') || (eventClass == kEventClassAppleEvent) || - ((eventClass == 'cgs ') && (eventKind != NSApplicationDefined))); - // If attrs is kEventAttributeUserEvent or kEventAttributeMonitored - // (i.e. a user input event), we shouldn't process it here while - // aMayWait is false. Likewise if currentEvent will eventually be - // turned into an OS-defined Cocoa event, or otherwise needs AppKit - // processing. Doing otherwise risks doing too much work here, and - // preventing the event from being properly processed by the AppKit - // framework. - if ((attrs != kEventAttributeNone) || osCocoaEvent) { - // Since we can't process the next event here (while aMayWait is false), - // we want moreEvents to be false on return. - eventProcessed = false; - // This call to ReleaseEvent() matches a call to RetainEvent() in - // AcquireFirstMatchingEventInQueue() above. - ReleaseEvent(currentEvent); - break; - } - // This call to RetainEvent() matches a call to ReleaseEvent() in - // RemoveEventFromQueue() below. - RetainEvent(currentEvent); - RemoveEventFromQueue(currentEventQueue, currentEvent); - SendEventToEventTarget(currentEvent, eventDispatcherTarget); - // This call to ReleaseEvent() matches a call to RetainEvent() in - // AcquireFirstMatchingEventInQueue() above. - ReleaseEvent(currentEvent); - eventProcessed = true; - } - } while (mRunningEventLoop); - - if (eventProcessed) { - moreEvents = - (AcquireFirstMatchingEventInQueue(currentEventQueue, 0, NULL, - kEventQueueOptionsNone) != NULL); - } - - mRunningEventLoop = wasRunningEventLoop; - - NS_OBJC_END_TRY_ABORT_BLOCK; - - if (!moreEvents) { - nsChildView::UpdateCurrentInputEventCount(); - } - - return moreEvents; -} - -// Run -// -// Overrides the base class's Run() method to call [NSApp run] (which spins -// the native run loop until the application quits). Since (unlike the base -// class's Run() method) we don't process any Gecko events here, they need -// to be processed elsewhere (in NativeEventCallback(), called from -// ProcessGeckoEvents()). -// -// Camino called [NSApp run] on its own (via NSApplicationMain()), and so -// didn't call nsAppShell::Run(). -// -// public -NS_IMETHODIMP -nsAppShell::Run(void) -{ - NS_ASSERTION(!mStarted, "nsAppShell::Run() called multiple times"); - if (mStarted || mTerminated) - return NS_OK; - - mStarted = true; - - AddScreenWakeLockListener(); - - NS_OBJC_TRY_ABORT([NSApp run]); - - RemoveScreenWakeLockListener(); - - return NS_OK; -} - -NS_IMETHODIMP -nsAppShell::Exit(void) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // This method is currently called more than once -- from (according to - // mento) an nsAppExitEvent dispatched by nsAppStartup::Quit() and from an - // XPCOM shutdown notification that nsBaseAppShell has registered to - // receive. So we need to ensure that multiple calls won't break anything. - // But we should also complain about it (since it isn't quite kosher). - if (mTerminated) { - NS_WARNING("nsAppShell::Exit() called redundantly"); - return NS_OK; - } - - mTerminated = true; - -#if !defined(RELEASE_OR_BETA) || defined(DEBUG) - nsSandboxViolationSink::Stop(); -#endif - - // Quoting from Apple's doc on the [NSApplication stop:] method (from their - // doc on the NSApplication class): "If this method is invoked during a - // modal event loop, it will break that loop but not the main event loop." - // nsAppShell::Exit() shouldn't be called from a modal event loop. So if - // it is we complain about it (to users of debug builds) and call [NSApp - // stop:] one extra time. (I'm not sure if modal event loops can be nested - // -- Apple's docs don't say one way or the other. But the return value - // of [NSApp _isRunningModal] doesn't change immediately after a call to - // [NSApp stop:], so we have to assume that one extra call to [NSApp stop:] - // will do the job.) - BOOL cocoaModal = [NSApp _isRunningModal]; - NS_ASSERTION(!cocoaModal, - "Don't call nsAppShell::Exit() from a modal event loop!"); - if (cocoaModal) - [NSApp stop:nullptr]; - [NSApp stop:nullptr]; - - // A call to Exit() just after a call to ScheduleNativeEventCallback() - // prevents the (normally) matching call to ProcessGeckoEvents() from - // happening. If we've been called from ProcessGeckoEvents() (as usually - // happens), we take care of it there. But if we have an unbalanced call - // to ScheduleNativeEventCallback() and ProcessGeckoEvents() isn't on the - // stack, we need to take care of the problem here. - if (!mNativeEventCallbackDepth && mNativeEventScheduledDepth) { - int32_t releaseCount = PR_ATOMIC_SET(&mNativeEventScheduledDepth, 0); - while (releaseCount-- > 0) - NS_RELEASE_THIS(); - } - - return nsBaseAppShell::Exit(); - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// OnProcessNextEvent -// -// This nsIThreadObserver method is called prior to processing an event. -// Set up an autorelease pool that will service any autoreleased Cocoa -// objects during this event. This includes native events processed by -// ProcessNextNativeEvent. The autorelease pool will be popped by -// AfterProcessNextEvent, it is important for these two methods to be -// tightly coupled. -// -// public -NS_IMETHODIMP -nsAppShell::OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NS_ASSERTION(mAutoreleasePools, - "No stack on which to store autorelease pool"); - - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - ::CFArrayAppendValue(mAutoreleasePools, pool); - - return nsBaseAppShell::OnProcessNextEvent(aThread, aMayWait); - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// AfterProcessNextEvent -// -// This nsIThreadObserver method is called after event processing is complete. -// The Cocoa implementation cleans up the autorelease pool create by the -// previous OnProcessNextEvent call. -// -// public -NS_IMETHODIMP -nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread, - bool aEventWasProcessed) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - CFIndex count = ::CFArrayGetCount(mAutoreleasePools); - - NS_ASSERTION(mAutoreleasePools && count, - "Processed an event, but there's no autorelease pool?"); - - const NSAutoreleasePool* pool = static_cast<const NSAutoreleasePool*> - (::CFArrayGetValueAtIndex(mAutoreleasePools, count - 1)); - ::CFArrayRemoveValueAtIndex(mAutoreleasePools, count - 1); - [pool release]; - - return nsBaseAppShell::AfterProcessNextEvent(aThread, aEventWasProcessed); - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - - -// AppShellDelegate implementation - - -@implementation AppShellDelegate -// initWithAppShell: -// -// Constructs the AppShellDelegate object -- (id)initWithAppShell:(nsAppShell*)aAppShell -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - if ((self = [self init])) { - mAppShell = aAppShell; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationWillTerminate:) - name:NSApplicationWillTerminateNotification - object:NSApp]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationDidBecomeActive:) - name:NSApplicationDidBecomeActiveNotification - object:NSApp]; - [[NSDistributedNotificationCenter defaultCenter] addObserver:self - selector:@selector(beginMenuTracking:) - name:@"com.apple.HIToolbox.beginMenuTrackingNotification" - object:nil]; - } - - return self; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (void)dealloc -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [[NSDistributedNotificationCenter defaultCenter] removeObserver:self]; - [super dealloc]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// applicationWillTerminate: -// -// Notify the nsAppShell that native event processing should be discontinued. -- (void)applicationWillTerminate:(NSNotification*)aNotification -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - mAppShell->WillTerminate(); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// applicationDidBecomeActive -// -// Make sure TextInputHandler::sLastModifierState is updated when we become -// active (since we won't have received [ChildView flagsChanged:] messages -// while inactive). -- (void)applicationDidBecomeActive:(NSNotification*)aNotification -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // [NSEvent modifierFlags] is valid on every kind of event, so we don't need - // to worry about getting an NSInternalInconsistencyException here. - NSEvent* currentEvent = [NSApp currentEvent]; - if (currentEvent) { - TextInputHandler::sLastModifierState = - [currentEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// beginMenuTracking -// -// Roll up our context menu (if any) when some other app (or the OS) opens -// any sort of menu. But make sure we don't do this for notifications we -// send ourselves (whose 'sender' will be @"org.mozilla.gecko.PopupWindow"). -- (void)beginMenuTracking:(NSNotification*)aNotification -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - NSString *sender = [aNotification object]; - if (!sender || ![sender isEqualToString:@"org.mozilla.gecko.PopupWindow"]) { - nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); - nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget(); - if (rollupWidget) - rollupListener->Rollup(0, true, nullptr, nullptr); - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -@end - -// We hook terminate: in order to make OS-initiated termination work nicely -// with Gecko's shutdown sequence. (Two ways to trigger OS-initiated -// termination: 1) Quit from the Dock menu; 2) Log out from (or shut down) -// your computer while the browser is active.) -@interface NSApplication (MethodSwizzling) -- (void)nsAppShell_NSApplication_terminate:(id)sender; -@end - -@implementation NSApplication (MethodSwizzling) - -// Called by the OS after [MacApplicationDelegate applicationShouldTerminate:] -// has returned NSTerminateNow. This method "subclasses" and replaces the -// OS's original implementation. The only thing the orginal method does which -// we need is that it posts NSApplicationWillTerminateNotification. Everything -// else is unneeded (because it's handled elsewhere), or actively interferes -// with Gecko's shutdown sequence. For example the original terminate: method -// causes the app to exit() inside [NSApp run] (called from nsAppShell::Run() -// above), which means that nothing runs after the call to nsAppStartup::Run() -// in XRE_Main(), which in particular means that ScopedXPCOMStartup's destructor -// and NS_ShutdownXPCOM() never get called. -- (void)nsAppShell_NSApplication_terminate:(id)sender -{ - [[NSNotificationCenter defaultCenter] postNotificationName:NSApplicationWillTerminateNotification - object:NSApp]; -} - -@end diff --git a/widget/cocoa/nsBidiKeyboard.h b/widget/cocoa/nsBidiKeyboard.h deleted file mode 100644 index e7e7ac8722..0000000000 --- a/widget/cocoa/nsBidiKeyboard.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- 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 nsBidiKeyboard_h_ -#define nsBidiKeyboard_h_ - -#include "nsIBidiKeyboard.h" - -class nsBidiKeyboard : public nsIBidiKeyboard -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIBIDIKEYBOARD - - nsBidiKeyboard(); - -protected: - virtual ~nsBidiKeyboard(); -}; - -#endif // nsBidiKeyboard_h_ diff --git a/widget/cocoa/nsBidiKeyboard.mm b/widget/cocoa/nsBidiKeyboard.mm deleted file mode 100644 index e0fc86aeb7..0000000000 --- a/widget/cocoa/nsBidiKeyboard.mm +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- 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/. */ - -#include "nsBidiKeyboard.h" -#include "nsCocoaUtils.h" -#include "TextInputHandler.h" - -// This must be the last include: -#include "nsObjCExceptions.h" - -using namespace mozilla::widget; - -NS_IMPL_ISUPPORTS(nsBidiKeyboard, nsIBidiKeyboard) - -nsBidiKeyboard::nsBidiKeyboard() : nsIBidiKeyboard() -{ - Reset(); -} - -nsBidiKeyboard::~nsBidiKeyboard() -{ -} - -NS_IMETHODIMP nsBidiKeyboard::Reset() -{ - return NS_OK; -} - -NS_IMETHODIMP nsBidiKeyboard::IsLangRTL(bool *aIsRTL) -{ - *aIsRTL = TISInputSourceWrapper::CurrentInputSource().IsForRTLLanguage(); - return NS_OK; -} - -NS_IMETHODIMP nsBidiKeyboard::GetHaveBidiKeyboards(bool* aResult) -{ - // not implemented yet - return NS_ERROR_NOT_IMPLEMENTED; -} diff --git a/widget/cocoa/nsChangeObserver.h b/widget/cocoa/nsChangeObserver.h deleted file mode 100644 index 1b9a001735..0000000000 --- a/widget/cocoa/nsChangeObserver.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- Mode: IDL; 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 nsChangeObserver_h_ -#define nsChangeObserver_h_ - -class nsIContent; -class nsIDocument; -class nsIAtom; - -#define NS_DECL_CHANGEOBSERVER \ -void ObserveAttributeChanged(nsIDocument *aDocument, nsIContent *aContent, nsIAtom *aAttribute) override; \ -void ObserveContentRemoved(nsIDocument *aDocument, nsIContent *aChild, int32_t aIndexInContainer) override; \ -void ObserveContentInserted(nsIDocument *aDocument, nsIContent* aContainer, nsIContent *aChild) override; - -// Something that wants to be alerted to changes in attributes or changes in -// its corresponding content object. -// -// This interface is used by our menu code so we only have to have one -// nsIDocumentObserver. -// -// Any class that implements this interface must take care to unregister itself -// on deletion. -class nsChangeObserver -{ -public: - // XXX use dom::Element - virtual void ObserveAttributeChanged(nsIDocument* aDocument, - nsIContent* aContent, - nsIAtom* aAttribute)=0; - - virtual void ObserveContentRemoved(nsIDocument* aDocument, - nsIContent* aChild, - int32_t aIndexInContainer)=0; - - virtual void ObserveContentInserted(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aChild)=0; -}; - -#endif // nsChangeObserver_h_ diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h deleted file mode 100644 index 2817c8d415..0000000000 --- a/widget/cocoa/nsChildView.h +++ /dev/null @@ -1,666 +0,0 @@ -/* -*- 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 nsChildView_h_ -#define nsChildView_h_ - -// formal protocols -#include "mozView.h" -#ifdef ACCESSIBILITY -#include "mozilla/a11y/Accessible.h" -#include "mozAccessibleProtocol.h" -#endif - -#include "nsISupports.h" -#include "nsBaseWidget.h" -#include "nsWeakPtr.h" -#include "TextInputHandler.h" -#include "nsCocoaUtils.h" -#include "gfxQuartzSurface.h" -#include "GLContextTypes.h" -#include "mozilla/Mutex.h" -#include "nsRegion.h" -#include "mozilla/MouseEvents.h" -#include "mozilla/UniquePtr.h" - -#include "nsString.h" -#include "nsIDragService.h" -#include "ViewRegion.h" - -#import <Carbon/Carbon.h> -#import <Cocoa/Cocoa.h> -#import <AppKit/NSOpenGL.h> - -class nsChildView; -class nsCocoaWindow; - -namespace { -class GLPresenter; -} // namespace - -namespace mozilla { -class InputData; -class PanGestureInput; -class SwipeTracker; -struct SwipeEventQueue; -class VibrancyManager; -namespace layers { -class GLManager; -class IAPZCTreeManager; -} // namespace layers -namespace widget { -class RectTextureImage; -class WidgetRenderingContext; -} // namespace widget -} // namespace mozilla - -@class PixelHostingView; - -@interface NSEvent (Undocumented) - -// Return Cocoa event's corresponding Carbon event. Not initialized (on -// synthetic events) until the OS actually "sends" the event. This method -// has been present in the same form since at least OS X 10.2.8. -- (EventRef)_eventRef; - -@end - -@interface NSView (Undocumented) - -// Draws the title string of a window. -// Present on NSThemeFrame since at least 10.6. -// _drawTitleBar is somewhat complex, and has changed over the years -// since OS X 10.6. But in that time it's never done anything that -// would break when called outside of -[NSView drawRect:] (which we -// sometimes do), or whose output can't be redirected to a -// CGContextRef object (which we also sometimes do). This is likely -// to remain true for the indefinite future. However we should -// check _drawTitleBar in each new major version of OS X. For more -// information see bug 877767. -- (void)_drawTitleBar:(NSRect)aRect; - -// Returns an NSRect that is the bounding box for all an NSView's dirty -// rectangles (ones that need to be redrawn). The full list of dirty -// rectangles can be obtained by calling -[NSView _dirtyRegion] and then -// calling -[NSRegion getRects:count:] on what it returns. Both these -// methods have been present in the same form since at least OS X 10.5. -// Unlike -[NSView getRectsBeingDrawn:count:], these methods can be called -// outside a call to -[NSView drawRect:]. -- (NSRect)_dirtyRect; - -// Undocumented method of one or more of NSFrameView's subclasses. Called -// when one or more of the titlebar buttons needs to be repositioned, to -// disappear, or to reappear (say if the window's style changes). If -// 'redisplay' is true, the entire titlebar (the window's top 22 pixels) is -// marked as needing redisplay. This method has been present in the same -// format since at least OS X 10.5. -- (void)_tileTitlebarAndRedisplay:(BOOL)redisplay; - -// The following undocumented methods are used to work around bug 1069658, -// which is an Apple bug or design flaw that effects Yosemite. None of them -// were present prior to Yosemite (OS X 10.10). -- (NSView *)titlebarView; // Method of NSThemeFrame -- (NSView *)titlebarContainerView; // Method of NSThemeFrame -- (BOOL)transparent; // Method of NSTitlebarView and NSTitlebarContainerView -- (void)setTransparent:(BOOL)transparent; // Method of NSTitlebarView and - // NSTitlebarContainerView - -@end - -@interface ChildView : NSView< -#ifdef ACCESSIBILITY - mozAccessible, -#endif - mozView, NSTextInputClient> -{ -@private - // the nsChildView that created the view. It retains this NSView, so - // the link back to it must be weak. - nsChildView* mGeckoChild; - - // Text input handler for mGeckoChild and us. Note that this is a weak - // reference. Ideally, this should be a strong reference but a ChildView - // object can live longer than the mGeckoChild that owns it. And if - // mTextInputHandler were a strong reference, this would make it difficult - // for Gecko's leak detector to detect leaked TextInputHandler objects. - // This is initialized by [mozView installTextInputHandler:aHandler] and - // cleared by [mozView uninstallTextInputHandler]. - mozilla::widget::TextInputHandler* mTextInputHandler; // [WEAK] - - // when mouseDown: is called, we store its event here (strong) - NSEvent* mLastMouseDownEvent; - - // Needed for IME support in e10s mode. Strong. - NSEvent* mLastKeyDownEvent; - - // Whether the last mouse down event was blocked from Gecko. - BOOL mBlockedLastMouseDown; - - // when acceptsFirstMouse: is called, we store the event here (strong) - NSEvent* mClickThroughMouseDownEvent; - - // WheelStart/Stop events should always come in pairs. This BOOL records the - // last received event so that, when we receive one of the events, we make sure - // to send its pair event first, in case we didn't yet for any reason. - BOOL mExpectingWheelStop; - - // Set to YES when our GL surface has been updated and we need to call - // updateGLContext before we composite. - BOOL mNeedsGLUpdate; - - // Holds our drag service across multiple drag calls. The reference to the - // service is obtained when the mouse enters the view and is released when - // the mouse exits or there is a drop. This prevents us from having to - // re-establish the connection to the service manager many times per second - // when handling |draggingUpdated:| messages. - nsIDragService* mDragService; - - NSOpenGLContext *mGLContext; - - // Simple gestures support - // - // mGestureState is used to detect when Cocoa has called both - // magnifyWithEvent and rotateWithEvent within the same - // beginGestureWithEvent and endGestureWithEvent sequence. We - // discard the spurious gesture event so as not to confuse Gecko. - // - // mCumulativeMagnification keeps track of the total amount of - // magnification peformed during a magnify gesture so that we can - // send that value with the final MozMagnifyGesture event. - // - // mCumulativeRotation keeps track of the total amount of rotation - // performed during a rotate gesture so we can send that value with - // the final MozRotateGesture event. - enum { - eGestureState_None, - eGestureState_StartGesture, - eGestureState_MagnifyGesture, - eGestureState_RotateGesture - } mGestureState; - float mCumulativeMagnification; - float mCumulativeRotation; - -#ifdef __LP64__ - // Support for fluid swipe tracking. - BOOL* mCancelSwipeAnimation; -#endif - - // Whether this uses off-main-thread compositing. - BOOL mUsingOMTCompositor; - - // The mask image that's used when painting into the titlebar using basic - // CGContext painting (i.e. non-accelerated). - CGImageRef mTopLeftCornerMask; - - // Subviews of self, which act as container views for vibrancy views and - // non-draggable views. - NSView* mVibrancyViewsContainer; // [STRONG] - NSView* mNonDraggableViewsContainer; // [STRONG] - - // The view that does our drawing. This is a subview of self so that it can - // be ordered on top of mVibrancyViewsContainer. - PixelHostingView* mPixelHostingView; -} - -// class initialization -+ (void)initialize; - -+ (void)registerViewForDraggedTypes:(NSView*)aView; - -// these are sent to the first responder when the window key status changes -- (void)viewsWindowDidBecomeKey; -- (void)viewsWindowDidResignKey; - -// Stop NSView hierarchy being changed during [ChildView drawRect:] -- (void)delayedTearDown; - -- (void)sendFocusEvent:(mozilla::EventMessage)eventMessage; - -- (void)handleMouseMoved:(NSEvent*)aEvent; - -- (void)sendMouseEnterOrExitEvent:(NSEvent*)aEvent - enter:(BOOL)aEnter - exitFrom:(mozilla::WidgetMouseEvent::ExitFrom)aExitFrom; - -- (void)updateGLContext; -- (void)_surfaceNeedsUpdate:(NSNotification*)notification; - -- (bool)preRender:(NSOpenGLContext *)aGLContext; -- (void)postRender:(NSOpenGLContext *)aGLContext; - -- (NSView*)vibrancyViewsContainer; -- (NSView*)nonDraggableViewsContainer; -- (NSView*)pixelHostingView; - -- (BOOL)isCoveringTitlebar; - -- (void)viewWillStartLiveResize; -- (void)viewDidEndLiveResize; - -- (NSColor*)vibrancyFillColorForThemeGeometryType:(nsITheme::ThemeGeometryType)aThemeGeometryType; -- (NSColor*)vibrancyFontSmoothingBackgroundColorForThemeGeometryType:(nsITheme::ThemeGeometryType)aThemeGeometryType; - -// Simple gestures support -// -// XXX - The swipeWithEvent, beginGestureWithEvent, magnifyWithEvent, -// rotateWithEvent, and endGestureWithEvent methods are part of a -// PRIVATE interface exported by nsResponder and reverse-engineering -// was necessary to obtain the methods' prototypes. Thus, Apple may -// change the interface in the future without notice. -// -// The prototypes were obtained from the following link: -// http://cocoadex.com/2008/02/nsevent-modifications-swipe-ro.html -- (void)swipeWithEvent:(NSEvent *)anEvent; -- (void)beginGestureWithEvent:(NSEvent *)anEvent; -- (void)magnifyWithEvent:(NSEvent *)anEvent; -- (void)smartMagnifyWithEvent:(NSEvent *)anEvent; -- (void)rotateWithEvent:(NSEvent *)anEvent; -- (void)endGestureWithEvent:(NSEvent *)anEvent; - -- (void)scrollWheel:(NSEvent *)anEvent; - -- (void)setUsingOMTCompositor:(BOOL)aUseOMTC; - -- (NSEvent*)lastKeyDownEvent; -@end - -class ChildViewMouseTracker { - -public: - - static void MouseMoved(NSEvent* aEvent); - static void MouseScrolled(NSEvent* aEvent); - static void OnDestroyView(ChildView* aView); - static void OnDestroyWindow(NSWindow* aWindow); - static BOOL WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent, - ChildView* aView, BOOL isClickThrough = NO); - static void MouseExitedWindow(NSEvent* aEvent); - static void MouseEnteredWindow(NSEvent* aEvent); - static void ReEvaluateMouseEnterState(NSEvent* aEvent = nil, ChildView* aOldView = nil); - static void ResendLastMouseMoveEvent(); - static ChildView* ViewForEvent(NSEvent* aEvent); - - static ChildView* sLastMouseEventView; - static NSEvent* sLastMouseMoveEvent; - static NSWindow* sWindowUnderMouse; - static NSPoint sLastScrollEventScreenLocation; -}; - -//------------------------------------------------------------------------- -// -// nsChildView -// -//------------------------------------------------------------------------- - -class nsChildView : public nsBaseWidget -{ -private: - typedef nsBaseWidget Inherited; - typedef mozilla::layers::IAPZCTreeManager IAPZCTreeManager; - -public: - nsChildView(); - - // nsIWidget interface - virtual MOZ_MUST_USE nsresult Create(nsIWidget* aParent, - nsNativeWidget aNativeParent, - const LayoutDeviceIntRect& aRect, - nsWidgetInitData* aInitData = nullptr) - override; - - virtual void Destroy() override; - - NS_IMETHOD Show(bool aState) override; - virtual bool IsVisible() const override; - - NS_IMETHOD SetParent(nsIWidget* aNewParent) override; - virtual nsIWidget* GetParent(void) override; - virtual float GetDPI() override; - - NS_IMETHOD Move(double aX, double aY) override; - NS_IMETHOD Resize(double aWidth, double aHeight, bool aRepaint) override; - NS_IMETHOD Resize(double aX, double aY, - double aWidth, double aHeight, bool aRepaint) override; - - NS_IMETHOD Enable(bool aState) override; - virtual bool IsEnabled() const override; - NS_IMETHOD SetFocus(bool aRaise) override; - virtual LayoutDeviceIntRect GetBounds() override; - virtual LayoutDeviceIntRect GetClientBounds() override; - virtual LayoutDeviceIntRect GetScreenBounds() override; - - // Returns the "backing scale factor" of the view's window, which is the - // ratio of pixels in the window's backing store to Cocoa points. Prior to - // HiDPI support in OS X 10.7, this was always 1.0, but in HiDPI mode it - // will be 2.0 (and might potentially other values as screen resolutions - // evolve). This gives the relationship between what Gecko calls "device - // pixels" and the Cocoa "points" coordinate system. - CGFloat BackingScaleFactor() const; - - mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() final { - return mozilla::DesktopToLayoutDeviceScale(BackingScaleFactor()); - } - - // Call if the window's backing scale factor changes - i.e., it is moved - // between HiDPI and non-HiDPI screens - void BackingScaleFactorChanged(); - - virtual double GetDefaultScaleInternal() override; - - virtual int32_t RoundsWidgetCoordinatesTo() override; - - NS_IMETHOD Invalidate(const LayoutDeviceIntRect &aRect) override; - - virtual void* GetNativeData(uint32_t aDataType) override; - virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations) override; - virtual LayoutDeviceIntPoint WidgetToScreenOffset() override; - virtual bool ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect) override; - - static bool ConvertStatus(nsEventStatus aStatus) - { return aStatus == nsEventStatus_eConsumeNoDefault; } - NS_IMETHOD DispatchEvent(mozilla::WidgetGUIEvent* aEvent, - nsEventStatus& aStatus) override; - - virtual bool WidgetTypeSupportsAcceleration() override; - virtual bool ShouldUseOffMainThreadCompositing() override; - - NS_IMETHOD SetCursor(nsCursor aCursor) override; - NS_IMETHOD SetCursor(imgIContainer* aCursor, uint32_t aHotspotX, uint32_t aHotspotY) override; - - NS_IMETHOD SetTitle(const nsAString& title) override; - - NS_IMETHOD GetAttention(int32_t aCycleCount) override; - - virtual bool HasPendingInputEvent() override; - - NS_IMETHOD ActivateNativeMenuItemAt(const nsAString& indexString) override; - NS_IMETHOD ForceUpdateNativeMenuAt(const nsAString& indexString) override; - NS_IMETHOD GetSelectionAsPlaintext(nsAString& aResult) override; - - NS_IMETHOD_(void) SetInputContext(const InputContext& aContext, - const InputContextAction& aAction) override; - NS_IMETHOD_(InputContext) GetInputContext() override; - NS_IMETHOD_(TextEventDispatcherListener*) - GetNativeTextEventDispatcherListener() override; - NS_IMETHOD AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent) override; - NS_IMETHOD_(bool) ExecuteNativeKeyBinding( - NativeKeyBindingsType aType, - const mozilla::WidgetKeyboardEvent& aEvent, - DoCommandCallback aCallback, - void* aCallbackData) override; - bool ExecuteNativeKeyBindingRemapped( - NativeKeyBindingsType aType, - const mozilla::WidgetKeyboardEvent& aEvent, - DoCommandCallback aCallback, - void* aCallbackData, - uint32_t aGeckoKeyCode, - uint32_t aCocoaKeyCode); - virtual nsIMEUpdatePreference GetIMEUpdatePreference() override; - - virtual nsTransparencyMode GetTransparencyMode() override; - virtual void SetTransparencyMode(nsTransparencyMode aMode) override; - - virtual nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout, - int32_t aNativeKeyCode, - uint32_t aModifierFlags, - const nsAString& aCharacters, - const nsAString& aUnmodifiedCharacters, - nsIObserver* aObserver) override; - - virtual nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint, - uint32_t aNativeMessage, - uint32_t aModifierFlags, - nsIObserver* aObserver) override; - - virtual nsresult SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint, - nsIObserver* aObserver) override - { return SynthesizeNativeMouseEvent(aPoint, NSMouseMoved, 0, aObserver); } - virtual nsresult SynthesizeNativeMouseScrollEvent(LayoutDeviceIntPoint aPoint, - uint32_t aNativeMessage, - double aDeltaX, - double aDeltaY, - double aDeltaZ, - uint32_t aModifierFlags, - uint32_t aAdditionalFlags, - nsIObserver* aObserver) override; - virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId, - TouchPointerState aPointerState, - LayoutDeviceIntPoint aPoint, - double aPointerPressure, - uint32_t aPointerOrientation, - nsIObserver* aObserver) override; - - // Mac specific methods - - virtual bool DispatchWindowEvent(mozilla::WidgetGUIEvent& event); - - void WillPaintWindow(); - bool PaintWindow(LayoutDeviceIntRegion aRegion); - bool PaintWindowInContext(CGContextRef aContext, const LayoutDeviceIntRegion& aRegion, - mozilla::gfx::IntSize aSurfaceSize); - -#ifdef ACCESSIBILITY - already_AddRefed<mozilla::a11y::Accessible> GetDocumentAccessible(); -#endif - - virtual void CreateCompositor() override; - virtual void PrepareWindowEffects() override; - virtual void CleanupWindowEffects() override; - virtual bool PreRender(mozilla::widget::WidgetRenderingContext* aContext) override; - virtual void PostRender(mozilla::widget::WidgetRenderingContext* aContext) override; - virtual void DrawWindowOverlay(mozilla::widget::WidgetRenderingContext* aManager, - LayoutDeviceIntRect aRect) override; - - virtual void UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) override; - - virtual void UpdateWindowDraggingRegion(const LayoutDeviceIntRegion& aRegion) override; - LayoutDeviceIntRegion GetNonDraggableRegion() { return mNonDraggableRegion.Region(); } - - virtual void ReportSwipeStarted(uint64_t aInputBlockId, bool aStartSwipe) override; - - virtual void LookUpDictionary( - const nsAString& aText, - const nsTArray<mozilla::FontRange>& aFontRangeArray, - const bool aIsVertical, - const LayoutDeviceIntPoint& aPoint) override; - - void ResetParent(); - - static bool DoHasPendingInputEvent(); - static uint32_t GetCurrentInputEventCount(); - static void UpdateCurrentInputEventCount(); - - NSView<mozView>* GetEditorView(); - - nsCocoaWindow* GetXULWindowWidget(); - - virtual void ReparentNativeWidget(nsIWidget* aNewParent) override; - - mozilla::widget::TextInputHandler* GetTextInputHandler() - { - return mTextInputHandler; - } - - void ClearVibrantAreas(); - NSColor* VibrancyFillColorForThemeGeometryType(nsITheme::ThemeGeometryType aThemeGeometryType); - NSColor* VibrancyFontSmoothingBackgroundColorForThemeGeometryType(nsITheme::ThemeGeometryType aThemeGeometryType); - - // unit conversion convenience functions - int32_t CocoaPointsToDevPixels(CGFloat aPts) const { - return nsCocoaUtils::CocoaPointsToDevPixels(aPts, BackingScaleFactor()); - } - LayoutDeviceIntPoint CocoaPointsToDevPixels(const NSPoint& aPt) const { - return nsCocoaUtils::CocoaPointsToDevPixels(aPt, BackingScaleFactor()); - } - LayoutDeviceIntPoint CocoaPointsToDevPixelsRoundDown(const NSPoint& aPt) const { - return nsCocoaUtils::CocoaPointsToDevPixelsRoundDown(aPt, BackingScaleFactor()); - } - LayoutDeviceIntRect CocoaPointsToDevPixels(const NSRect& aRect) const { - return nsCocoaUtils::CocoaPointsToDevPixels(aRect, BackingScaleFactor()); - } - CGFloat DevPixelsToCocoaPoints(int32_t aPixels) const { - return nsCocoaUtils::DevPixelsToCocoaPoints(aPixels, BackingScaleFactor()); - } - NSRect DevPixelsToCocoaPoints(const LayoutDeviceIntRect& aRect) const { - return nsCocoaUtils::DevPixelsToCocoaPoints(aRect, BackingScaleFactor()); - } - - already_AddRefed<mozilla::gfx::DrawTarget> - StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion, - mozilla::layers::BufferMode* aBufferMode) override; - void EndRemoteDrawing() override; - void CleanupRemoteDrawing() override; - bool InitCompositor(mozilla::layers::Compositor* aCompositor) override; - - NS_IMETHOD StartPluginIME(const mozilla::WidgetKeyboardEvent& aKeyboardEvent, - int32_t aPanelX, int32_t aPanelY, - nsString& aCommitted) override; - - virtual void SetPluginFocused(bool& aFocused) override; - - bool IsPluginFocused() { return mPluginFocused; } - - virtual LayoutDeviceIntPoint GetClientOffset() override; - - void DispatchAPZWheelInputEvent(mozilla::InputData& aEvent, bool aCanTriggerSwipe); - - void SwipeFinished(); - -protected: - virtual ~nsChildView(); - - void ReportMoveEvent(); - void ReportSizeEvent(); - - void TearDownView(); - - virtual already_AddRefed<nsIWidget> - AllocateChildPopupWidget() override - { - static NS_DEFINE_IID(kCPopUpCID, NS_POPUP_CID); - nsCOMPtr<nsIWidget> widget = do_CreateInstance(kCPopUpCID); - return widget.forget(); - } - - void ConfigureAPZCTreeManager() override; - void ConfigureAPZControllerThread() override; - - void DoRemoteComposition(const LayoutDeviceIntRect& aRenderRect); - - // Overlay drawing functions for OpenGL drawing - void DrawWindowOverlay(mozilla::layers::GLManager* aManager, LayoutDeviceIntRect aRect); - void MaybeDrawResizeIndicator(mozilla::layers::GLManager* aManager); - void MaybeDrawRoundedCorners(mozilla::layers::GLManager* aManager, const LayoutDeviceIntRect& aRect); - void MaybeDrawTitlebar(mozilla::layers::GLManager* aManager); - - // Redraw the contents of mTitlebarCGContext on the main thread, as - // determined by mDirtyTitlebarRegion. - void UpdateTitlebarCGContext(); - - LayoutDeviceIntRect RectContainingTitlebarControls(); - void UpdateVibrancy(const nsTArray<ThemeGeometry>& aThemeGeometries); - mozilla::VibrancyManager& EnsureVibrancyManager(); - - nsIWidget* GetWidgetForListenerEvents(); - - struct SwipeInfo { - bool wantsSwipe; - uint32_t allowedDirections; - }; - - SwipeInfo SendMayStartSwipe(const mozilla::PanGestureInput& aSwipeStartEvent); - void TrackScrollEventAsSwipe(const mozilla::PanGestureInput& aSwipeStartEvent, - uint32_t aAllowedDirections); - -protected: - - ChildView<mozView>* mView; // my parallel cocoa view, [STRONG] - RefPtr<mozilla::widget::TextInputHandler> mTextInputHandler; - InputContext mInputContext; - - NSView<mozView>* mParentView; - nsIWidget* mParentWidget; - -#ifdef ACCESSIBILITY - // weak ref to this childview's associated mozAccessible for speed reasons - // (we get queried for it *a lot* but don't want to own it) - nsWeakPtr mAccessible; -#endif - - // Protects the view from being teared down while a composition is in - // progress on the compositor thread. - mozilla::Mutex mViewTearDownLock; - - mozilla::Mutex mEffectsLock; - - // May be accessed from any thread, protected - // by mEffectsLock. - bool mShowsResizeIndicator; - LayoutDeviceIntRect mResizeIndicatorRect; - bool mHasRoundedBottomCorners; - int mDevPixelCornerRadius; - bool mIsCoveringTitlebar; - bool mIsFullscreen; - bool mIsOpaque; - LayoutDeviceIntRect mTitlebarRect; - - // The area of mTitlebarCGContext that needs to be redrawn during the next - // transaction. Accessed from any thread, protected by mEffectsLock. - LayoutDeviceIntRegion mUpdatedTitlebarRegion; - CGContextRef mTitlebarCGContext; - - // Compositor thread only - mozilla::UniquePtr<mozilla::widget::RectTextureImage> mResizerImage; - mozilla::UniquePtr<mozilla::widget::RectTextureImage> mCornerMaskImage; - mozilla::UniquePtr<mozilla::widget::RectTextureImage> mTitlebarImage; - mozilla::UniquePtr<mozilla::widget::RectTextureImage> mBasicCompositorImage; - - // The area of mTitlebarCGContext that has changed and needs to be - // uploaded to to mTitlebarImage. Main thread only. - nsIntRegion mDirtyTitlebarRegion; - - mozilla::ViewRegion mNonDraggableRegion; - - // Cached value of [mView backingScaleFactor], to avoid sending two obj-c - // messages (respondsToSelector, backingScaleFactor) every time we need to - // use it. - // ** We'll need to reinitialize this if the backing resolution changes. ** - mutable CGFloat mBackingScaleFactor; - - bool mVisible; - bool mDrawing; - bool mIsDispatchPaint; // Is a paint event being dispatched - - bool mPluginFocused; - - // Used in OMTC BasicLayers mode. Presents the BasicCompositor result - // surface to the screen using an OpenGL context. - mozilla::UniquePtr<GLPresenter> mGLPresenter; - - mozilla::UniquePtr<mozilla::VibrancyManager> mVibrancyManager; - RefPtr<mozilla::SwipeTracker> mSwipeTracker; - mozilla::UniquePtr<mozilla::SwipeEventQueue> mSwipeEventQueue; - - // Only used for drawRect-based painting in popups. - RefPtr<mozilla::gfx::DrawTarget> mBackingSurface; - - // This flag is only used when APZ is off. It indicates that the current pan - // gesture was processed as a swipe. Sometimes the swipe animation can finish - // before momentum events of the pan gesture have stopped firing, so this - // flag tells us that we shouldn't allow the remaining events to cause - // scrolling. It is reset to false once a new gesture starts (as indicated by - // a PANGESTURE_(MAY)START event). - bool mCurrentPanGestureBelongsToSwipe; - - static uint32_t sLastInputEventCount; - - void ReleaseTitlebarCGContext(); - - // This is used by SynthesizeNativeTouchPoint to maintain state between - // multiple synthesized points - mozilla::UniquePtr<mozilla::MultiTouchInput> mSynthesizedTouchInput; -}; - -#endif // nsChildView_h_ diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm deleted file mode 100644 index 868687fe16..0000000000 --- a/widget/cocoa/nsChildView.mm +++ /dev/null @@ -1,6151 +0,0 @@ -/* -*- Mode: objc; 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/. */ - -#include "mozilla/ArrayUtils.h" - -#include "mozilla/Logging.h" - -#include <unistd.h> -#include <math.h> - -#include "nsChildView.h" -#include "nsCocoaWindow.h" - -#include "mozilla/MiscEvents.h" -#include "mozilla/MouseEvents.h" -#include "mozilla/TextEvents.h" -#include "mozilla/TouchEvents.h" - -#include "nsArrayUtils.h" -#include "nsObjCExceptions.h" -#include "nsCOMPtr.h" -#include "nsToolkit.h" -#include "nsCRT.h" - -#include "nsFontMetrics.h" -#include "nsIRollupListener.h" -#include "nsViewManager.h" -#include "nsIInterfaceRequestor.h" -#include "nsIFile.h" -#include "nsILocalFileMac.h" -#include "nsGfxCIID.h" -#include "nsIDOMSimpleGestureEvent.h" -#include "nsThemeConstants.h" -#include "nsIWidgetListener.h" -#include "nsIPresShell.h" - -#include "nsDragService.h" -#include "nsClipboard.h" -#include "nsCursorManager.h" -#include "nsWindowMap.h" -#include "nsCocoaFeatures.h" -#include "nsCocoaUtils.h" -#include "nsMenuUtilsX.h" -#include "nsMenuBarX.h" -#include "NativeKeyBindings.h" -#include "ComplexTextInputPanel.h" - -#include "gfxContext.h" -#include "gfxQuartzSurface.h" -#include "gfxUtils.h" -#include "nsRegion.h" -#include "Layers.h" -#include "ClientLayerManager.h" -#include "mozilla/layers/LayerManagerComposite.h" -#include "GfxTexturesReporter.h" -#include "GLTextureImage.h" -#include "GLContextProvider.h" -#include "GLContextCGL.h" -#include "ScopedGLHelpers.h" -#include "HeapCopyOfStackArray.h" -#include "mozilla/layers/IAPZCTreeManager.h" -#include "mozilla/layers/APZThreadUtils.h" -#include "mozilla/layers/GLManager.h" -#include "mozilla/layers/CompositorOGL.h" -#include "mozilla/layers/CompositorBridgeParent.h" -#include "mozilla/layers/BasicCompositor.h" -#include "mozilla/layers/InputAPZContext.h" -#include "mozilla/widget/CompositorWidget.h" -#include "gfxUtils.h" -#include "gfxPrefs.h" -#include "mozilla/gfx/2D.h" -#include "mozilla/gfx/BorrowedContext.h" -#include "mozilla/gfx/MacIOSurface.h" -#ifdef ACCESSIBILITY -#include "nsAccessibilityService.h" -#include "mozilla/a11y/Platform.h" -#endif - -#include "mozilla/Preferences.h" - -#include <dlfcn.h> - -#include <ApplicationServices/ApplicationServices.h> - -#include "GeckoProfiler.h" - -#include "nsIDOMWheelEvent.h" -#include "mozilla/layers/ChromeProcessController.h" -#include "nsLayoutUtils.h" -#include "InputData.h" -#include "RectTextureImage.h" -#include "SwipeTracker.h" -#include "VibrancyManager.h" -#include "nsNativeThemeCocoa.h" -#include "nsIDOMWindowUtils.h" -#include "Units.h" -#include "UnitTransforms.h" -#include "mozilla/UniquePtrExtensions.h" - -using namespace mozilla; -using namespace mozilla::layers; -using namespace mozilla::gl; -using namespace mozilla::widget; - -using mozilla::gfx::Matrix4x4; - -#undef DEBUG_UPDATE -#undef INVALIDATE_DEBUGGING // flash areas as they are invalidated - -// Don't put more than this many rects in the dirty region, just fluff -// out to the bounding-box if there are more -#define MAX_RECTS_IN_REGION 100 - -PRLogModuleInfo* sCocoaLog = nullptr; - -extern "C" { - CG_EXTERN void CGContextResetCTM(CGContextRef); - CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform); - CG_EXTERN void CGContextResetClip(CGContextRef); - - typedef CFTypeRef CGSRegionObj; - CGError CGSNewRegionWithRect(const CGRect *rect, CGSRegionObj *outRegion); - CGError CGSNewRegionWithRectList(const CGRect *rects, int rectCount, CGSRegionObj *outRegion); -} - -// defined in nsMenuBarX.mm -extern NSMenu* sApplicationMenu; // Application menu shared by all menubars - -static bool gChildViewMethodsSwizzled = false; - -extern nsIArray *gDraggedTransferables; - -ChildView* ChildViewMouseTracker::sLastMouseEventView = nil; -NSEvent* ChildViewMouseTracker::sLastMouseMoveEvent = nil; -NSWindow* ChildViewMouseTracker::sWindowUnderMouse = nil; -NSPoint ChildViewMouseTracker::sLastScrollEventScreenLocation = NSZeroPoint; - -#ifdef INVALIDATE_DEBUGGING -static void blinkRect(Rect* r); -static void blinkRgn(RgnHandle rgn); -#endif - -bool gUserCancelledDrag = false; - -uint32_t nsChildView::sLastInputEventCount = 0; - -// The view that will do our drawing or host our NSOpenGLContext or Core Animation layer. -@interface PixelHostingView : NSView { -} -@end - -@interface ChildView(Private) - -// sets up our view, attaching it to its owning gecko view -- (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild; - -// set up a gecko mouse event based on a cocoa mouse event -- (void) convertCocoaMouseWheelEvent:(NSEvent*)aMouseEvent - toGeckoEvent:(WidgetWheelEvent*)outWheelEvent; -- (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent - toGeckoEvent:(WidgetInputEvent*)outGeckoEvent; - -- (NSMenu*)contextMenu; - -- (BOOL)isRectObscuredBySubview:(NSRect)inRect; - -- (LayoutDeviceIntRegion)nativeDirtyRegionWithBoundingRect:(NSRect)aRect; -- (BOOL)isUsingOpenGL; -- (void)drawUsingOpenGL; - -- (BOOL)hasRoundedBottomCorners; -- (CGFloat)cornerRadius; -- (void)clearCorners; - --(void)setGLOpaque:(BOOL)aOpaque; - -// Overlay drawing functions for traditional CGContext drawing -- (void)drawTitleString; -- (void)drawTitlebarHighlight; -- (void)maskTopCornersInContext:(CGContextRef)aContext; - -#if USE_CLICK_HOLD_CONTEXTMENU - // called on a timer two seconds after a mouse down to see if we should display - // a context menu (click-hold) -- (void)clickHoldCallback:(id)inEvent; -#endif - -#ifdef ACCESSIBILITY -- (id<mozAccessible>)accessible; -#endif - -- (LayoutDeviceIntPoint)convertWindowCoordinates:(NSPoint)aPoint; -- (LayoutDeviceIntPoint)convertWindowCoordinatesRoundDown:(NSPoint)aPoint; - -- (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent; -- (void)updateWindowDraggableState; - -- (bool)shouldConsiderStartingSwipeFromEvent:(NSEvent*)aEvent; - -@end - -@interface NSView(NSThemeFrameCornerRadius) -- (float)roundedCornerRadius; -@end - -@interface NSWindow(NSWindowShouldZoomOnDoubleClick) -+ (BOOL)_shouldZoomOnDoubleClick; // present on 10.7 and above -@end - -// Starting with 10.7 the bottom corners of all windows are rounded. -// Unfortunately, the standard rounding that OS X applies to OpenGL views -// does not use anti-aliasing and looks very crude. Since we want a smooth, -// anti-aliased curve, we'll draw it ourselves. -// Additionally, we need to turn off the OS-supplied rounding because it -// eats into our corner's curve. We do that by overriding an NSSurface method. -@interface NSSurface @end - -@implementation NSSurface(DontCutOffCorners) -- (CGSRegionObj)_createRoundedBottomRegionForRect:(CGRect)rect -{ - // Create a normal rect region without rounded bottom corners. - CGSRegionObj region; - CGSNewRegionWithRect(&rect, ®ion); - return region; -} -@end - -#pragma mark - - -// Flips a screen coordinate from a point in the cocoa coordinate system (bottom-left rect) to a point -// that is a "flipped" cocoa coordinate system (starts in the top-left). -static inline void -FlipCocoaScreenCoordinate(NSPoint &inPoint) -{ - inPoint.y = nsCocoaUtils::FlippedScreenY(inPoint.y); -} - -void EnsureLogInitialized() -{ - if (!sCocoaLog) { - sCocoaLog = PR_NewLogModule("nsCocoaWidgets"); - } -} - -namespace { - -// Used for OpenGL drawing from the compositor thread for OMTC BasicLayers. -// We need to use OpenGL for this because there seems to be no other robust -// way of drawing from a secondary thread without locking, which would cause -// deadlocks in our setup. See bug 882523. -class GLPresenter : public GLManager -{ -public: - static mozilla::UniquePtr<GLPresenter> CreateForWindow(nsIWidget* aWindow) - { - // Contrary to CompositorOGL, we allow unaccelerated OpenGL contexts to be - // used. BasicCompositor only requires very basic GL functionality. - RefPtr<GLContext> context = gl::GLContextProvider::CreateForWindow(aWindow, false); - return context ? MakeUnique<GLPresenter>(context) : nullptr; - } - - explicit GLPresenter(GLContext* aContext); - virtual ~GLPresenter(); - - virtual GLContext* gl() const override { return mGLContext; } - virtual ShaderProgramOGL* GetProgram(GLenum aTarget, gfx::SurfaceFormat aFormat) override - { - MOZ_ASSERT(aTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB); - MOZ_ASSERT(aFormat == gfx::SurfaceFormat::R8G8B8A8); - return mRGBARectProgram.get(); - } - virtual const gfx::Matrix4x4& GetProjMatrix() const override - { - return mProjMatrix; - } - virtual void ActivateProgram(ShaderProgramOGL *aProg) override - { - mGLContext->fUseProgram(aProg->GetProgram()); - } - virtual void BindAndDrawQuad(ShaderProgramOGL *aProg, - const gfx::Rect& aLayerRect, - const gfx::Rect& aTextureRect) override; - - void BeginFrame(LayoutDeviceIntSize aRenderSize); - void EndFrame(); - - NSOpenGLContext* GetNSOpenGLContext() - { - return GLContextCGL::Cast(mGLContext)->GetNSOpenGLContext(); - } - -protected: - RefPtr<mozilla::gl::GLContext> mGLContext; - mozilla::UniquePtr<mozilla::layers::ShaderProgramOGL> mRGBARectProgram; - gfx::Matrix4x4 mProjMatrix; - GLuint mQuadVBO; -}; - -} // unnamed namespace - -namespace mozilla { - -struct SwipeEventQueue { - SwipeEventQueue(uint32_t aAllowedDirections, uint64_t aInputBlockId) - : allowedDirections(aAllowedDirections) - , inputBlockId(aInputBlockId) - {} - - nsTArray<PanGestureInput> queuedEvents; - uint32_t allowedDirections; - uint64_t inputBlockId; -}; - -} // namespace mozilla - -#pragma mark - - -nsChildView::nsChildView() : nsBaseWidget() -, mView(nullptr) -, mParentView(nullptr) -, mParentWidget(nullptr) -, mViewTearDownLock("ChildViewTearDown") -, mEffectsLock("WidgetEffects") -, mShowsResizeIndicator(false) -, mHasRoundedBottomCorners(false) -, mIsCoveringTitlebar(false) -, mIsFullscreen(false) -, mIsOpaque(false) -, mTitlebarCGContext(nullptr) -, mBackingScaleFactor(0.0) -, mVisible(false) -, mDrawing(false) -, mIsDispatchPaint(false) -{ - EnsureLogInitialized(); -} - -nsChildView::~nsChildView() -{ - ReleaseTitlebarCGContext(); - - if (mSwipeTracker) { - mSwipeTracker->Destroy(); - mSwipeTracker = nullptr; - } - - // Notify the children that we're gone. childView->ResetParent() can change - // our list of children while it's being iterated, so the way we iterate the - // list must allow for this. - for (nsIWidget* kid = mLastChild; kid;) { - nsChildView* childView = static_cast<nsChildView*>(kid); - kid = kid->GetPrevSibling(); - childView->ResetParent(); - } - - NS_WARNING_ASSERTION( - mOnDestroyCalled, - "nsChildView object destroyed without calling Destroy()"); - - DestroyCompositor(); - - // An nsChildView object that was in use can be destroyed without Destroy() - // ever being called on it. So we also need to do a quick, safe cleanup - // here (it's too late to just call Destroy(), which can cause crashes). - // It's particularly important to make sure widgetDestroyed is called on our - // mView -- this method NULLs mView's mGeckoChild, and NULL checks on - // mGeckoChild are used throughout the ChildView class to tell if it's safe - // to use a ChildView object. - [mView widgetDestroyed]; // Safe if mView is nil. - mParentWidget = nil; - TearDownView(); // Safe if called twice. -} - -void -nsChildView::ReleaseTitlebarCGContext() -{ - if (mTitlebarCGContext) { - CGContextRelease(mTitlebarCGContext); - mTitlebarCGContext = nullptr; - } -} - -nsresult -nsChildView::Create(nsIWidget* aParent, - nsNativeWidget aNativeParent, - const LayoutDeviceIntRect& aRect, - nsWidgetInitData* aInitData) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // Because the hidden window is created outside of an event loop, - // we need to provide an autorelease pool to avoid leaking cocoa objects - // (see bug 559075). - nsAutoreleasePool localPool; - - // See NSView (MethodSwizzling) below. - if (!gChildViewMethodsSwizzled) { - nsToolkit::SwizzleMethods([NSView class], @selector(mouseDownCanMoveWindow), - @selector(nsChildView_NSView_mouseDownCanMoveWindow)); - gChildViewMethodsSwizzled = true; - } - - mBounds = aRect; - - // Ensure that the toolkit is created. - nsToolkit::GetToolkit(); - - BaseCreate(aParent, aInitData); - - // inherit things from the parent view and create our parallel - // NSView in the Cocoa display system - mParentView = nil; - if (aParent) { - // inherit the top-level window. NS_NATIVE_WIDGET is always a NSView - // regardless of if we're asking a window or a view (for compatibility - // with windows). - mParentView = (NSView<mozView>*)aParent->GetNativeData(NS_NATIVE_WIDGET); - mParentWidget = aParent; - } else { - // This is the normal case. When we're the root widget of the view hiararchy, - // aNativeParent will be the contentView of our window, since that's what - // nsCocoaWindow returns when asked for an NS_NATIVE_VIEW. - mParentView = reinterpret_cast<NSView<mozView>*>(aNativeParent); - } - - // create our parallel NSView and hook it up to our parent. Recall - // that NS_NATIVE_WIDGET is the NSView. - CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mParentView); - NSRect r = nsCocoaUtils::DevPixelsToCocoaPoints(mBounds, scaleFactor); - mView = [[[[ChildView alloc] initWithFrame:r geckoChild:this] autorelease] retain]; - - if (!mView) { - return NS_ERROR_FAILURE; - } - - // If this view was created in a Gecko view hierarchy, the initial state - // is hidden. If the view is attached only to a native NSView but has - // no Gecko parent (as in embedding), the initial state is visible. - if (mParentWidget) - [mView setHidden:YES]; - else - mVisible = true; - - // Hook it up in the NSView hierarchy. - if (mParentView) { - [mParentView addSubview:mView]; - } - - // if this is a ChildView, make sure that our per-window data - // is set up - if ([mView isKindOfClass:[ChildView class]]) - [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:[mView window]]; - - NS_ASSERTION(!mTextInputHandler, "mTextInputHandler has already existed"); - mTextInputHandler = new TextInputHandler(this, mView); - - mPluginFocused = false; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - - -void nsChildView::TearDownView() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mView) - return; - - NSWindow* win = [mView window]; - NSResponder* responder = [win firstResponder]; - - // We're being unhooked from the view hierarchy, don't leave our view - // or a child view as the window first responder. - if (responder && [responder isKindOfClass:[NSView class]] && - [(NSView*)responder isDescendantOf:mView]) { - [win makeFirstResponder:[mView superview]]; - } - - // If mView is win's contentView, win (mView's NSWindow) "owns" mView -- - // win has retained mView, and will detach it from the view hierarchy and - // release it when necessary (when win is itself destroyed (in a call to - // [win dealloc])). So all we need to do here is call [mView release] (to - // match the call to [mView retain] in nsChildView::StandardCreate()). - // Also calling [mView removeFromSuperviewWithoutNeedingDisplay] causes - // mView to be released again and dealloced, while remaining win's - // contentView. So if we do that here, win will (for a short while) have - // an invalid contentView (for the consequences see bmo bugs 381087 and - // 374260). - if ([mView isEqual:[win contentView]]) { - [mView release]; - } else { - // Stop NSView hierarchy being changed during [ChildView drawRect:] - [mView performSelectorOnMainThread:@selector(delayedTearDown) withObject:nil waitUntilDone:false]; - } - mView = nil; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -nsCocoaWindow* -nsChildView::GetXULWindowWidget() -{ - id windowDelegate = [[mView window] delegate]; - if (windowDelegate && [windowDelegate isKindOfClass:[WindowDelegate class]]) { - return [(WindowDelegate *)windowDelegate geckoWidget]; - } - return nullptr; -} - -void nsChildView::Destroy() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Make sure that no composition is in progress while disconnecting - // ourselves from the view. - MutexAutoLock lock(mViewTearDownLock); - - if (mOnDestroyCalled) - return; - mOnDestroyCalled = true; - - // Stuff below may delete the last ref to this - nsCOMPtr<nsIWidget> kungFuDeathGrip(this); - - [mView widgetDestroyed]; - - nsBaseWidget::Destroy(); - - NotifyWindowDestroyed(); - mParentWidget = nil; - - TearDownView(); - - nsBaseWidget::OnDestroy(); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -#pragma mark - - -#if 0 -static void PrintViewHierarchy(NSView *view) -{ - while (view) { - NSLog(@" view is %x, frame %@", view, NSStringFromRect([view frame])); - view = [view superview]; - } -} -#endif - -// Return native data according to aDataType -void* nsChildView::GetNativeData(uint32_t aDataType) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL; - - void* retVal = nullptr; - - switch (aDataType) - { - case NS_NATIVE_WIDGET: - case NS_NATIVE_DISPLAY: - retVal = (void*)mView; - break; - - case NS_NATIVE_WINDOW: - retVal = [mView window]; - break; - - case NS_NATIVE_GRAPHIC: - NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a Mac OS X child view!"); - retVal = nullptr; - break; - - case NS_NATIVE_OFFSETX: - retVal = 0; - break; - - case NS_NATIVE_OFFSETY: - retVal = 0; - break; - - case NS_RAW_NATIVE_IME_CONTEXT: - retVal = GetPseudoIMEContext(); - if (retVal) { - break; - } - retVal = [mView inputContext]; - // If input context isn't available on this widget, we should set |this| - // instead of nullptr since if this returns nullptr, IMEStateManager - // cannot manage composition with TextComposition instance. Although, - // this case shouldn't occur. - if (NS_WARN_IF(!retVal)) { - retVal = this; - } - break; - } - - return retVal; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL; -} - -#pragma mark - - -nsTransparencyMode nsChildView::GetTransparencyMode() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - nsCocoaWindow* windowWidget = GetXULWindowWidget(); - return windowWidget ? windowWidget->GetTransparencyMode() : eTransparencyOpaque; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(eTransparencyOpaque); -} - -// This is called by nsContainerFrame on the root widget for all window types -// except popup windows (when nsCocoaWindow::SetTransparencyMode is used instead). -void nsChildView::SetTransparencyMode(nsTransparencyMode aMode) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - nsCocoaWindow* windowWidget = GetXULWindowWidget(); - if (windowWidget) { - windowWidget->SetTransparencyMode(aMode); - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -bool nsChildView::IsVisible() const -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - if (!mVisible) { - return mVisible; - } - - // mVisible does not accurately reflect the state of a hidden tabbed view - // so verify that the view has a window as well - // then check native widget hierarchy visibility - return ([mView window] != nil) && !NSIsEmptyRect([mView visibleRect]); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); -} - -// Some NSView methods (e.g. setFrame and setHidden) invalidate the view's -// bounds in our window. However, we don't want these invalidations because -// they are unnecessary and because they actually slow us down since we -// block on the compositor inside drawRect. -// When we actually need something invalidated, there will be an explicit call -// to Invalidate from Gecko, so turning these automatic invalidations off -// won't hurt us in the non-OMTC case. -// The invalidations inside these NSView methods happen via a call to the -// private method -[NSWindow _setNeedsDisplayInRect:]. Our BaseWindow -// implementation of that method is augmented to let us ignore those calls -// using -[BaseWindow disable/enableSetNeedsDisplay]. -static void -ManipulateViewWithoutNeedingDisplay(NSView* aView, void (^aCallback)()) -{ - BaseWindow* win = nil; - if ([[aView window] isKindOfClass:[BaseWindow class]]) { - win = (BaseWindow*)[aView window]; - } - [win disableSetNeedsDisplay]; - aCallback(); - [win enableSetNeedsDisplay]; -} - -// Hide or show this component -NS_IMETHODIMP nsChildView::Show(bool aState) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (aState != mVisible) { - // Provide an autorelease pool because this gets called during startup - // on the "hidden window", resulting in cocoa object leakage if there's - // no pool in place. - nsAutoreleasePool localPool; - - ManipulateViewWithoutNeedingDisplay(mView, ^{ - [mView setHidden:!aState]; - }); - - mVisible = aState; - } - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// Change the parent of this widget -NS_IMETHODIMP -nsChildView::SetParent(nsIWidget* aNewParent) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (mOnDestroyCalled) - return NS_OK; - - nsCOMPtr<nsIWidget> kungFuDeathGrip(this); - - if (mParentWidget) { - mParentWidget->RemoveChild(this); - } - - if (aNewParent) { - ReparentNativeWidget(aNewParent); - } else { - [mView removeFromSuperview]; - mParentView = nil; - } - - mParentWidget = aNewParent; - - if (mParentWidget) { - mParentWidget->AddChild(this); - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -void -nsChildView::ReparentNativeWidget(nsIWidget* aNewParent) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - NS_PRECONDITION(aNewParent, ""); - - if (mOnDestroyCalled) - return; - - NSView<mozView>* newParentView = - (NSView<mozView>*)aNewParent->GetNativeData(NS_NATIVE_WIDGET); - NS_ENSURE_TRUE_VOID(newParentView); - - // we hold a ref to mView, so this is safe - [mView removeFromSuperview]; - mParentView = newParentView; - [mParentView addSubview:mView]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void nsChildView::ResetParent() -{ - if (!mOnDestroyCalled) { - if (mParentWidget) - mParentWidget->RemoveChild(this); - if (mView) - [mView removeFromSuperview]; - } - mParentWidget = nullptr; -} - -nsIWidget* -nsChildView::GetParent() -{ - return mParentWidget; -} - -float -nsChildView::GetDPI() -{ - NSWindow* window = [mView window]; - if (window && [window isKindOfClass:[BaseWindow class]]) { - return [(BaseWindow*)window getDPI]; - } - - return 96.0; -} - -NS_IMETHODIMP nsChildView::Enable(bool aState) -{ - return NS_OK; -} - -bool nsChildView::IsEnabled() const -{ - return true; -} - -NS_IMETHODIMP nsChildView::SetFocus(bool aRaise) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NSWindow* window = [mView window]; - if (window) - [window makeFirstResponder:mView]; - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// Override to set the cursor on the mac -NS_IMETHODIMP nsChildView::SetCursor(nsCursor aCursor) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if ([mView isDragInProgress]) - return NS_OK; // Don't change the cursor during dragging. - - nsBaseWidget::SetCursor(aCursor); - return [[nsCursorManager sharedInstance] setCursor:aCursor]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// implement to fix "hidden virtual function" warning -NS_IMETHODIMP nsChildView::SetCursor(imgIContainer* aCursor, - uint32_t aHotspotX, uint32_t aHotspotY) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - nsBaseWidget::SetCursor(aCursor, aHotspotX, aHotspotY); - return [[nsCursorManager sharedInstance] setCursorWithImage:aCursor hotSpotX:aHotspotX hotSpotY:aHotspotY scaleFactor:BackingScaleFactor()]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -#pragma mark - - -// Get this component dimension -LayoutDeviceIntRect -nsChildView::GetBounds() -{ - return !mView ? mBounds : CocoaPointsToDevPixels([mView frame]); -} - -LayoutDeviceIntRect -nsChildView::GetClientBounds() -{ - LayoutDeviceIntRect rect = GetBounds(); - if (!mParentWidget) { - // For top level widgets we want the position on screen, not the position - // of this view inside the window. - rect.MoveTo(WidgetToScreenOffset()); - } - return rect; -} - -LayoutDeviceIntRect -nsChildView::GetScreenBounds() -{ - LayoutDeviceIntRect rect = GetBounds(); - rect.MoveTo(WidgetToScreenOffset()); - return rect; -} - -double -nsChildView::GetDefaultScaleInternal() -{ - return BackingScaleFactor(); -} - -CGFloat -nsChildView::BackingScaleFactor() const -{ - if (mBackingScaleFactor > 0.0) { - return mBackingScaleFactor; - } - if (!mView) { - return 1.0; - } - mBackingScaleFactor = nsCocoaUtils::GetBackingScaleFactor(mView); - return mBackingScaleFactor; -} - -void -nsChildView::BackingScaleFactorChanged() -{ - CGFloat newScale = nsCocoaUtils::GetBackingScaleFactor(mView); - - // ignore notification if it hasn't really changed (or maybe we have - // disabled HiDPI mode via prefs) - if (mBackingScaleFactor == newScale) { - return; - } - - mBackingScaleFactor = newScale; - NSRect frame = [mView frame]; - mBounds = nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, newScale); - - if (mWidgetListener && !mWidgetListener->GetXULWindow()) { - nsIPresShell* presShell = mWidgetListener->GetPresShell(); - if (presShell) { - presShell->BackingScaleFactorChanged(); - } - } -} - -int32_t -nsChildView::RoundsWidgetCoordinatesTo() -{ - if (BackingScaleFactor() == 2.0) { - return 2; - } - return 1; -} - -// Move this component, aX and aY are in the parent widget coordinate system -NS_IMETHODIMP nsChildView::Move(double aX, double aY) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - int32_t x = NSToIntRound(aX); - int32_t y = NSToIntRound(aY); - - if (!mView || (mBounds.x == x && mBounds.y == y)) - return NS_OK; - - mBounds.x = x; - mBounds.y = y; - - ManipulateViewWithoutNeedingDisplay(mView, ^{ - [mView setFrame:DevPixelsToCocoaPoints(mBounds)]; - }); - - NotifyRollupGeometryChange(); - ReportMoveEvent(); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP nsChildView::Resize(double aWidth, double aHeight, bool aRepaint) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - int32_t width = NSToIntRound(aWidth); - int32_t height = NSToIntRound(aHeight); - - if (!mView || (mBounds.width == width && mBounds.height == height)) - return NS_OK; - - mBounds.width = width; - mBounds.height = height; - - ManipulateViewWithoutNeedingDisplay(mView, ^{ - [mView setFrame:DevPixelsToCocoaPoints(mBounds)]; - }); - - if (mVisible && aRepaint) { - [[mView pixelHostingView] setNeedsDisplay:YES]; - } - - NotifyRollupGeometryChange(); - ReportSizeEvent(); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP nsChildView::Resize(double aX, double aY, - double aWidth, double aHeight, bool aRepaint) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - int32_t x = NSToIntRound(aX); - int32_t y = NSToIntRound(aY); - int32_t width = NSToIntRound(aWidth); - int32_t height = NSToIntRound(aHeight); - - BOOL isMoving = (mBounds.x != x || mBounds.y != y); - BOOL isResizing = (mBounds.width != width || mBounds.height != height); - if (!mView || (!isMoving && !isResizing)) - return NS_OK; - - if (isMoving) { - mBounds.x = x; - mBounds.y = y; - } - if (isResizing) { - mBounds.width = width; - mBounds.height = height; - } - - ManipulateViewWithoutNeedingDisplay(mView, ^{ - [mView setFrame:DevPixelsToCocoaPoints(mBounds)]; - }); - - if (mVisible && aRepaint) { - [[mView pixelHostingView] setNeedsDisplay:YES]; - } - - NotifyRollupGeometryChange(); - if (isMoving) { - ReportMoveEvent(); - if (mOnDestroyCalled) - return NS_OK; - } - if (isResizing) - ReportSizeEvent(); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -static const int32_t resizeIndicatorWidth = 15; -static const int32_t resizeIndicatorHeight = 15; -bool nsChildView::ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect) -{ - NSView *topLevelView = mView, *superView = nil; - while ((superView = [topLevelView superview])) - topLevelView = superView; - - if (![[topLevelView window] showsResizeIndicator] || - !([[topLevelView window] styleMask] & NSResizableWindowMask)) - return false; - - if (aResizerRect) { - NSSize bounds = [topLevelView bounds].size; - NSPoint corner = NSMakePoint(bounds.width, [topLevelView isFlipped] ? bounds.height : 0); - corner = [topLevelView convertPoint:corner toView:mView]; - aResizerRect->SetRect(NSToIntRound(corner.x) - resizeIndicatorWidth, - NSToIntRound(corner.y) - resizeIndicatorHeight, - resizeIndicatorWidth, resizeIndicatorHeight); - } - return true; -} - -nsresult nsChildView::SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout, - int32_t aNativeKeyCode, - uint32_t aModifierFlags, - const nsAString& aCharacters, - const nsAString& aUnmodifiedCharacters, - nsIObserver* aObserver) -{ - AutoObserverNotifier notifier(aObserver, "keyevent"); - return mTextInputHandler->SynthesizeNativeKeyEvent(aNativeKeyboardLayout, - aNativeKeyCode, - aModifierFlags, - aCharacters, - aUnmodifiedCharacters); -} - -nsresult nsChildView::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint, - uint32_t aNativeMessage, - uint32_t aModifierFlags, - nsIObserver* aObserver) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - AutoObserverNotifier notifier(aObserver, "mouseevent"); - - NSPoint pt = - nsCocoaUtils::DevPixelsToCocoaPoints(aPoint, BackingScaleFactor()); - - // Move the mouse cursor to the requested position and reconnect it to the mouse. - CGWarpMouseCursorPosition(NSPointToCGPoint(pt)); - CGAssociateMouseAndMouseCursorPosition(true); - - // aPoint is given with the origin on the top left, but convertScreenToBase - // expects a point in a coordinate system that has its origin on the bottom left. - NSPoint screenPoint = NSMakePoint(pt.x, nsCocoaUtils::FlippedScreenY(pt.y)); - NSPoint windowPoint = - nsCocoaUtils::ConvertPointFromScreen([mView window], screenPoint); - - NSEvent* event = [NSEvent mouseEventWithType:(NSEventType)aNativeMessage - location:windowPoint - modifierFlags:aModifierFlags - timestamp:[[NSProcessInfo processInfo] systemUptime] - windowNumber:[[mView window] windowNumber] - context:nil - eventNumber:0 - clickCount:1 - pressure:0.0]; - - if (!event) - return NS_ERROR_FAILURE; - - if ([[mView window] isKindOfClass:[BaseWindow class]]) { - // Tracking area events don't end up in their tracking areas when sent - // through [NSApp sendEvent:], so pass them directly to the right methods. - BaseWindow* window = (BaseWindow*)[mView window]; - if (aNativeMessage == NSMouseEntered) { - [window mouseEntered:event]; - return NS_OK; - } - if (aNativeMessage == NSMouseExited) { - [window mouseExited:event]; - return NS_OK; - } - if (aNativeMessage == NSMouseMoved) { - [window mouseMoved:event]; - return NS_OK; - } - } - - [NSApp sendEvent:event]; - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -nsresult nsChildView::SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPoint aPoint, - uint32_t aNativeMessage, - double aDeltaX, - double aDeltaY, - double aDeltaZ, - uint32_t aModifierFlags, - uint32_t aAdditionalFlags, - nsIObserver* aObserver) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - AutoObserverNotifier notifier(aObserver, "mousescrollevent"); - - NSPoint pt = - nsCocoaUtils::DevPixelsToCocoaPoints(aPoint, BackingScaleFactor()); - - // Move the mouse cursor to the requested position and reconnect it to the mouse. - CGWarpMouseCursorPosition(NSPointToCGPoint(pt)); - CGAssociateMouseAndMouseCursorPosition(true); - - // Mostly copied from http://stackoverflow.com/a/6130349 - CGScrollEventUnit units = - (aAdditionalFlags & nsIDOMWindowUtils::MOUSESCROLL_SCROLL_LINES) - ? kCGScrollEventUnitLine : kCGScrollEventUnitPixel; - CGEventRef cgEvent = CGEventCreateScrollWheelEvent(NULL, units, 3, aDeltaY, aDeltaX, aDeltaZ); - if (!cgEvent) { - return NS_ERROR_FAILURE; - } - - CGEventPost(kCGHIDEventTap, cgEvent); - CFRelease(cgEvent); - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -nsresult nsChildView::SynthesizeNativeTouchPoint(uint32_t aPointerId, - TouchPointerState aPointerState, - mozilla::LayoutDeviceIntPoint aPoint, - double aPointerPressure, - uint32_t aPointerOrientation, - nsIObserver* aObserver) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - AutoObserverNotifier notifier(aObserver, "touchpoint"); - - MOZ_ASSERT(NS_IsMainThread()); - if (aPointerState == TOUCH_HOVER) { - return NS_ERROR_UNEXPECTED; - } - - if (!mSynthesizedTouchInput) { - mSynthesizedTouchInput = MakeUnique<MultiTouchInput>(); - } - - LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset(); - MultiTouchInput inputToDispatch = UpdateSynthesizedTouchState( - mSynthesizedTouchInput.get(), PR_IntervalNow(), TimeStamp::Now(), - aPointerId, aPointerState, pointInWindow, aPointerPressure, - aPointerOrientation); - DispatchTouchInput(inputToDispatch); - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// First argument has to be an NSMenu representing the application's top-level -// menu bar. The returned item is *not* retained. -static NSMenuItem* NativeMenuItemWithLocation(NSMenu* menubar, NSString* locationString) -{ - NSArray* indexes = [locationString componentsSeparatedByString:@"|"]; - unsigned int indexCount = [indexes count]; - if (indexCount == 0) - return nil; - - NSMenu* currentSubmenu = [NSApp mainMenu]; - for (unsigned int i = 0; i < indexCount; i++) { - int targetIndex; - // We remove the application menu from consideration for the top-level menu - if (i == 0) - targetIndex = [[indexes objectAtIndex:i] intValue] + 1; - else - targetIndex = [[indexes objectAtIndex:i] intValue]; - int itemCount = [currentSubmenu numberOfItems]; - if (targetIndex < itemCount) { - NSMenuItem* menuItem = [currentSubmenu itemAtIndex:targetIndex]; - // if this is the last index just return the menu item - if (i == (indexCount - 1)) - return menuItem; - // if this is not the last index find the submenu and keep going - if ([menuItem hasSubmenu]) - currentSubmenu = [menuItem submenu]; - else - return nil; - } - } - - return nil; -} - -// Used for testing native menu system structure and event handling. -NS_IMETHODIMP nsChildView::ActivateNativeMenuItemAt(const nsAString& indexString) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NSString* locationString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading()) - length:indexString.Length()]; - NSMenuItem* item = NativeMenuItemWithLocation([NSApp mainMenu], locationString); - // We can't perform an action on an item with a submenu, that will raise - // an obj-c exception. - if (item && ![item hasSubmenu]) { - NSMenu* parent = [item menu]; - if (parent) { - // NSLog(@"Performing action for native menu item titled: %@\n", - // [[currentSubmenu itemAtIndex:targetIndex] title]); - [parent performActionForItemAtIndex:[parent indexOfItem:item]]; - return NS_OK; - } - } - return NS_ERROR_FAILURE; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// Used for testing native menu system structure and event handling. -NS_IMETHODIMP nsChildView::ForceUpdateNativeMenuAt(const nsAString& indexString) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - nsCocoaWindow *widget = GetXULWindowWidget(); - if (widget) { - nsMenuBarX* mb = widget->GetMenuBar(); - if (mb) { - if (indexString.IsEmpty()) - mb->ForceNativeMenuReload(); - else - mb->ForceUpdateNativeMenuAt(indexString); - } - } - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -#pragma mark - - -#ifdef INVALIDATE_DEBUGGING - -static Boolean KeyDown(const UInt8 theKey) -{ - KeyMap map; - GetKeys(map); - return ((*((UInt8 *)map + (theKey >> 3)) >> (theKey & 7)) & 1) != 0; -} - -static Boolean caps_lock() -{ - return KeyDown(0x39); -} - -static void blinkRect(Rect* r) -{ - StRegionFromPool oldClip; - if (oldClip != NULL) - ::GetClip(oldClip); - - ::ClipRect(r); - ::InvertRect(r); - UInt32 end = ::TickCount() + 5; - while (::TickCount() < end) ; - ::InvertRect(r); - - if (oldClip != NULL) - ::SetClip(oldClip); -} - -static void blinkRgn(RgnHandle rgn) -{ - StRegionFromPool oldClip; - if (oldClip != NULL) - ::GetClip(oldClip); - - ::SetClip(rgn); - ::InvertRgn(rgn); - UInt32 end = ::TickCount() + 5; - while (::TickCount() < end) ; - ::InvertRgn(rgn); - - if (oldClip != NULL) - ::SetClip(oldClip); -} - -#endif - -// Invalidate this component's visible area -NS_IMETHODIMP nsChildView::Invalidate(const LayoutDeviceIntRect& aRect) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (!mView || !mVisible) - return NS_OK; - - NS_ASSERTION(GetLayerManager()->GetBackendType() != LayersBackend::LAYERS_CLIENT, - "Shouldn't need to invalidate with accelerated OMTC layers!"); - - [[mView pixelHostingView] setNeedsDisplayInRect:DevPixelsToCocoaPoints(aRect)]; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -bool -nsChildView::WidgetTypeSupportsAcceleration() -{ - // Don't use OpenGL for transparent windows or for popup windows. - return mView && [[mView window] isOpaque] && - ![[mView window] isKindOfClass:[PopupWindow class]]; -} - -bool -nsChildView::ShouldUseOffMainThreadCompositing() -{ - // Don't use OMTC for transparent windows or for popup windows. - if (!mView || ![[mView window] isOpaque] || - [[mView window] isKindOfClass:[PopupWindow class]]) - return false; - - return nsBaseWidget::ShouldUseOffMainThreadCompositing(); -} - -inline uint16_t COLOR8TOCOLOR16(uint8_t color8) -{ - // return (color8 == 0xFF ? 0xFFFF : (color8 << 8)); - return (color8 << 8) | color8; /* (color8 * 257) == (color8 * 0x0101) */ -} - -#pragma mark - - -nsresult nsChildView::ConfigureChildren(const nsTArray<Configuration>& aConfigurations) -{ - return NS_OK; -} - -// Invokes callback and ProcessEvent methods on Event Listener object -NS_IMETHODIMP nsChildView::DispatchEvent(WidgetGUIEvent* event, - nsEventStatus& aStatus) -{ - RefPtr<nsChildView> kungFuDeathGrip(this); - -#ifdef DEBUG - debug_DumpEvent(stdout, event->mWidget, event, "something", 0); -#endif - - NS_ASSERTION(!(mTextInputHandler && mTextInputHandler->IsIMEComposing() && - event->HasKeyEventMessage()), - "Any key events should not be fired during IME composing"); - - if (event->mFlags.mIsSynthesizedForTests) { - WidgetKeyboardEvent* keyEvent = event->AsKeyboardEvent(); - if (keyEvent) { - nsresult rv = mTextInputHandler->AttachNativeKeyEvent(*keyEvent); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - aStatus = nsEventStatus_eIgnore; - - nsIWidgetListener* listener = mWidgetListener; - - // If the listener is NULL, check if the parent is a popup. If it is, then - // this child is the popup content view attached to a popup. Get the - // listener from the parent popup instead. - nsCOMPtr<nsIWidget> parentWidget = mParentWidget; - if (!listener && parentWidget) { - if (parentWidget->WindowType() == eWindowType_popup) { - // Check just in case event->mWidget isn't this widget - if (event->mWidget) { - listener = event->mWidget->GetWidgetListener(); - } - if (!listener) { - event->mWidget = parentWidget; - listener = parentWidget->GetWidgetListener(); - } - } - } - - if (listener) - aStatus = listener->HandleEvent(event, mUseAttachedEvents); - - return NS_OK; -} - -bool nsChildView::DispatchWindowEvent(WidgetGUIEvent& event) -{ - nsEventStatus status; - DispatchEvent(&event, status); - return ConvertStatus(status); -} - -nsIWidget* -nsChildView::GetWidgetForListenerEvents() -{ - // If there is no listener, use the parent popup's listener if that exists. - if (!mWidgetListener && mParentWidget && - mParentWidget->WindowType() == eWindowType_popup) { - return mParentWidget; - } - - return this; -} - -void nsChildView::WillPaintWindow() -{ - nsCOMPtr<nsIWidget> widget = GetWidgetForListenerEvents(); - - nsIWidgetListener* listener = widget->GetWidgetListener(); - if (listener) { - listener->WillPaintWindow(widget); - } -} - -bool nsChildView::PaintWindow(LayoutDeviceIntRegion aRegion) -{ - nsCOMPtr<nsIWidget> widget = GetWidgetForListenerEvents(); - - nsIWidgetListener* listener = widget->GetWidgetListener(); - if (!listener) - return false; - - bool returnValue = false; - bool oldDispatchPaint = mIsDispatchPaint; - mIsDispatchPaint = true; - returnValue = listener->PaintWindow(widget, aRegion); - - listener = widget->GetWidgetListener(); - if (listener) { - listener->DidPaintWindow(); - } - - mIsDispatchPaint = oldDispatchPaint; - return returnValue; -} - -bool -nsChildView::PaintWindowInContext(CGContextRef aContext, const LayoutDeviceIntRegion& aRegion, gfx::IntSize aSurfaceSize) -{ - if (!mBackingSurface || mBackingSurface->GetSize() != aSurfaceSize) { - mBackingSurface = - gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(aSurfaceSize, - gfx::SurfaceFormat::B8G8R8A8); - if (!mBackingSurface) { - return false; - } - } - - RefPtr<gfxContext> targetContext = gfxContext::CreateOrNull(mBackingSurface); - MOZ_ASSERT(targetContext); // already checked the draw target above - - // Set up the clip region and clear existing contents in the backing surface. - targetContext->NewPath(); - for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { - const LayoutDeviceIntRect& r = iter.Get(); - targetContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height)); - mBackingSurface->ClearRect(gfx::Rect(r.ToUnknownRect())); - } - targetContext->Clip(); - - nsAutoRetainCocoaObject kungFuDeathGrip(mView); - bool painted = false; - if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) { - nsBaseWidget::AutoLayerManagerSetup - setupLayerManager(this, targetContext, BufferMode::BUFFER_NONE); - painted = PaintWindow(aRegion); - } else if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) { - // We only need this so that we actually get DidPaintWindow fired - painted = PaintWindow(aRegion); - } - - uint8_t* data; - gfx::IntSize size; - int32_t stride; - gfx::SurfaceFormat format; - - if (!mBackingSurface->LockBits(&data, &size, &stride, &format)) { - return false; - } - - // Draw the backing surface onto the window. - CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, data, stride * size.height, NULL); - NSColorSpace* colorSpace = [[mView window] colorSpace]; - CGImageRef image = CGImageCreate(size.width, size.height, 8, 32, stride, - [colorSpace CGColorSpace], - kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst, - provider, NULL, false, kCGRenderingIntentDefault); - CGContextSaveGState(aContext); - CGContextTranslateCTM(aContext, 0, size.height); - CGContextScaleCTM(aContext, 1, -1); - CGContextSetBlendMode(aContext, kCGBlendModeCopy); - CGContextDrawImage(aContext, CGRectMake(0, 0, size.width, size.height), image); - CGImageRelease(image); - CGDataProviderRelease(provider); - CGContextRestoreGState(aContext); - - mBackingSurface->ReleaseBits(data); - - return painted; -} - -#pragma mark - - -void nsChildView::ReportMoveEvent() -{ - NotifyWindowMoved(mBounds.x, mBounds.y); -} - -void nsChildView::ReportSizeEvent() -{ - if (mWidgetListener) - mWidgetListener->WindowResized(this, mBounds.width, mBounds.height); -} - -#pragma mark - - -LayoutDeviceIntPoint nsChildView::GetClientOffset() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - NSPoint origin = [mView convertPoint:NSMakePoint(0, 0) toView:nil]; - origin.y = [[mView window] frame].size.height - origin.y; - return CocoaPointsToDevPixels(origin); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(LayoutDeviceIntPoint(0, 0)); -} - -// Return the offset between this child view and the screen. -// @return -- widget origin in device-pixel coords -LayoutDeviceIntPoint nsChildView::WidgetToScreenOffset() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - NSPoint origin = NSMakePoint(0, 0); - - // 1. First translate view origin point into window coords. - // The returned point is in bottom-left coordinates. - origin = [mView convertPoint:origin toView:nil]; - - // 2. We turn the window-coord rect's origin into screen (still bottom-left) coords. - origin = nsCocoaUtils::ConvertPointToScreen([mView window], origin); - - // 3. Since we're dealing in bottom-left coords, we need to make it top-left coords - // before we pass it back to Gecko. - FlipCocoaScreenCoordinate(origin); - - // convert to device pixels - return CocoaPointsToDevPixels(origin); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(LayoutDeviceIntPoint(0,0)); -} - -NS_IMETHODIMP nsChildView::SetTitle(const nsAString& title) -{ - // child views don't have titles - return NS_OK; -} - -NS_IMETHODIMP nsChildView::GetAttention(int32_t aCycleCount) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - [NSApp requestUserAttention:NSInformationalRequest]; - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -/* static */ -bool nsChildView::DoHasPendingInputEvent() -{ - return sLastInputEventCount != GetCurrentInputEventCount(); -} - -/* static */ -uint32_t nsChildView::GetCurrentInputEventCount() -{ - // Can't use kCGAnyInputEventType because that updates too rarely for us (and - // always in increments of 30+!) and because apparently it's sort of broken - // on Tiger. So just go ahead and query the counters we care about. - static const CGEventType eventTypes[] = { - kCGEventLeftMouseDown, - kCGEventLeftMouseUp, - kCGEventRightMouseDown, - kCGEventRightMouseUp, - kCGEventMouseMoved, - kCGEventLeftMouseDragged, - kCGEventRightMouseDragged, - kCGEventKeyDown, - kCGEventKeyUp, - kCGEventScrollWheel, - kCGEventTabletPointer, - kCGEventOtherMouseDown, - kCGEventOtherMouseUp, - kCGEventOtherMouseDragged - }; - - uint32_t eventCount = 0; - for (uint32_t i = 0; i < ArrayLength(eventTypes); ++i) { - eventCount += - CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, - eventTypes[i]); - } - return eventCount; -} - -/* static */ -void nsChildView::UpdateCurrentInputEventCount() -{ - sLastInputEventCount = GetCurrentInputEventCount(); -} - -bool nsChildView::HasPendingInputEvent() -{ - return DoHasPendingInputEvent(); -} - -#pragma mark - - -NS_IMETHODIMP -nsChildView::StartPluginIME(const mozilla::WidgetKeyboardEvent& aKeyboardEvent, - int32_t aPanelX, int32_t aPanelY, - nsString& aCommitted) -{ - NS_ENSURE_TRUE(mView, NS_ERROR_NOT_AVAILABLE); - - ComplexTextInputPanel* ctiPanel = - ComplexTextInputPanel::GetSharedComplexTextInputPanel(); - - ctiPanel->PlacePanel(aPanelX, aPanelY); - // We deliberately don't use TextInputHandler::GetCurrentKeyEvent() to - // obtain the NSEvent* we pass to InterpretKeyEvent(). This works fine in - // non-e10s mode. But in e10s mode TextInputHandler::HandleKeyDownEvent() - // has already returned, so the relevant KeyEventState* (and its NSEvent*) - // is already out of scope. Furthermore we don't *need* to use it. - // StartPluginIME() is only ever called to start a new IME session when none - // currently exists. So nested IME should never reach here, and so it should - // be fine to use the last key-down event received by -[ChildView keyDown:] - // (as we currently do). - ctiPanel->InterpretKeyEvent([(ChildView*)mView lastKeyDownEvent], aCommitted); - - return NS_OK; -} - -void -nsChildView::SetPluginFocused(bool& aFocused) -{ - if (aFocused == mPluginFocused) { - return; - } - if (!aFocused) { - ComplexTextInputPanel* ctiPanel = - ComplexTextInputPanel::GetSharedComplexTextInputPanel(); - if (ctiPanel) { - ctiPanel->CancelComposition(); - } - } - mPluginFocused = aFocused; -} - -NS_IMETHODIMP_(void) -nsChildView::SetInputContext(const InputContext& aContext, - const InputContextAction& aAction) -{ - NS_ENSURE_TRUE_VOID(mTextInputHandler); - - if (mTextInputHandler->IsFocused()) { - if (aContext.IsPasswordEditor()) { - TextInputHandler::EnableSecureEventInput(); - } else { - TextInputHandler::EnsureSecureEventInputDisabled(); - } - } - - mInputContext = aContext; - switch (aContext.mIMEState.mEnabled) { - case IMEState::ENABLED: - case IMEState::PLUGIN: - mTextInputHandler->SetASCIICapableOnly(false); - mTextInputHandler->EnableIME(true); - if (mInputContext.mIMEState.mOpen != IMEState::DONT_CHANGE_OPEN_STATE) { - mTextInputHandler->SetIMEOpenState( - mInputContext.mIMEState.mOpen == IMEState::OPEN); - } - break; - case IMEState::DISABLED: - mTextInputHandler->SetASCIICapableOnly(false); - mTextInputHandler->EnableIME(false); - break; - case IMEState::PASSWORD: - mTextInputHandler->SetASCIICapableOnly(true); - mTextInputHandler->EnableIME(false); - break; - default: - NS_ERROR("not implemented!"); - } -} - -NS_IMETHODIMP_(InputContext) -nsChildView::GetInputContext() -{ - switch (mInputContext.mIMEState.mEnabled) { - case IMEState::ENABLED: - case IMEState::PLUGIN: - if (mTextInputHandler) { - mInputContext.mIMEState.mOpen = - mTextInputHandler->IsIMEOpened() ? IMEState::OPEN : IMEState::CLOSED; - break; - } - // If mTextInputHandler is null, set CLOSED instead... - MOZ_FALLTHROUGH; - default: - mInputContext.mIMEState.mOpen = IMEState::CLOSED; - break; - } - return mInputContext; -} - -NS_IMETHODIMP_(TextEventDispatcherListener*) -nsChildView::GetNativeTextEventDispatcherListener() -{ - if (NS_WARN_IF(!mTextInputHandler)) { - return nullptr; - } - return mTextInputHandler; -} - -NS_IMETHODIMP -nsChildView::AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent) -{ - NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE); - return mTextInputHandler->AttachNativeKeyEvent(aEvent); -} - -bool -nsChildView::ExecuteNativeKeyBindingRemapped(NativeKeyBindingsType aType, - const WidgetKeyboardEvent& aEvent, - DoCommandCallback aCallback, - void* aCallbackData, - uint32_t aGeckoKeyCode, - uint32_t aCocoaKeyCode) -{ - NSEvent *originalEvent = reinterpret_cast<NSEvent*>(aEvent.mNativeKeyEvent); - - WidgetKeyboardEvent modifiedEvent(aEvent); - modifiedEvent.mKeyCode = aGeckoKeyCode; - - unichar ch = nsCocoaUtils::ConvertGeckoKeyCodeToMacCharCode(aGeckoKeyCode); - NSString *chars = - [[[NSString alloc] initWithCharacters:&ch length:1] autorelease]; - - modifiedEvent.mNativeKeyEvent = - [NSEvent keyEventWithType:[originalEvent type] - location:[originalEvent locationInWindow] - modifierFlags:[originalEvent modifierFlags] - timestamp:[originalEvent timestamp] - windowNumber:[originalEvent windowNumber] - context:[originalEvent context] - characters:chars - charactersIgnoringModifiers:chars - isARepeat:[originalEvent isARepeat] - keyCode:aCocoaKeyCode]; - - NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType); - return keyBindings->Execute(modifiedEvent, aCallback, aCallbackData); -} - -NS_IMETHODIMP_(bool) -nsChildView::ExecuteNativeKeyBinding(NativeKeyBindingsType aType, - const WidgetKeyboardEvent& aEvent, - DoCommandCallback aCallback, - void* aCallbackData) -{ - // If the key is a cursor-movement arrow, and the current selection has - // vertical writing-mode, we'll remap so that the movement command - // generated (in terms of characters/lines) will be appropriate for - // the physical direction of the arrow. - if (aEvent.mKeyCode >= NS_VK_LEFT && aEvent.mKeyCode <= NS_VK_DOWN) { - WidgetQueryContentEvent query(true, eQuerySelectedText, this); - DispatchWindowEvent(query); - - if (query.mSucceeded && query.mReply.mWritingMode.IsVertical()) { - uint32_t geckoKey = 0; - uint32_t cocoaKey = 0; - - switch (aEvent.mKeyCode) { - case NS_VK_LEFT: - if (query.mReply.mWritingMode.IsVerticalLR()) { - geckoKey = NS_VK_UP; - cocoaKey = kVK_UpArrow; - } else { - geckoKey = NS_VK_DOWN; - cocoaKey = kVK_DownArrow; - } - break; - - case NS_VK_RIGHT: - if (query.mReply.mWritingMode.IsVerticalLR()) { - geckoKey = NS_VK_DOWN; - cocoaKey = kVK_DownArrow; - } else { - geckoKey = NS_VK_UP; - cocoaKey = kVK_UpArrow; - } - break; - - case NS_VK_UP: - geckoKey = NS_VK_LEFT; - cocoaKey = kVK_LeftArrow; - break; - - case NS_VK_DOWN: - geckoKey = NS_VK_RIGHT; - cocoaKey = kVK_RightArrow; - break; - } - - return ExecuteNativeKeyBindingRemapped(aType, aEvent, aCallback, - aCallbackData, - geckoKey, cocoaKey); - } - } - - NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType); - return keyBindings->Execute(aEvent, aCallback, aCallbackData); -} - -nsIMEUpdatePreference -nsChildView::GetIMEUpdatePreference() -{ - // XXX Shouldn't we move floating window which shows composition string - // when plugin has focus and its parent is scrolled or the window is - // moved? - return nsIMEUpdatePreference(); -} - -NSView<mozView>* nsChildView::GetEditorView() -{ - NSView<mozView>* editorView = mView; - // We need to get editor's view. E.g., when the focus is in the bookmark - // dialog, the view is <panel> element of the dialog. At this time, the key - // events are processed the parent window's view that has native focus. - WidgetQueryContentEvent textContent(true, eQueryTextContent, this); - textContent.InitForQueryTextContent(0, 0); - DispatchWindowEvent(textContent); - if (textContent.mSucceeded && textContent.mReply.mFocusedWidget) { - NSView<mozView>* view = static_cast<NSView<mozView>*>( - textContent.mReply.mFocusedWidget->GetNativeData(NS_NATIVE_WIDGET)); - if (view) - editorView = view; - } - return editorView; -} - -#pragma mark - - -void -nsChildView::CreateCompositor() -{ - nsBaseWidget::CreateCompositor(); - if (mCompositorBridgeChild) { - [(ChildView *)mView setUsingOMTCompositor:true]; - } -} - -void -nsChildView::ConfigureAPZCTreeManager() -{ - nsBaseWidget::ConfigureAPZCTreeManager(); -} - -void -nsChildView::ConfigureAPZControllerThread() -{ - if (gfxPrefs::AsyncPanZoomSeparateEventThread()) { - // The EventThreadRunner is the controller thread, but it doesn't - // have a MessageLoop. - APZThreadUtils::SetControllerThread(nullptr); - } else { - nsBaseWidget::ConfigureAPZControllerThread(); - } -} - -LayoutDeviceIntRect -nsChildView::RectContainingTitlebarControls() -{ - // Start with a thin strip at the top of the window for the highlight line. - NSRect rect = NSMakeRect(0, 0, [mView bounds].size.width, - [(ChildView*)mView cornerRadius]); - - // If we draw the titlebar title string, increase the height to the default - // titlebar height. This height does not necessarily include all the titlebar - // controls because we may have moved them further down, but at least it will - // include the whole title text. - BaseWindow* window = (BaseWindow*)[mView window]; - if ([window wantsTitleDrawn] && [window isKindOfClass:[ToolbarWindow class]]) { - CGFloat defaultTitlebarHeight = [(ToolbarWindow*)window titlebarHeight]; - rect.size.height = std::max(rect.size.height, defaultTitlebarHeight); - } - - // Add the rects of the titlebar controls. - for (id view in [window titlebarControls]) { - rect = NSUnionRect(rect, [mView convertRect:[view bounds] fromView:view]); - } - return CocoaPointsToDevPixels(rect); -} - -void -nsChildView::PrepareWindowEffects() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - bool canBeOpaque; - { - MutexAutoLock lock(mEffectsLock); - mShowsResizeIndicator = ShowsResizeIndicator(&mResizeIndicatorRect); - mHasRoundedBottomCorners = [(ChildView*)mView hasRoundedBottomCorners]; - CGFloat cornerRadius = [(ChildView*)mView cornerRadius]; - mDevPixelCornerRadius = cornerRadius * BackingScaleFactor(); - mIsCoveringTitlebar = [(ChildView*)mView isCoveringTitlebar]; - NSInteger styleMask = [[mView window] styleMask]; - bool wasFullscreen = mIsFullscreen; - mIsFullscreen = (styleMask & NSFullScreenWindowMask) || !(styleMask & NSTitledWindowMask); - - canBeOpaque = mIsFullscreen && wasFullscreen; - if (canBeOpaque && VibrancyManager::SystemSupportsVibrancy()) { - canBeOpaque = !EnsureVibrancyManager().HasVibrantRegions(); - } - if (mIsCoveringTitlebar) { - mTitlebarRect = RectContainingTitlebarControls(); - UpdateTitlebarCGContext(); - } - } - - // If we've just transitioned into or out of full screen then update the opacity on our GLContext. - if (canBeOpaque != mIsOpaque) { - mIsOpaque = canBeOpaque; - [(ChildView*)mView setGLOpaque:canBeOpaque]; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -nsChildView::CleanupWindowEffects() -{ - mResizerImage = nullptr; - mCornerMaskImage = nullptr; - mTitlebarImage = nullptr; -} - -bool -nsChildView::PreRender(WidgetRenderingContext* aContext) -{ - UniquePtr<GLManager> manager(GLManager::CreateGLManager(aContext->mLayerManager)); - if (!manager) { - return true; - } - - // The lock makes sure that we don't attempt to tear down the view while - // compositing. That would make us unable to call postRender on it when the - // composition is done, thus keeping the GL context locked forever. - mViewTearDownLock.Lock(); - - NSOpenGLContext *glContext = GLContextCGL::Cast(manager->gl())->GetNSOpenGLContext(); - - if (![(ChildView*)mView preRender:glContext]) { - mViewTearDownLock.Unlock(); - return false; - } - return true; -} - -void -nsChildView::PostRender(WidgetRenderingContext* aContext) -{ - UniquePtr<GLManager> manager(GLManager::CreateGLManager(aContext->mLayerManager)); - if (!manager) { - return; - } - NSOpenGLContext *glContext = GLContextCGL::Cast(manager->gl())->GetNSOpenGLContext(); - [(ChildView*)mView postRender:glContext]; - mViewTearDownLock.Unlock(); -} - -void -nsChildView::DrawWindowOverlay(WidgetRenderingContext* aContext, - LayoutDeviceIntRect aRect) -{ - mozilla::UniquePtr<GLManager> manager(GLManager::CreateGLManager(aContext->mLayerManager)); - if (manager) { - DrawWindowOverlay(manager.get(), aRect); - } -} - -void -nsChildView::DrawWindowOverlay(GLManager* aManager, LayoutDeviceIntRect aRect) -{ - GLContext* gl = aManager->gl(); - ScopedGLState scopedScissorTestState(gl, LOCAL_GL_SCISSOR_TEST, false); - - MaybeDrawTitlebar(aManager); - MaybeDrawResizeIndicator(aManager); - MaybeDrawRoundedCorners(aManager, aRect); -} - -static void -ClearRegion(gfx::DrawTarget *aDT, LayoutDeviceIntRegion aRegion) -{ - gfxUtils::ClipToRegion(aDT, aRegion.ToUnknownRegion()); - aDT->ClearRect(gfx::Rect(0, 0, aDT->GetSize().width, aDT->GetSize().height)); - aDT->PopClip(); -} - -static void -DrawResizer(CGContextRef aCtx) -{ - CGContextSetShouldAntialias(aCtx, false); - CGPoint points[6]; - points[0] = CGPointMake(13.0f, 4.0f); - points[1] = CGPointMake(3.0f, 14.0f); - points[2] = CGPointMake(13.0f, 8.0f); - points[3] = CGPointMake(7.0f, 14.0f); - points[4] = CGPointMake(13.0f, 12.0f); - points[5] = CGPointMake(11.0f, 14.0f); - CGContextSetRGBStrokeColor(aCtx, 0.00f, 0.00f, 0.00f, 0.15f); - CGContextStrokeLineSegments(aCtx, points, 6); - - points[0] = CGPointMake(13.0f, 5.0f); - points[1] = CGPointMake(4.0f, 14.0f); - points[2] = CGPointMake(13.0f, 9.0f); - points[3] = CGPointMake(8.0f, 14.0f); - points[4] = CGPointMake(13.0f, 13.0f); - points[5] = CGPointMake(12.0f, 14.0f); - CGContextSetRGBStrokeColor(aCtx, 0.13f, 0.13f, 0.13f, 0.54f); - CGContextStrokeLineSegments(aCtx, points, 6); - - points[0] = CGPointMake(13.0f, 6.0f); - points[1] = CGPointMake(5.0f, 14.0f); - points[2] = CGPointMake(13.0f, 10.0f); - points[3] = CGPointMake(9.0f, 14.0f); - points[5] = CGPointMake(13.0f, 13.9f); - points[4] = CGPointMake(13.0f, 14.0f); - CGContextSetRGBStrokeColor(aCtx, 0.84f, 0.84f, 0.84f, 0.55f); - CGContextStrokeLineSegments(aCtx, points, 6); -} - -void -nsChildView::MaybeDrawResizeIndicator(GLManager* aManager) -{ - MutexAutoLock lock(mEffectsLock); - if (!mShowsResizeIndicator) { - return; - } - - if (!mResizerImage) { - mResizerImage = MakeUnique<RectTextureImage>(); - } - - LayoutDeviceIntSize size = mResizeIndicatorRect.Size(); - mResizerImage->UpdateIfNeeded(size, LayoutDeviceIntRegion(), ^(gfx::DrawTarget* drawTarget, const LayoutDeviceIntRegion& updateRegion) { - ClearRegion(drawTarget, updateRegion); - gfx::BorrowedCGContext borrow(drawTarget); - DrawResizer(borrow.cg); - borrow.Finish(); - }); - - mResizerImage->Draw(aManager, mResizeIndicatorRect.TopLeft()); -} - -// Draw the highlight line at the top of the titlebar. -// This function draws into the current NSGraphicsContext and assumes flippedness. -static void -DrawTitlebarHighlight(NSSize aWindowSize, CGFloat aRadius, CGFloat aDevicePixelWidth) -{ - [NSGraphicsContext saveGraphicsState]; - - // Set up the clip path. We start with the outer rectangle and cut out a - // slightly smaller inner rectangle with rounded corners. - // The outer corners of the resulting path will be square, but they will be - // masked away in a later step. - NSBezierPath* path = [NSBezierPath bezierPath]; - [path setWindingRule:NSEvenOddWindingRule]; - NSRect pathRect = NSMakeRect(0, 0, aWindowSize.width, aRadius + 2); - [path appendBezierPathWithRect:pathRect]; - pathRect = NSInsetRect(pathRect, aDevicePixelWidth, aDevicePixelWidth); - CGFloat innerRadius = aRadius - aDevicePixelWidth; - [path appendBezierPathWithRoundedRect:pathRect xRadius:innerRadius yRadius:innerRadius]; - [path addClip]; - - // Now we fill the path with a subtle highlight gradient. - // We don't use NSGradient because it's 5x to 15x slower than the manual fill, - // as indicated by the performance test in bug 880620. - for (CGFloat y = 0; y < aRadius; y += aDevicePixelWidth) { - CGFloat t = y / aRadius; - [[NSColor colorWithDeviceWhite:1.0 alpha:0.4 * (1.0 - t)] set]; - NSRectFillUsingOperation(NSMakeRect(0, y, aWindowSize.width, aDevicePixelWidth), NSCompositeSourceOver); - } - - [NSGraphicsContext restoreGraphicsState]; -} - -static CGContextRef -CreateCGContext(const LayoutDeviceIntSize& aSize) -{ - CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); - CGContextRef ctx = - CGBitmapContextCreate(NULL, - aSize.width, - aSize.height, - 8 /* bitsPerComponent */, - aSize.width * 4, - cs, - kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst); - CGColorSpaceRelease(cs); - - CGContextTranslateCTM(ctx, 0, aSize.height); - CGContextScaleCTM(ctx, 1, -1); - CGContextSetInterpolationQuality(ctx, kCGInterpolationLow); - - return ctx; -} - -LayoutDeviceIntSize -TextureSizeForSize(const LayoutDeviceIntSize& aSize) -{ - return LayoutDeviceIntSize(RoundUpPow2(aSize.width), - RoundUpPow2(aSize.height)); -} - -// When this method is entered, mEffectsLock is already being held. -void -nsChildView::UpdateTitlebarCGContext() -{ - if (mTitlebarRect.IsEmpty()) { - ReleaseTitlebarCGContext(); - return; - } - - NSRect titlebarRect = DevPixelsToCocoaPoints(mTitlebarRect); - NSRect dirtyRect = [mView convertRect:[(BaseWindow*)[mView window] getAndResetNativeDirtyRect] fromView:nil]; - NSRect dirtyTitlebarRect = NSIntersectionRect(titlebarRect, dirtyRect); - - LayoutDeviceIntSize texSize = TextureSizeForSize(mTitlebarRect.Size()); - if (!mTitlebarCGContext || - CGBitmapContextGetWidth(mTitlebarCGContext) != size_t(texSize.width) || - CGBitmapContextGetHeight(mTitlebarCGContext) != size_t(texSize.height)) { - dirtyTitlebarRect = titlebarRect; - - ReleaseTitlebarCGContext(); - - mTitlebarCGContext = CreateCGContext(texSize); - } - - if (NSIsEmptyRect(dirtyTitlebarRect)) { - return; - } - - CGContextRef ctx = mTitlebarCGContext; - - CGContextSaveGState(ctx); - - double scale = BackingScaleFactor(); - CGContextScaleCTM(ctx, scale, scale); - - CGContextClipToRect(ctx, NSRectToCGRect(dirtyTitlebarRect)); - CGContextClearRect(ctx, NSRectToCGRect(dirtyTitlebarRect)); - - NSGraphicsContext* oldContext = [NSGraphicsContext currentContext]; - - CGContextSaveGState(ctx); - - BaseWindow* window = (BaseWindow*)[mView window]; - NSView* frameView = [[window contentView] superview]; - if (![frameView isFlipped]) { - CGContextTranslateCTM(ctx, 0, [frameView bounds].size.height); - CGContextScaleCTM(ctx, 1, -1); - } - NSGraphicsContext* context = [NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:[frameView isFlipped]]; - [NSGraphicsContext setCurrentContext:context]; - - // Draw the title string. - if ([window wantsTitleDrawn] && [frameView respondsToSelector:@selector(_drawTitleBar:)]) { - [frameView _drawTitleBar:[frameView bounds]]; - } - - // Draw the titlebar controls into the titlebar image. - for (id view in [window titlebarControls]) { - NSRect viewFrame = [view frame]; - NSRect viewRect = [mView convertRect:viewFrame fromView:frameView]; - if (!NSIntersectsRect(dirtyTitlebarRect, viewRect)) { - continue; - } - // All of the titlebar controls we're interested in are subclasses of - // NSButton. - if (![view isKindOfClass:[NSButton class]]) { - continue; - } - NSButton *button = (NSButton *) view; - id cellObject = [button cell]; - if (![cellObject isKindOfClass:[NSCell class]]) { - continue; - } - NSCell *cell = (NSCell *) cellObject; - - CGContextSaveGState(ctx); - CGContextTranslateCTM(ctx, viewFrame.origin.x, viewFrame.origin.y); - - if ([context isFlipped] != [view isFlipped]) { - CGContextTranslateCTM(ctx, 0, viewFrame.size.height); - CGContextScaleCTM(ctx, 1, -1); - } - - [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:[view isFlipped]]]; - - if ([window useBrightTitlebarForeground] && !nsCocoaFeatures::OnYosemiteOrLater() && - view == [window standardWindowButton:NSWindowFullScreenButton]) { - // Make the fullscreen button visible on dark titlebar backgrounds by - // drawing it into a new transparency layer and turning it white. - CGRect r = NSRectToCGRect([view bounds]); - CGContextBeginTransparencyLayerWithRect(ctx, r, nullptr); - - // Draw twice for double opacity. - [cell drawWithFrame:[button bounds] inView:button]; - [cell drawWithFrame:[button bounds] inView:button]; - - // Make it white. - CGContextSetBlendMode(ctx, kCGBlendModeSourceIn); - CGContextSetRGBFillColor(ctx, 1, 1, 1, 1); - CGContextFillRect(ctx, r); - CGContextSetBlendMode(ctx, kCGBlendModeNormal); - - CGContextEndTransparencyLayer(ctx); - } else { - [cell drawWithFrame:[button bounds] inView:button]; - } - - [NSGraphicsContext setCurrentContext:context]; - CGContextRestoreGState(ctx); - } - - CGContextRestoreGState(ctx); - - DrawTitlebarHighlight([frameView bounds].size, [(ChildView*)mView cornerRadius], - DevPixelsToCocoaPoints(1)); - - [NSGraphicsContext setCurrentContext:oldContext]; - - CGContextRestoreGState(ctx); - - mUpdatedTitlebarRegion.OrWith(CocoaPointsToDevPixels(dirtyTitlebarRect)); -} - -// This method draws an overlay in the top of the window which contains the -// titlebar controls (e.g. close, min, zoom, fullscreen) and the titlebar -// highlight effect. -// This is necessary because the real titlebar controls are covered by our -// OpenGL context. Note that in terms of the NSView hierarchy, our ChildView -// is actually below the titlebar controls - that's why hovering and clicking -// them works as expected - but their visual representation is only drawn into -// the normal window buffer, and the window buffer surface lies below the -// GLContext surface. In order to make the titlebar controls visible, we have -// to redraw them inside the OpenGL context surface. -void -nsChildView::MaybeDrawTitlebar(GLManager* aManager) -{ - MutexAutoLock lock(mEffectsLock); - if (!mIsCoveringTitlebar || mIsFullscreen) { - return; - } - - LayoutDeviceIntRegion updatedTitlebarRegion; - updatedTitlebarRegion.And(mUpdatedTitlebarRegion, mTitlebarRect); - mUpdatedTitlebarRegion.SetEmpty(); - - if (!mTitlebarImage) { - mTitlebarImage = MakeUnique<RectTextureImage>(); - } - - mTitlebarImage->UpdateFromCGContext(mTitlebarRect.Size(), - updatedTitlebarRegion, - mTitlebarCGContext); - - mTitlebarImage->Draw(aManager, mTitlebarRect.TopLeft()); -} - -static void -DrawTopLeftCornerMask(CGContextRef aCtx, int aRadius) -{ - CGContextSetRGBFillColor(aCtx, 1.0, 1.0, 1.0, 1.0); - CGContextFillEllipseInRect(aCtx, CGRectMake(0, 0, aRadius * 2, aRadius * 2)); -} - -void -nsChildView::MaybeDrawRoundedCorners(GLManager* aManager, - const LayoutDeviceIntRect& aRect) -{ - MutexAutoLock lock(mEffectsLock); - - if (!mCornerMaskImage) { - mCornerMaskImage = MakeUnique<RectTextureImage>(); - } - - LayoutDeviceIntSize size(mDevPixelCornerRadius, mDevPixelCornerRadius); - mCornerMaskImage->UpdateIfNeeded(size, LayoutDeviceIntRegion(), ^(gfx::DrawTarget* drawTarget, const LayoutDeviceIntRegion& updateRegion) { - ClearRegion(drawTarget, updateRegion); - RefPtr<gfx::PathBuilder> builder = drawTarget->CreatePathBuilder(); - builder->Arc(gfx::Point(mDevPixelCornerRadius, mDevPixelCornerRadius), mDevPixelCornerRadius, 0, 2.0f * M_PI); - RefPtr<gfx::Path> path = builder->Finish(); - drawTarget->Fill(path, - gfx::ColorPattern(gfx::Color(1.0, 1.0, 1.0, 1.0)), - gfx::DrawOptions(1.0f, gfx::CompositionOp::OP_SOURCE)); - }); - - // Use operator destination in: multiply all 4 channels with source alpha. - aManager->gl()->fBlendFuncSeparate(LOCAL_GL_ZERO, LOCAL_GL_SRC_ALPHA, - LOCAL_GL_ZERO, LOCAL_GL_SRC_ALPHA); - - Matrix4x4 flipX = Matrix4x4::Scaling(-1, 1, 1); - Matrix4x4 flipY = Matrix4x4::Scaling(1, -1, 1); - - if (mIsCoveringTitlebar && !mIsFullscreen) { - // Mask the top corners. - mCornerMaskImage->Draw(aManager, aRect.TopLeft()); - mCornerMaskImage->Draw(aManager, aRect.TopRight(), flipX); - } - - if (mHasRoundedBottomCorners && !mIsFullscreen) { - // Mask the bottom corners. - mCornerMaskImage->Draw(aManager, aRect.BottomLeft(), flipY); - mCornerMaskImage->Draw(aManager, aRect.BottomRight(), flipY * flipX); - } - - // Reset blend mode. - aManager->gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA, - LOCAL_GL_ONE, LOCAL_GL_ONE); -} - -static int32_t -FindTitlebarBottom(const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries, - int32_t aWindowWidth) -{ - int32_t titlebarBottom = 0; - for (uint32_t i = 0; i < aThemeGeometries.Length(); ++i) { - const nsIWidget::ThemeGeometry& g = aThemeGeometries[i]; - if ((g.mType == nsNativeThemeCocoa::eThemeGeometryTypeTitlebar) && - g.mRect.X() <= 0 && - g.mRect.XMost() >= aWindowWidth && - g.mRect.Y() <= 0) { - titlebarBottom = std::max(titlebarBottom, g.mRect.YMost()); - } - } - return titlebarBottom; -} - -static int32_t -FindUnifiedToolbarBottom(const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries, - int32_t aWindowWidth, int32_t aTitlebarBottom) -{ - int32_t unifiedToolbarBottom = aTitlebarBottom; - for (uint32_t i = 0; i < aThemeGeometries.Length(); ++i) { - const nsIWidget::ThemeGeometry& g = aThemeGeometries[i]; - if ((g.mType == nsNativeThemeCocoa::eThemeGeometryTypeToolbar) && - g.mRect.X() <= 0 && - g.mRect.XMost() >= aWindowWidth && - g.mRect.Y() <= aTitlebarBottom) { - unifiedToolbarBottom = std::max(unifiedToolbarBottom, g.mRect.YMost()); - } - } - return unifiedToolbarBottom; -} - -static LayoutDeviceIntRect -FindFirstRectOfType(const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries, - nsITheme::ThemeGeometryType aThemeGeometryType) -{ - for (uint32_t i = 0; i < aThemeGeometries.Length(); ++i) { - const nsIWidget::ThemeGeometry& g = aThemeGeometries[i]; - if (g.mType == aThemeGeometryType) { - return g.mRect; - } - } - return LayoutDeviceIntRect(); -} - -void -nsChildView::UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) -{ - if (![mView window]) - return; - - UpdateVibrancy(aThemeGeometries); - - if (![[mView window] isKindOfClass:[ToolbarWindow class]]) - return; - - // Update unified toolbar height and sheet attachment position. - int32_t windowWidth = mBounds.width; - int32_t titlebarBottom = FindTitlebarBottom(aThemeGeometries, windowWidth); - int32_t unifiedToolbarBottom = - FindUnifiedToolbarBottom(aThemeGeometries, windowWidth, titlebarBottom); - int32_t toolboxBottom = - FindFirstRectOfType(aThemeGeometries, nsNativeThemeCocoa::eThemeGeometryTypeToolbox).YMost(); - - ToolbarWindow* win = (ToolbarWindow*)[mView window]; - bool drawsContentsIntoWindowFrame = [win drawsContentsIntoWindowFrame]; - int32_t titlebarHeight = CocoaPointsToDevPixels([win titlebarHeight]); - int32_t contentOffset = drawsContentsIntoWindowFrame ? titlebarHeight : 0; - int32_t devUnifiedHeight = titlebarHeight + unifiedToolbarBottom - contentOffset; - [win setUnifiedToolbarHeight:DevPixelsToCocoaPoints(devUnifiedHeight)]; - int32_t devSheetPosition = titlebarHeight + std::max(toolboxBottom, unifiedToolbarBottom) - contentOffset; - [win setSheetAttachmentPosition:DevPixelsToCocoaPoints(devSheetPosition)]; - - // Update titlebar control offsets. - LayoutDeviceIntRect windowButtonRect = FindFirstRectOfType(aThemeGeometries, nsNativeThemeCocoa::eThemeGeometryTypeWindowButtons); - [win placeWindowButtons:[mView convertRect:DevPixelsToCocoaPoints(windowButtonRect) toView:nil]]; - LayoutDeviceIntRect fullScreenButtonRect = FindFirstRectOfType(aThemeGeometries, nsNativeThemeCocoa::eThemeGeometryTypeFullscreenButton); - [win placeFullScreenButton:[mView convertRect:DevPixelsToCocoaPoints(fullScreenButtonRect) toView:nil]]; -} - -static LayoutDeviceIntRegion -GatherThemeGeometryRegion(const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries, - nsITheme::ThemeGeometryType aThemeGeometryType) -{ - LayoutDeviceIntRegion region; - for (size_t i = 0; i < aThemeGeometries.Length(); ++i) { - const nsIWidget::ThemeGeometry& g = aThemeGeometries[i]; - if (g.mType == aThemeGeometryType) { - region.OrWith(g.mRect); - } - } - return region; -} - -template<typename Region> -static void MakeRegionsNonOverlappingImpl(Region& aOutUnion) { } - -template<typename Region, typename ... Regions> -static void MakeRegionsNonOverlappingImpl(Region& aOutUnion, Region& aFirst, Regions& ... aRest) -{ - MakeRegionsNonOverlappingImpl(aOutUnion, aRest...); - aFirst.SubOut(aOutUnion); - aOutUnion.OrWith(aFirst); -} - -// Subtracts parts from regions in such a way that they don't have any overlap. -// Each region in the argument list will have the union of all the regions -// *following* it subtracted from itself. In other words, the arguments are -// sorted low priority to high priority. -template<typename Region, typename ... Regions> -static void MakeRegionsNonOverlapping(Region& aFirst, Regions& ... aRest) -{ - Region unionOfAll; - MakeRegionsNonOverlappingImpl(unionOfAll, aFirst, aRest...); -} - -void -nsChildView::UpdateVibrancy(const nsTArray<ThemeGeometry>& aThemeGeometries) -{ - if (!VibrancyManager::SystemSupportsVibrancy()) { - return; - } - - LayoutDeviceIntRegion sheetRegion = - GatherThemeGeometryRegion(aThemeGeometries, nsNativeThemeCocoa::eThemeGeometryTypeSheet); - LayoutDeviceIntRegion vibrantLightRegion = - GatherThemeGeometryRegion(aThemeGeometries, nsNativeThemeCocoa::eThemeGeometryTypeVibrancyLight); - LayoutDeviceIntRegion vibrantDarkRegion = - GatherThemeGeometryRegion(aThemeGeometries, nsNativeThemeCocoa::eThemeGeometryTypeVibrancyDark); - LayoutDeviceIntRegion menuRegion = - GatherThemeGeometryRegion(aThemeGeometries, nsNativeThemeCocoa::eThemeGeometryTypeMenu); - LayoutDeviceIntRegion tooltipRegion = - GatherThemeGeometryRegion(aThemeGeometries, nsNativeThemeCocoa::eThemeGeometryTypeTooltip); - LayoutDeviceIntRegion highlightedMenuItemRegion = - GatherThemeGeometryRegion(aThemeGeometries, nsNativeThemeCocoa::eThemeGeometryTypeHighlightedMenuItem); - LayoutDeviceIntRegion sourceListRegion = - GatherThemeGeometryRegion(aThemeGeometries, nsNativeThemeCocoa::eThemeGeometryTypeSourceList); - LayoutDeviceIntRegion sourceListSelectionRegion = - GatherThemeGeometryRegion(aThemeGeometries, nsNativeThemeCocoa::eThemeGeometryTypeSourceListSelection); - LayoutDeviceIntRegion activeSourceListSelectionRegion = - GatherThemeGeometryRegion(aThemeGeometries, nsNativeThemeCocoa::eThemeGeometryTypeActiveSourceListSelection); - - MakeRegionsNonOverlapping(sheetRegion, vibrantLightRegion, vibrantDarkRegion, - menuRegion, tooltipRegion, highlightedMenuItemRegion, - sourceListRegion, sourceListSelectionRegion, - activeSourceListSelectionRegion); - - auto& vm = EnsureVibrancyManager(); - vm.UpdateVibrantRegion(VibrancyType::LIGHT, vibrantLightRegion); - vm.UpdateVibrantRegion(VibrancyType::TOOLTIP, tooltipRegion); - vm.UpdateVibrantRegion(VibrancyType::MENU, menuRegion); - vm.UpdateVibrantRegion(VibrancyType::HIGHLIGHTED_MENUITEM, highlightedMenuItemRegion); - vm.UpdateVibrantRegion(VibrancyType::SHEET, sheetRegion); - vm.UpdateVibrantRegion(VibrancyType::SOURCE_LIST, sourceListRegion); - vm.UpdateVibrantRegion(VibrancyType::SOURCE_LIST_SELECTION, sourceListSelectionRegion); - vm.UpdateVibrantRegion(VibrancyType::ACTIVE_SOURCE_LIST_SELECTION, activeSourceListSelectionRegion); - vm.UpdateVibrantRegion(VibrancyType::DARK, vibrantDarkRegion); -} - -static VibrancyType -ThemeGeometryTypeToVibrancyType(nsITheme::ThemeGeometryType aThemeGeometryType) -{ - switch (aThemeGeometryType) { - case nsNativeThemeCocoa::eThemeGeometryTypeVibrancyLight: - return VibrancyType::LIGHT; - case nsNativeThemeCocoa::eThemeGeometryTypeVibrancyDark: - return VibrancyType::DARK; - case nsNativeThemeCocoa::eThemeGeometryTypeTooltip: - return VibrancyType::TOOLTIP; - case nsNativeThemeCocoa::eThemeGeometryTypeMenu: - return VibrancyType::MENU; - case nsNativeThemeCocoa::eThemeGeometryTypeHighlightedMenuItem: - return VibrancyType::HIGHLIGHTED_MENUITEM; - case nsNativeThemeCocoa::eThemeGeometryTypeSheet: - return VibrancyType::SHEET; - case nsNativeThemeCocoa::eThemeGeometryTypeSourceList: - return VibrancyType::SOURCE_LIST; - case nsNativeThemeCocoa::eThemeGeometryTypeSourceListSelection: - return VibrancyType::SOURCE_LIST_SELECTION; - case nsNativeThemeCocoa::eThemeGeometryTypeActiveSourceListSelection: - return VibrancyType::ACTIVE_SOURCE_LIST_SELECTION; - default: - MOZ_CRASH(); - } -} - -NSColor* -nsChildView::VibrancyFillColorForThemeGeometryType(nsITheme::ThemeGeometryType aThemeGeometryType) -{ - if (VibrancyManager::SystemSupportsVibrancy()) { - return EnsureVibrancyManager().VibrancyFillColorForType( - ThemeGeometryTypeToVibrancyType(aThemeGeometryType)); - } - return [NSColor whiteColor]; -} - -NSColor* -nsChildView::VibrancyFontSmoothingBackgroundColorForThemeGeometryType(nsITheme::ThemeGeometryType aThemeGeometryType) -{ - if (VibrancyManager::SystemSupportsVibrancy()) { - return EnsureVibrancyManager().VibrancyFontSmoothingBackgroundColorForType( - ThemeGeometryTypeToVibrancyType(aThemeGeometryType)); - } - return [NSColor clearColor]; -} - -mozilla::VibrancyManager& -nsChildView::EnsureVibrancyManager() -{ - MOZ_ASSERT(mView, "Only call this once we have a view!"); - if (!mVibrancyManager) { - mVibrancyManager = MakeUnique<VibrancyManager>(*this, [mView vibrancyViewsContainer]); - } - return *mVibrancyManager; -} - -nsChildView::SwipeInfo -nsChildView::SendMayStartSwipe(const mozilla::PanGestureInput& aSwipeStartEvent) -{ - nsCOMPtr<nsIWidget> kungFuDeathGrip(this); - - uint32_t direction = (aSwipeStartEvent.mPanDisplacement.x > 0.0) - ? (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_RIGHT - : (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_LEFT; - - // We're ready to start the animation. Tell Gecko about it, and at the same - // time ask it if it really wants to start an animation for this event. - // This event also reports back the directions that we can swipe in. - LayoutDeviceIntPoint position = - RoundedToInt(aSwipeStartEvent.mPanStartPoint * ScreenToLayoutDeviceScale(1)); - WidgetSimpleGestureEvent geckoEvent = - SwipeTracker::CreateSwipeGestureEvent(eSwipeGestureMayStart, this, - position); - geckoEvent.mDirection = direction; - geckoEvent.mDelta = 0.0; - geckoEvent.mAllowedDirections = 0; - bool shouldStartSwipe = DispatchWindowEvent(geckoEvent); // event cancelled == swipe should start - - SwipeInfo result = { shouldStartSwipe, geckoEvent.mAllowedDirections }; - return result; -} - -void -nsChildView::TrackScrollEventAsSwipe(const mozilla::PanGestureInput& aSwipeStartEvent, - uint32_t aAllowedDirections) -{ - // If a swipe is currently being tracked kill it -- it's been interrupted - // by another gesture event. - if (mSwipeTracker) { - mSwipeTracker->CancelSwipe(); - mSwipeTracker->Destroy(); - mSwipeTracker = nullptr; - } - - uint32_t direction = (aSwipeStartEvent.mPanDisplacement.x > 0.0) - ? (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_RIGHT - : (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_LEFT; - - mSwipeTracker = new SwipeTracker(*this, aSwipeStartEvent, - aAllowedDirections, direction); - - if (!mAPZC) { - mCurrentPanGestureBelongsToSwipe = true; - } -} - -void -nsChildView::SwipeFinished() -{ - mSwipeTracker = nullptr; -} - -already_AddRefed<gfx::DrawTarget> -nsChildView::StartRemoteDrawingInRegion(LayoutDeviceIntRegion& aInvalidRegion, - BufferMode* aBufferMode) -{ - // should have created the GLPresenter in InitCompositor. - MOZ_ASSERT(mGLPresenter); - if (!mGLPresenter) { - mGLPresenter = GLPresenter::CreateForWindow(this); - - if (!mGLPresenter) { - return nullptr; - } - } - - LayoutDeviceIntRegion dirtyRegion(aInvalidRegion); - LayoutDeviceIntSize renderSize = mBounds.Size(); - - if (!mBasicCompositorImage) { - mBasicCompositorImage = MakeUnique<RectTextureImage>(); - } - - RefPtr<gfx::DrawTarget> drawTarget = - mBasicCompositorImage->BeginUpdate(renderSize, dirtyRegion); - - if (!drawTarget) { - // Composite unchanged textures. - DoRemoteComposition(mBounds); - return nullptr; - } - - aInvalidRegion = mBasicCompositorImage->GetUpdateRegion(); - *aBufferMode = BufferMode::BUFFER_NONE; - - return drawTarget.forget(); -} - -void -nsChildView::EndRemoteDrawing() -{ - mBasicCompositorImage->EndUpdate(); - DoRemoteComposition(mBounds); -} - -void -nsChildView::CleanupRemoteDrawing() -{ - mBasicCompositorImage = nullptr; - mCornerMaskImage = nullptr; - mResizerImage = nullptr; - mTitlebarImage = nullptr; - mGLPresenter = nullptr; -} - -bool -nsChildView::InitCompositor(Compositor* aCompositor) -{ - if (aCompositor->GetBackendType() == LayersBackend::LAYERS_BASIC) { - if (!mGLPresenter) { - mGLPresenter = GLPresenter::CreateForWindow(this); - } - - return !!mGLPresenter; - } - return true; -} - -void -nsChildView::DoRemoteComposition(const LayoutDeviceIntRect& aRenderRect) -{ - if (![(ChildView*)mView preRender:mGLPresenter->GetNSOpenGLContext()]) { - return; - } - mGLPresenter->BeginFrame(aRenderRect.Size()); - - // Draw the result from the basic compositor. - mBasicCompositorImage->Draw(mGLPresenter.get(), LayoutDeviceIntPoint(0, 0)); - - // DrawWindowOverlay doesn't do anything for non-GL, so it didn't paint - // anything during the basic compositor transaction. Draw the overlay now. - DrawWindowOverlay(mGLPresenter.get(), aRenderRect); - - mGLPresenter->EndFrame(); - - [(ChildView*)mView postRender:mGLPresenter->GetNSOpenGLContext()]; -} - -@interface NonDraggableView : NSView -@end - -@implementation NonDraggableView -- (BOOL)mouseDownCanMoveWindow { return NO; } -- (NSView*)hitTest:(NSPoint)aPoint { return nil; } -@end - -void -nsChildView::UpdateWindowDraggingRegion(const LayoutDeviceIntRegion& aRegion) -{ - // mView returns YES from mouseDownCanMoveWindow, so we need to put NSViews - // that return NO from mouseDownCanMoveWindow in the places that shouldn't - // be draggable. We can't do it the other way round because returning - // YES from mouseDownCanMoveWindow doesn't have any effect if there's a - // superview that returns NO. - LayoutDeviceIntRegion nonDraggable; - nonDraggable.Sub(LayoutDeviceIntRect(0, 0, mBounds.width, mBounds.height), aRegion); - - __block bool changed = false; - - // Suppress calls to setNeedsDisplay during NSView geometry changes. - ManipulateViewWithoutNeedingDisplay(mView, ^() { - changed = mNonDraggableRegion.UpdateRegion( - nonDraggable, *this, [mView nonDraggableViewsContainer], ^() { - return [[NonDraggableView alloc] initWithFrame:NSZeroRect]; - }); - }); - - if (changed) { - // Trigger an update to the window server. This will call - // mouseDownCanMoveWindow. - // Doing this manually is only necessary because we're suppressing - // setNeedsDisplay calls above. - [[mView window] setMovableByWindowBackground:NO]; - [[mView window] setMovableByWindowBackground:YES]; - } -} - -void -nsChildView::ReportSwipeStarted(uint64_t aInputBlockId, - bool aStartSwipe) -{ - if (mSwipeEventQueue && mSwipeEventQueue->inputBlockId == aInputBlockId) { - if (aStartSwipe) { - PanGestureInput& startEvent = mSwipeEventQueue->queuedEvents[0]; - TrackScrollEventAsSwipe(startEvent, mSwipeEventQueue->allowedDirections); - for (size_t i = 1; i < mSwipeEventQueue->queuedEvents.Length(); i++) { - mSwipeTracker->ProcessEvent(mSwipeEventQueue->queuedEvents[i]); - } - } - mSwipeEventQueue = nullptr; - } -} - -void -nsChildView::DispatchAPZWheelInputEvent(InputData& aEvent, bool aCanTriggerSwipe) -{ - if (mSwipeTracker && aEvent.mInputType == PANGESTURE_INPUT) { - // Give the swipe tracker a first pass at the event. If a new pan gesture - // has been started since the beginning of the swipe, the swipe tracker - // will know to ignore the event. - nsEventStatus status = mSwipeTracker->ProcessEvent(aEvent.AsPanGestureInput()); - if (status == nsEventStatus_eConsumeNoDefault) { - return; - } - } - - WidgetWheelEvent event(true, eWheel, this); - - if (mAPZC) { - uint64_t inputBlockId = 0; - ScrollableLayerGuid guid; - - nsEventStatus result = mAPZC->ReceiveInputEvent(aEvent, &guid, &inputBlockId); - if (result == nsEventStatus_eConsumeNoDefault) { - return; - } - - switch(aEvent.mInputType) { - case PANGESTURE_INPUT: { - PanGestureInput& panInput = aEvent.AsPanGestureInput(); - - event = panInput.ToWidgetWheelEvent(this); - if (aCanTriggerSwipe) { - SwipeInfo swipeInfo = SendMayStartSwipe(panInput); - event.mCanTriggerSwipe = swipeInfo.wantsSwipe; - if (swipeInfo.wantsSwipe) { - if (result == nsEventStatus_eIgnore) { - // APZ has determined and that scrolling horizontally in the - // requested direction is impossible, so it didn't do any - // scrolling for the event. - // We know now that MayStartSwipe wants a swipe, so we can start - // the swipe now. - TrackScrollEventAsSwipe(panInput, swipeInfo.allowedDirections); - } else { - // We don't know whether this event can start a swipe, so we need - // to queue up events and wait for a call to ReportSwipeStarted. - // APZ might already have started scrolling in response to the - // event if it knew that it's the right thing to do. In that case - // we'll still get a call to ReportSwipeStarted, and we will - // discard the queued events at that point. - mSwipeEventQueue = MakeUnique<SwipeEventQueue>(swipeInfo.allowedDirections, - inputBlockId); - } - } - } - - if (mSwipeEventQueue && mSwipeEventQueue->inputBlockId == inputBlockId) { - mSwipeEventQueue->queuedEvents.AppendElement(panInput); - } - break; - } - case SCROLLWHEEL_INPUT: { - event = aEvent.AsScrollWheelInput().ToWidgetWheelEvent(this); - break; - }; - default: - MOZ_CRASH("unsupported event type"); - return; - } - if (event.mMessage == eWheel && - (event.mDeltaX != 0 || event.mDeltaY != 0)) { - ProcessUntransformedAPZEvent(&event, guid, inputBlockId, result); - } - return; - } - - nsEventStatus status; - switch(aEvent.mInputType) { - case PANGESTURE_INPUT: { - PanGestureInput panInput = aEvent.AsPanGestureInput(); - if (panInput.mType == PanGestureInput::PANGESTURE_MAYSTART || - panInput.mType == PanGestureInput::PANGESTURE_START) { - mCurrentPanGestureBelongsToSwipe = false; - } - if (mCurrentPanGestureBelongsToSwipe) { - // Ignore this event. It's a momentum event from a scroll gesture - // that was processed as a swipe, and the swipe animation has - // already finished (so mSwipeTracker is already null). - MOZ_ASSERT(panInput.IsMomentum(), - "If the fingers are still on the touchpad, we should still have a SwipeTracker, and it should have consumed this event."); - return; - } - - event = panInput.ToWidgetWheelEvent(this); - if (aCanTriggerSwipe) { - SwipeInfo swipeInfo = SendMayStartSwipe(panInput); - - // We're in the non-APZ case here, but we still want to know whether - // the event was routed to a child process, so we use InputAPZContext - // to get that piece of information. - ScrollableLayerGuid guid; - InputAPZContext context(guid, 0, nsEventStatus_eIgnore); - - event.mCanTriggerSwipe = swipeInfo.wantsSwipe; - DispatchEvent(&event, status); - if (swipeInfo.wantsSwipe) { - if (context.WasRoutedToChildProcess()) { - // We don't know whether this event can start a swipe, so we need - // to queue up events and wait for a call to ReportSwipeStarted. - mSwipeEventQueue = MakeUnique<SwipeEventQueue>(swipeInfo.allowedDirections, 0); - } else if (event.TriggersSwipe()) { - TrackScrollEventAsSwipe(panInput, swipeInfo.allowedDirections); - } - } - - if (mSwipeEventQueue && mSwipeEventQueue->inputBlockId == 0) { - mSwipeEventQueue->queuedEvents.AppendElement(panInput); - } - return; - } - break; - } - case SCROLLWHEEL_INPUT: { - event = aEvent.AsScrollWheelInput().ToWidgetWheelEvent(this); - break; - } - default: - MOZ_CRASH("unexpected event type"); - return; - } - if (event.mMessage == eWheel && - (event.mDeltaX != 0 || event.mDeltaY != 0)) { - DispatchEvent(&event, status); - } -} - -// When using 10.11, calling showDefinitionForAttributedString causes the -// following exception on LookupViewService. (rdar://26476091) -// -// Exception: decodeObjectForKey: class "TitlebarAndBackgroundColor" not -// loaded or does not exist -// -// So we set temporary color that is NSColor before calling it. - -class MOZ_RAII AutoBackgroundSetter final { -public: - explicit AutoBackgroundSetter(NSView* aView) { - if (nsCocoaFeatures::OnElCapitanOrLater() && - [[aView window] isKindOfClass:[ToolbarWindow class]]) { - mWindow = [(ToolbarWindow*)[aView window] retain]; - [mWindow setTemporaryBackgroundColor]; - } else { - mWindow = nullptr; - } - } - - ~AutoBackgroundSetter() { - if (mWindow) { - [mWindow restoreBackgroundColor]; - [mWindow release]; - } - } - -private: - ToolbarWindow* mWindow; // strong -}; - -void -nsChildView::LookUpDictionary( - const nsAString& aText, - const nsTArray<mozilla::FontRange>& aFontRangeArray, - const bool aIsVertical, - const LayoutDeviceIntPoint& aPoint) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - NSMutableAttributedString* attrStr = - nsCocoaUtils::GetNSMutableAttributedString(aText, aFontRangeArray, - aIsVertical, - BackingScaleFactor()); - NSPoint pt = - nsCocoaUtils::DevPixelsToCocoaPoints(aPoint, BackingScaleFactor()); - NSDictionary* attributes = [attrStr attributesAtIndex:0 effectiveRange:nil]; - NSFont* font = [attributes objectForKey:NSFontAttributeName]; - if (font) { - if (aIsVertical) { - pt.x -= [font descender]; - } else { - pt.y += [font ascender]; - } - } - - AutoBackgroundSetter setter(mView); - [mView showDefinitionForAttributedString:attrStr atPoint:pt]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -#ifdef ACCESSIBILITY -already_AddRefed<a11y::Accessible> -nsChildView::GetDocumentAccessible() -{ - if (!mozilla::a11y::ShouldA11yBeEnabled()) - return nullptr; - - if (mAccessible) { - RefPtr<a11y::Accessible> ret; - CallQueryReferent(mAccessible.get(), - static_cast<a11y::Accessible**>(getter_AddRefs(ret))); - return ret.forget(); - } - - // need to fetch the accessible anew, because it has gone away. - // cache the accessible in our weak ptr - RefPtr<a11y::Accessible> acc = GetRootAccessible(); - mAccessible = do_GetWeakReference(acc.get()); - - return acc.forget(); -} -#endif - -// GLPresenter implementation - -GLPresenter::GLPresenter(GLContext* aContext) - : mGLContext(aContext) -{ - mGLContext->MakeCurrent(); - ShaderConfigOGL config; - config.SetTextureTarget(LOCAL_GL_TEXTURE_RECTANGLE_ARB); - mRGBARectProgram = MakeUnique<ShaderProgramOGL>(mGLContext, - ProgramProfileOGL::GetProfileFor(config)); - - // Create mQuadVBO. - mGLContext->fGenBuffers(1, &mQuadVBO); - mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mQuadVBO); - - // 1 quad, with the number of the quad (vertexID) encoded in w. - GLfloat vertices[] = { - 0.0f, 0.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 0.0f, 0.0f, - }; - HeapCopyOfStackArray<GLfloat> verticesOnHeap(vertices); - mGLContext->fBufferData(LOCAL_GL_ARRAY_BUFFER, - verticesOnHeap.ByteLength(), - verticesOnHeap.Data(), - LOCAL_GL_STATIC_DRAW); - mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0); -} - -GLPresenter::~GLPresenter() -{ - if (mQuadVBO) { - mGLContext->MakeCurrent(); - mGLContext->fDeleteBuffers(1, &mQuadVBO); - mQuadVBO = 0; - } -} - -void -GLPresenter::BindAndDrawQuad(ShaderProgramOGL *aProgram, - const gfx::Rect& aLayerRect, - const gfx::Rect& aTextureRect) -{ - mGLContext->MakeCurrent(); - - gfx::Rect layerRects[4]; - gfx::Rect textureRects[4]; - - layerRects[0] = aLayerRect; - textureRects[0] = aTextureRect; - - aProgram->SetLayerRects(layerRects); - aProgram->SetTextureRects(textureRects); - - const GLuint coordAttribIndex = 0; - - mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mQuadVBO); - mGLContext->fVertexAttribPointer(coordAttribIndex, 4, - LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, - (GLvoid*)0); - mGLContext->fEnableVertexAttribArray(coordAttribIndex); - mGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, 6); - mGLContext->fDisableVertexAttribArray(coordAttribIndex); -} - -void -GLPresenter::BeginFrame(LayoutDeviceIntSize aRenderSize) -{ - mGLContext->MakeCurrent(); - - mGLContext->fViewport(0, 0, aRenderSize.width, aRenderSize.height); - - // Matrix to transform (0, 0, width, height) to viewport space (-1.0, 1.0, - // 2, 2) and flip the contents. - gfx::Matrix viewMatrix = gfx::Matrix::Translation(-1.0, 1.0); - viewMatrix.PreScale(2.0f / float(aRenderSize.width), - 2.0f / float(aRenderSize.height)); - viewMatrix.PreScale(1.0f, -1.0f); - - gfx::Matrix4x4 matrix3d = gfx::Matrix4x4::From2D(viewMatrix); - matrix3d._33 = 0.0f; - - // set the projection matrix for the next time the program is activated - mProjMatrix = matrix3d; - - // Default blend function implements "OVER" - mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA, - LOCAL_GL_ONE, LOCAL_GL_ONE); - mGLContext->fEnable(LOCAL_GL_BLEND); - - mGLContext->fClearColor(0.0, 0.0, 0.0, 0.0); - mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT); - - mGLContext->fEnable(LOCAL_GL_TEXTURE_RECTANGLE_ARB); -} - -void -GLPresenter::EndFrame() -{ - mGLContext->SwapBuffers(); - mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0); -} - -class WidgetsReleaserRunnable final : public mozilla::Runnable -{ -public: - explicit WidgetsReleaserRunnable(nsTArray<nsCOMPtr<nsIWidget>>&& aWidgetArray) - : mWidgetArray(aWidgetArray) - { - } - - // Do nothing; all this runnable does is hold a reference the widgets in - // mWidgetArray, and those references will be dropped when this runnable - // is destroyed. - -private: - nsTArray<nsCOMPtr<nsIWidget>> mWidgetArray; -}; - -#pragma mark - - -// ViewRegionContainerView is a view class for certain subviews of ChildView -// which contain the NSViews created for ViewRegions (see ViewRegion.h). -// It doesn't do anything interesting, it only acts as a container so that it's -// easier for ChildView to control the z order of its children. -@interface ViewRegionContainerView : NSView { -} -@end - -@implementation ViewRegionContainerView - -- (NSView*)hitTest:(NSPoint)aPoint { - return nil; // Be transparent to mouse events. -} - -- (BOOL)isFlipped { - return [[self superview] isFlipped]; -} - -- (BOOL)mouseDownCanMoveWindow { - return [[self superview] mouseDownCanMoveWindow]; -} - -@end -; - -@implementation ChildView - -// globalDragPboard is non-null during native drag sessions that did not originate -// in our native NSView (it is set in |draggingEntered:|). It is unset when the -// drag session ends for this view, either with the mouse exiting or when a drop -// occurs in this view. -NSPasteboard* globalDragPboard = nil; - -// gLastDragView and gLastDragMouseDownEvent are used to communicate information -// to the drag service during drag invocation (starting a drag in from the view). -// gLastDragView is only non-null while mouseDragged is on the call stack. -NSView* gLastDragView = nil; -NSEvent* gLastDragMouseDownEvent = nil; - -+ (void)initialize -{ - static BOOL initialized = NO; - - if (!initialized) { - // Inform the OS about the types of services (from the "Services" menu) - // that we can handle. - - NSArray *sendTypes = [[NSArray alloc] initWithObjects:NSStringPboardType,NSHTMLPboardType,nil]; - NSArray *returnTypes = [[NSArray alloc] initWithObjects:NSStringPboardType,NSHTMLPboardType,nil]; - - [NSApp registerServicesMenuSendTypes:sendTypes returnTypes:returnTypes]; - - [sendTypes release]; - [returnTypes release]; - - initialized = YES; - } -} - -+ (void)registerViewForDraggedTypes:(NSView*)aView -{ - [aView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, - NSStringPboardType, - NSHTMLPboardType, - NSURLPboardType, - NSFilesPromisePboardType, - kWildcardPboardType, - kCorePboardType_url, - kCorePboardType_urld, - kCorePboardType_urln, - nil]]; -} - -// initWithFrame:geckoChild: -- (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - if ((self = [super initWithFrame:inFrame])) { - mGeckoChild = inChild; - mBlockedLastMouseDown = NO; - mExpectingWheelStop = NO; - - mLastMouseDownEvent = nil; - mLastKeyDownEvent = nil; - mClickThroughMouseDownEvent = nil; - mDragService = nullptr; - - mGestureState = eGestureState_None; - mCumulativeMagnification = 0.0; - mCumulativeRotation = 0.0; - - mNeedsGLUpdate = NO; - - [self setFocusRingType:NSFocusRingTypeNone]; - -#ifdef __LP64__ - mCancelSwipeAnimation = nil; -#endif - - mNonDraggableViewsContainer = [[ViewRegionContainerView alloc] initWithFrame:[self bounds]]; - mVibrancyViewsContainer = [[ViewRegionContainerView alloc] initWithFrame:[self bounds]]; - - [mNonDraggableViewsContainer setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [mVibrancyViewsContainer setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - - [self addSubview:mNonDraggableViewsContainer]; - [self addSubview:mVibrancyViewsContainer]; - - mPixelHostingView = [[PixelHostingView alloc] initWithFrame:[self bounds]]; - [mPixelHostingView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - - [self addSubview:mPixelHostingView]; - - mTopLeftCornerMask = NULL; - } - - // register for things we'll take from other applications - [ChildView registerViewForDraggedTypes:self]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(systemMetricsChanged) - name:NSControlTintDidChangeNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(systemMetricsChanged) - name:NSSystemColorsDidChangeNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(scrollbarSystemMetricChanged) - name:NSPreferredScrollerStyleDidChangeNotification - object:nil]; - [[NSDistributedNotificationCenter defaultCenter] addObserver:self - selector:@selector(systemMetricsChanged) - name:@"AppleAquaScrollBarVariantChanged" - object:nil - suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(_surfaceNeedsUpdate:) - name:NSViewGlobalFrameDidChangeNotification - object:mPixelHostingView]; - - return self; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -// ComplexTextInputPanel's interpretKeyEvent hack won't work without this. -// It makes calls to +[NSTextInputContext currentContext], deep in system -// code, return the appropriate context. -- (NSTextInputContext *)inputContext -{ - NSTextInputContext* pluginContext = NULL; - if (mGeckoChild && mGeckoChild->IsPluginFocused()) { - ComplexTextInputPanel* ctiPanel = - ComplexTextInputPanel::GetSharedComplexTextInputPanel(); - if (ctiPanel) { - pluginContext = (NSTextInputContext*) ctiPanel->GetInputContext(); - } - } - if (pluginContext) { - return pluginContext; - } else { - if (!mGeckoChild) { - // -[ChildView widgetDestroyed] has been called, but - // -[ChildView delayedTearDown] has not yet completed. Accessing - // [super inputContext] now would uselessly recreate a text input context - // for us, under which -[ChildView validAttributesForMarkedText] would - // be called and the assertion checking for mTextInputHandler would fail. - // We return nil to avoid that. - return nil; - } - return [super inputContext]; - } -} - -- (void)installTextInputHandler:(TextInputHandler*)aHandler -{ - mTextInputHandler = aHandler; -} - -- (void)uninstallTextInputHandler -{ - mTextInputHandler = nullptr; -} - -- (bool)preRender:(NSOpenGLContext *)aGLContext -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - if (![self window] || - ([[self window] isKindOfClass:[BaseWindow class]] && - ![(BaseWindow*)[self window] isVisibleOrBeingShown])) { - // Before the window is shown, our GL context's front FBO is not - // framebuffer complete, so we refuse to render. - return false; - } - - if (!mGLContext) { - mGLContext = aGLContext; - [mGLContext retain]; - mNeedsGLUpdate = true; - } - - CGLLockContext((CGLContextObj)[aGLContext CGLContextObj]); - - if (mNeedsGLUpdate) { - [self updateGLContext]; - mNeedsGLUpdate = NO; - } - - return true; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); -} - -- (void)postRender:(NSOpenGLContext *)aGLContext -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - CGLUnlockContext((CGLContextObj)[aGLContext CGLContextObj]); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (NSView*)vibrancyViewsContainer { - return mVibrancyViewsContainer; -} - -- (NSView*)nonDraggableViewsContainer { - return mNonDraggableViewsContainer; -} - -- (NSView*)pixelHostingView { - return mPixelHostingView; -} - -- (void)dealloc -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [mGLContext release]; - [mLastMouseDownEvent release]; - [mLastKeyDownEvent release]; - [mClickThroughMouseDownEvent release]; - CGImageRelease(mTopLeftCornerMask); - ChildViewMouseTracker::OnDestroyView(self); - - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [[NSDistributedNotificationCenter defaultCenter] removeObserver:self]; - [mVibrancyViewsContainer removeFromSuperview]; - [mVibrancyViewsContainer release]; - [mNonDraggableViewsContainer removeFromSuperview]; - [mNonDraggableViewsContainer release]; - [mPixelHostingView removeFromSuperview]; - [mPixelHostingView release]; - - [super dealloc]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)widgetDestroyed -{ - if (mTextInputHandler) { - mTextInputHandler->OnDestroyWidget(mGeckoChild); - mTextInputHandler = nullptr; - } - mGeckoChild = nullptr; - - // Just in case we're destroyed abruptly and missed the draggingExited - // or performDragOperation message. - NS_IF_RELEASE(mDragService); -} - -// mozView method, return our gecko child view widget. Note this does not AddRef. -- (nsIWidget*) widget -{ - return static_cast<nsIWidget*>(mGeckoChild); -} - -- (void)systemMetricsChanged -{ - if (mGeckoChild) - mGeckoChild->NotifyThemeChanged(); -} - -- (void)scrollbarSystemMetricChanged -{ - [self systemMetricsChanged]; - - if (mGeckoChild) { - nsIWidgetListener* listener = mGeckoChild->GetWidgetListener(); - if (listener) { - nsIPresShell* presShell = listener->GetPresShell(); - if (presShell) { - presShell->ReconstructFrames(); - } - } - } -} - -- (NSString*)description -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - return [NSString stringWithFormat:@"ChildView %p, gecko child %p, frame %@", self, mGeckoChild, NSStringFromRect([self frame])]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -// Make the origin of this view the topLeft corner (gecko origin) rather -// than the bottomLeft corner (standard cocoa origin). -- (BOOL)isFlipped -{ - return YES; -} - -- (BOOL)isOpaque -{ - return [[self window] isOpaque]; -} - -- (void)sendFocusEvent:(EventMessage)eventMessage -{ - if (!mGeckoChild) - return; - - nsEventStatus status = nsEventStatus_eIgnore; - WidgetGUIEvent focusGuiEvent(true, eventMessage, mGeckoChild); - focusGuiEvent.mTime = PR_IntervalNow(); - mGeckoChild->DispatchEvent(&focusGuiEvent, status); -} - -// We accept key and mouse events, so don't keep passing them up the chain. Allow -// this to be a 'focused' widget for event dispatch. -- (BOOL)acceptsFirstResponder -{ - return YES; -} - -// Accept mouse down events on background windows -- (BOOL)acceptsFirstMouse:(NSEvent*)aEvent -{ - if (![[self window] isKindOfClass:[PopupWindow class]]) { - // We rely on this function to tell us that the mousedown was on a - // background window. Inside mouseDown we can't tell whether we were - // inactive because at that point we've already been made active. - // Unfortunately, acceptsFirstMouse is called for PopupWindows even when - // their parent window is active, so ignore this on them for now. - mClickThroughMouseDownEvent = [aEvent retain]; - } - return YES; -} - -- (BOOL)mouseDownCanMoveWindow -{ - // Return YES so that parts of this view can be draggable. The non-draggable - // parts will be covered by NSViews that return NO from - // mouseDownCanMoveWindow and thus override draggability from the inside. - // These views are assembled in nsChildView::UpdateWindowDraggingRegion. - return YES; -} - --(void)updateGLContext -{ - [mGLContext setView:mPixelHostingView]; - [mGLContext update]; -} - -- (void)_surfaceNeedsUpdate:(NSNotification*)notification -{ - if (mGLContext) { - CGLLockContext((CGLContextObj)[mGLContext CGLContextObj]); - mNeedsGLUpdate = YES; - CGLUnlockContext((CGLContextObj)[mGLContext CGLContextObj]); - } -} - -- (void)viewDidChangeBackingProperties -{ - [super viewDidChangeBackingProperties]; - if (mGeckoChild) { - // actually, it could be the color space that's changed, - // but we can't tell the difference here except by retrieving - // the backing scale factor and comparing to the old value - mGeckoChild->BackingScaleFactorChanged(); - } -} - -- (BOOL)isCoveringTitlebar -{ - return [[self window] isKindOfClass:[BaseWindow class]] && - [(BaseWindow*)[self window] mainChildView] == self && - [(BaseWindow*)[self window] drawsContentsIntoWindowFrame]; -} - -- (void)viewWillStartLiveResize -{ - nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); - - if (!observerService) { - return; - } - - observerService->NotifyObservers(nullptr, "live-resize-start", nullptr); -} - -- (void)viewDidEndLiveResize -{ - nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); - - if (!observerService) { - return; - } - - observerService->NotifyObservers(nullptr, "live-resize-end", nullptr); -} - -- (NSColor*)vibrancyFillColorForThemeGeometryType:(nsITheme::ThemeGeometryType)aThemeGeometryType -{ - if (!mGeckoChild) { - return [NSColor whiteColor]; - } - return mGeckoChild->VibrancyFillColorForThemeGeometryType(aThemeGeometryType); -} - -- (NSColor*)vibrancyFontSmoothingBackgroundColorForThemeGeometryType:(nsITheme::ThemeGeometryType)aThemeGeometryType -{ - if (!mGeckoChild) { - return [NSColor clearColor]; - } - return mGeckoChild->VibrancyFontSmoothingBackgroundColorForThemeGeometryType(aThemeGeometryType); -} - -- (LayoutDeviceIntRegion)nativeDirtyRegionWithBoundingRect:(NSRect)aRect -{ - LayoutDeviceIntRect boundingRect = mGeckoChild->CocoaPointsToDevPixels(aRect); - const NSRect *rects; - NSInteger count; - [mPixelHostingView getRectsBeingDrawn:&rects count:&count]; - - if (count > MAX_RECTS_IN_REGION) { - return boundingRect; - } - - LayoutDeviceIntRegion region; - for (NSInteger i = 0; i < count; ++i) { - region.Or(region, mGeckoChild->CocoaPointsToDevPixels(rects[i])); - } - region.And(region, boundingRect); - return region; -} - -// The display system has told us that a portion of our view is dirty. Tell -// gecko to paint it -// This method is called from mPixelHostingView's drawRect handler. -- (void)doDrawRect:(NSRect)aRect -{ - if (!NS_IsMainThread()) { - // In the presence of CoreAnimation, this method can sometimes be called on - // a non-main thread. Ignore those calls because Gecko can only react to - // them on the main thread. - return; - } - - if (!mGeckoChild || !mGeckoChild->IsVisible()) - return; - CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; - - if ([self isUsingOpenGL]) { - // Since this view is usually declared as opaque, the window's pixel - // buffer may now contain garbage which we need to prevent from reaching - // the screen. The only place where garbage can show is in the window - // corners and the vibrant regions of the window - the rest of the window - // is covered by opaque content in our OpenGL surface. - // So we need to clear the pixel buffer contents in these areas. - [self clearCorners]; - - // Force a sync OMTC composite into the OpenGL context and return. - LayoutDeviceIntRect geckoBounds = mGeckoChild->GetBounds(); - LayoutDeviceIntRegion region(geckoBounds); - - mGeckoChild->PaintWindow(region); - return; - } - - PROFILER_LABEL("ChildView", "drawRect", - js::ProfileEntry::Category::GRAPHICS); - - // The CGContext that drawRect supplies us with comes with a transform that - // scales one user space unit to one Cocoa point, which can consist of - // multiple dev pixels. But Gecko expects its supplied context to be scaled - // to device pixels, so we need to reverse the scaling. - double scale = mGeckoChild->BackingScaleFactor(); - CGContextSaveGState(cgContext); - CGContextScaleCTM(cgContext, 1.0 / scale, 1.0 / scale); - - NSSize viewSize = [self bounds].size; - gfx::IntSize backingSize = gfx::IntSize::Truncate(viewSize.width * scale, viewSize.height * scale); - LayoutDeviceIntRegion region = [self nativeDirtyRegionWithBoundingRect:aRect]; - - bool painted = mGeckoChild->PaintWindowInContext(cgContext, region, backingSize); - - // Undo the scale transform so that from now on the context is in - // CocoaPoints again. - CGContextRestoreGState(cgContext); - - if (!painted && [mPixelHostingView isOpaque]) { - // Gecko refused to draw, but we've claimed to be opaque, so we have to - // draw something--fill with white. - CGContextSetRGBFillColor(cgContext, 1, 1, 1, 1); - CGContextFillRect(cgContext, NSRectToCGRect(aRect)); - } - - if ([self isCoveringTitlebar]) { - [self drawTitleString]; - [self drawTitlebarHighlight]; - [self maskTopCornersInContext:cgContext]; - } -} - -- (BOOL)isUsingOpenGL -{ - if (!mGeckoChild || ![self window]) - return NO; - - return mGLContext || mUsingOMTCompositor; -} - - -- (BOOL)hasRoundedBottomCorners -{ - return [[self window] respondsToSelector:@selector(bottomCornerRounded)] && - [[self window] bottomCornerRounded]; -} - -- (CGFloat)cornerRadius -{ - NSView* frameView = [[[self window] contentView] superview]; - if (!frameView || ![frameView respondsToSelector:@selector(roundedCornerRadius)]) - return 4.0f; - return [frameView roundedCornerRadius]; -} - --(void)setGLOpaque:(BOOL)aOpaque -{ - CGLLockContext((CGLContextObj)[mGLContext CGLContextObj]); - // Make the context opaque for fullscreen (since it performs better), and transparent - // for windowed (since we need it for rounded corners). - GLint opaque = aOpaque ? 1 : 0; - [mGLContext setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; - CGLUnlockContext((CGLContextObj)[mGLContext CGLContextObj]); -} - -// Accelerated windows have two NSSurfaces: -// (1) The window's pixel buffer in the back and -// (2) the OpenGL view in the front. -// These two surfaces are composited by the window manager. Drawing into the -// CGContext which is provided by drawRect ends up in (1). -// When our window has rounded corners, the OpenGL view has transparent pixels -// in the corners. In these places the contents of the window's pixel buffer -// can show through. So we need to make sure that the pixel buffer is -// transparent in the corners so that no garbage reaches the screen. -// The contents of the pixel buffer in the rest of the window don't matter -// because they're covered by opaque pixels of the OpenGL context. -// Making the corners transparent works even though our window is -// declared "opaque" (in the NSWindow's isOpaque method). -- (void)clearCorners -{ - CGFloat radius = [self cornerRadius]; - CGFloat w = [self bounds].size.width, h = [self bounds].size.height; - [[NSColor clearColor] set]; - - if ([self isCoveringTitlebar]) { - NSRectFill(NSMakeRect(0, 0, radius, radius)); - NSRectFill(NSMakeRect(w - radius, 0, radius, radius)); - } - - if ([self hasRoundedBottomCorners]) { - NSRectFill(NSMakeRect(0, h - radius, radius, radius)); - NSRectFill(NSMakeRect(w - radius, h - radius, radius, radius)); - } -} - -// This is the analog of nsChildView::MaybeDrawRoundedCorners for CGContexts. -// We only need to mask the top corners here because Cocoa does the masking -// for the window's bottom corners automatically (starting with 10.7). -- (void)maskTopCornersInContext:(CGContextRef)aContext -{ - CGFloat radius = [self cornerRadius]; - int32_t devPixelCornerRadius = mGeckoChild->CocoaPointsToDevPixels(radius); - - // First make sure that mTopLeftCornerMask is set up. - if (!mTopLeftCornerMask || - int32_t(CGImageGetWidth(mTopLeftCornerMask)) != devPixelCornerRadius) { - CGImageRelease(mTopLeftCornerMask); - CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); - CGContextRef imgCtx = CGBitmapContextCreate(NULL, - devPixelCornerRadius, - devPixelCornerRadius, - 8, devPixelCornerRadius * 4, - rgb, kCGImageAlphaPremultipliedFirst); - CGColorSpaceRelease(rgb); - DrawTopLeftCornerMask(imgCtx, devPixelCornerRadius); - mTopLeftCornerMask = CGBitmapContextCreateImage(imgCtx); - CGContextRelease(imgCtx); - } - - // kCGBlendModeDestinationIn is the secret sauce which allows us to erase - // already painted pixels. It's defined as R = D * Sa: multiply all channels - // of the destination pixel with the alpha of the source pixel. In our case, - // the source is mTopLeftCornerMask. - CGContextSaveGState(aContext); - CGContextSetBlendMode(aContext, kCGBlendModeDestinationIn); - - CGRect destRect = CGRectMake(0, 0, radius, radius); - - // Erase the top left corner... - CGContextDrawImage(aContext, destRect, mTopLeftCornerMask); - - // ... and the top right corner. - CGContextTranslateCTM(aContext, [self bounds].size.width, 0); - CGContextScaleCTM(aContext, -1, 1); - CGContextDrawImage(aContext, destRect, mTopLeftCornerMask); - - CGContextRestoreGState(aContext); -} - -- (void)drawTitleString -{ - BaseWindow* window = (BaseWindow*)[self window]; - if (![window wantsTitleDrawn]) { - return; - } - - NSView* frameView = [[window contentView] superview]; - if (![frameView respondsToSelector:@selector(_drawTitleBar:)]) { - return; - } - - NSGraphicsContext* oldContext = [NSGraphicsContext currentContext]; - CGContextRef ctx = (CGContextRef)[oldContext graphicsPort]; - CGContextSaveGState(ctx); - if ([oldContext isFlipped] != [frameView isFlipped]) { - CGContextTranslateCTM(ctx, 0, [self bounds].size.height); - CGContextScaleCTM(ctx, 1, -1); - } - [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:[frameView isFlipped]]]; - [frameView _drawTitleBar:[frameView bounds]]; - CGContextRestoreGState(ctx); - [NSGraphicsContext setCurrentContext:oldContext]; -} - -- (void)drawTitlebarHighlight -{ - DrawTitlebarHighlight([self bounds].size, [self cornerRadius], - mGeckoChild->DevPixelsToCocoaPoints(1)); -} - -- (void)viewWillDraw -{ - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - if (mGeckoChild) { - // The OS normally *will* draw our NSWindow, no matter what we do here. - // But Gecko can delete our parent widget(s) (along with mGeckoChild) - // while processing a paint request, which closes our NSWindow and - // makes the OS throw an NSInternalInconsistencyException assertion when - // it tries to draw it. Sometimes the OS also aborts the browser process. - // So we need to retain our parent(s) here and not release it/them until - // the next time through the main thread's run loop. When we do this we - // also need to retain and release mGeckoChild, which holds a strong - // reference to us. See bug 550392. - nsIWidget* parent = mGeckoChild->GetParent(); - if (parent) { - nsTArray<nsCOMPtr<nsIWidget>> widgetArray; - while (parent) { - widgetArray.AppendElement(parent); - parent = parent->GetParent(); - } - widgetArray.AppendElement(mGeckoChild); - nsCOMPtr<nsIRunnable> releaserRunnable = - new WidgetsReleaserRunnable(Move(widgetArray)); - NS_DispatchToMainThread(releaserRunnable); - } - - if ([self isUsingOpenGL]) { - if (ShadowLayerForwarder* slf = mGeckoChild->GetLayerManager()->AsShadowForwarder()) { - slf->WindowOverlayChanged(); - } - } - - mGeckoChild->WillPaintWindow(); - } - [super viewWillDraw]; -} - -#if USE_CLICK_HOLD_CONTEXTMENU -// -// -clickHoldCallback: -// -// called from a timer two seconds after a mouse down to see if we should display -// a context menu (click-hold). |anEvent| is the original mouseDown event. If we're -// still in that mouseDown by this time, put up the context menu, otherwise just -// fuhgeddaboutit. |anEvent| has been retained by the OS until after this callback -// fires so we're ok there. -// -// This code currently messes in a bunch of edge cases (bugs 234751, 232964, 232314) -// so removing it until we get it straightened out. -// -- (void)clickHoldCallback:(id)theEvent; -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if( theEvent == [NSApp currentEvent] ) { - // we're still in the middle of the same mousedown event here, activate - // click-hold context menu by triggering the right mouseDown action. - NSEvent* clickHoldEvent = [NSEvent mouseEventWithType:NSRightMouseDown - location:[theEvent locationInWindow] - modifierFlags:[theEvent modifierFlags] - timestamp:[theEvent timestamp] - windowNumber:[theEvent windowNumber] - context:[theEvent context] - eventNumber:[theEvent eventNumber] - clickCount:[theEvent clickCount] - pressure:[theEvent pressure]]; - [self rightMouseDown:clickHoldEvent]; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} -#endif - -// If we've just created a non-native context menu, we need to mark it as -// such and let the OS (and other programs) know when it opens and closes -// (this is how the OS knows to close other programs' context menus when -// ours open). We send the initial notification here, but others are sent -// in nsCocoaWindow::Show(). -- (void)maybeInitContextMenuTracking -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - -#ifdef MOZ_USE_NATIVE_POPUP_WINDOWS - return; -#endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */ - - nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); - NS_ENSURE_TRUE_VOID(rollupListener); - nsCOMPtr<nsIWidget> widget = rollupListener->GetRollupWidget(); - NS_ENSURE_TRUE_VOID(widget); - - NSWindow *popupWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW); - if (!popupWindow || ![popupWindow isKindOfClass:[PopupWindow class]]) - return; - - [[NSDistributedNotificationCenter defaultCenter] - postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification" - object:@"org.mozilla.gecko.PopupWindow"]; - [(PopupWindow*)popupWindow setIsContextMenu:YES]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// Returns true if the event should no longer be processed, false otherwise. -// This does not return whether or not anything was rolled up. -- (BOOL)maybeRollup:(NSEvent*)theEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - BOOL consumeEvent = NO; - - nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); - NS_ENSURE_TRUE(rollupListener, false); - nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget(); - if (rollupWidget) { - NSWindow* currentPopup = static_cast<NSWindow*>(rollupWidget->GetNativeData(NS_NATIVE_WINDOW)); - if (!nsCocoaUtils::IsEventOverWindow(theEvent, currentPopup)) { - // event is not over the rollup window, default is to roll up - bool shouldRollup = true; - - // check to see if scroll events should roll up the popup - if ([theEvent type] == NSScrollWheel) { - shouldRollup = rollupListener->ShouldRollupOnMouseWheelEvent(); - // consume scroll events that aren't over the popup - // unless the popup is an arrow panel - consumeEvent = rollupListener->ShouldConsumeOnMouseWheelEvent(); - } - - // if we're dealing with menus, we probably have submenus and - // we don't want to rollup if the click is in a parent menu of - // the current submenu - uint32_t popupsToRollup = UINT32_MAX; - AutoTArray<nsIWidget*, 5> widgetChain; - uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain); - for (uint32_t i = 0; i < widgetChain.Length(); i++) { - nsIWidget* widget = widgetChain[i]; - NSWindow* currWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW); - if (nsCocoaUtils::IsEventOverWindow(theEvent, currWindow)) { - // don't roll up if the mouse event occurred within a menu of the - // same type. If the mouse event occurred in a menu higher than - // that, roll up, but pass the number of popups to Rollup so - // that only those of the same type close up. - if (i < sameTypeCount) { - shouldRollup = false; - } - else { - popupsToRollup = sameTypeCount; - } - break; - } - } - - if (shouldRollup) { - if ([theEvent type] == NSLeftMouseDown) { - NSPoint point = [NSEvent mouseLocation]; - FlipCocoaScreenCoordinate(point); - gfx::IntPoint pos = gfx::IntPoint::Truncate(point.x, point.y); - consumeEvent = (BOOL)rollupListener->Rollup(popupsToRollup, true, &pos, nullptr); - } - else { - consumeEvent = (BOOL)rollupListener->Rollup(popupsToRollup, true, nullptr, nullptr); - } - } - } - } - - return consumeEvent; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); -} - -/* - * In OS X Mountain Lion and above, smart zoom gestures are implemented in - * smartMagnifyWithEvent. In OS X Lion, they are implemented in - * magnifyWithEvent. See inline comments for more info. - * - * The prototypes swipeWithEvent, beginGestureWithEvent, magnifyWithEvent, - * smartMagnifyWithEvent, rotateWithEvent, and endGestureWithEvent were - * obtained from the following links: - * https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSResponder_Class/Reference/Reference.html - * https://developer.apple.com/library/mac/#releasenotes/Cocoa/AppKit.html - */ - -- (void)swipeWithEvent:(NSEvent *)anEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!anEvent || !mGeckoChild) - return; - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - float deltaX = [anEvent deltaX]; // left=1.0, right=-1.0 - float deltaY = [anEvent deltaY]; // up=1.0, down=-1.0 - - // Setup the "swipe" event. - WidgetSimpleGestureEvent geckoEvent(true, eSwipeGesture, mGeckoChild); - [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent]; - - // Record the left/right direction. - if (deltaX > 0.0) - geckoEvent.mDirection |= nsIDOMSimpleGestureEvent::DIRECTION_LEFT; - else if (deltaX < 0.0) - geckoEvent.mDirection |= nsIDOMSimpleGestureEvent::DIRECTION_RIGHT; - - // Record the up/down direction. - if (deltaY > 0.0) - geckoEvent.mDirection |= nsIDOMSimpleGestureEvent::DIRECTION_UP; - else if (deltaY < 0.0) - geckoEvent.mDirection |= nsIDOMSimpleGestureEvent::DIRECTION_DOWN; - - // Send the event. - mGeckoChild->DispatchWindowEvent(geckoEvent); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)beginGestureWithEvent:(NSEvent *)anEvent -{ - if (!anEvent) - return; - - mGestureState = eGestureState_StartGesture; - mCumulativeMagnification = 0; - mCumulativeRotation = 0.0; -} - -- (void)magnifyWithEvent:(NSEvent *)anEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!anEvent || !mGeckoChild) - return; - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - float deltaZ = [anEvent deltaZ]; - - EventMessage msg; - switch (mGestureState) { - case eGestureState_StartGesture: - msg = eMagnifyGestureStart; - mGestureState = eGestureState_MagnifyGesture; - break; - - case eGestureState_MagnifyGesture: - msg = eMagnifyGestureUpdate; - break; - - case eGestureState_None: - case eGestureState_RotateGesture: - default: - return; - } - - // Setup the event. - WidgetSimpleGestureEvent geckoEvent(true, msg, mGeckoChild); - geckoEvent.mDelta = deltaZ; - [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent]; - - // Send the event. - mGeckoChild->DispatchWindowEvent(geckoEvent); - - // Keep track of the cumulative magnification for the final "magnify" event. - mCumulativeMagnification += deltaZ; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)smartMagnifyWithEvent:(NSEvent *)anEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!anEvent || !mGeckoChild) { - return; - } - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - // Setup the "double tap" event. - WidgetSimpleGestureEvent geckoEvent(true, eTapGesture, mGeckoChild); - [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent]; - geckoEvent.mClickCount = 1; - - // Send the event. - mGeckoChild->DispatchWindowEvent(geckoEvent); - - // Clear the gesture state - mGestureState = eGestureState_None; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)rotateWithEvent:(NSEvent *)anEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!anEvent || !mGeckoChild) - return; - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - float rotation = [anEvent rotation]; - - EventMessage msg; - switch (mGestureState) { - case eGestureState_StartGesture: - msg = eRotateGestureStart; - mGestureState = eGestureState_RotateGesture; - break; - - case eGestureState_RotateGesture: - msg = eRotateGestureUpdate; - break; - - case eGestureState_None: - case eGestureState_MagnifyGesture: - default: - return; - } - - // Setup the event. - WidgetSimpleGestureEvent geckoEvent(true, msg, mGeckoChild); - [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent]; - geckoEvent.mDelta = -rotation; - if (rotation > 0.0) { - geckoEvent.mDirection = nsIDOMSimpleGestureEvent::ROTATION_COUNTERCLOCKWISE; - } else { - geckoEvent.mDirection = nsIDOMSimpleGestureEvent::ROTATION_CLOCKWISE; - } - - // Send the event. - mGeckoChild->DispatchWindowEvent(geckoEvent); - - // Keep track of the cumulative rotation for the final "rotate" event. - mCumulativeRotation += rotation; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)endGestureWithEvent:(NSEvent *)anEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!anEvent || !mGeckoChild) { - // Clear the gestures state if we cannot send an event. - mGestureState = eGestureState_None; - mCumulativeMagnification = 0.0; - mCumulativeRotation = 0.0; - return; - } - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - switch (mGestureState) { - case eGestureState_MagnifyGesture: - { - // Setup the "magnify" event. - WidgetSimpleGestureEvent geckoEvent(true, eMagnifyGesture, mGeckoChild); - geckoEvent.mDelta = mCumulativeMagnification; - [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent]; - - // Send the event. - mGeckoChild->DispatchWindowEvent(geckoEvent); - } - break; - - case eGestureState_RotateGesture: - { - // Setup the "rotate" event. - WidgetSimpleGestureEvent geckoEvent(true, eRotateGesture, mGeckoChild); - [self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent]; - geckoEvent.mDelta = -mCumulativeRotation; - if (mCumulativeRotation > 0.0) { - geckoEvent.mDirection = nsIDOMSimpleGestureEvent::ROTATION_COUNTERCLOCKWISE; - } else { - geckoEvent.mDirection = nsIDOMSimpleGestureEvent::ROTATION_CLOCKWISE; - } - - // Send the event. - mGeckoChild->DispatchWindowEvent(geckoEvent); - } - break; - - case eGestureState_None: - case eGestureState_StartGesture: - default: - break; - } - - // Clear the gestures state. - mGestureState = eGestureState_None; - mCumulativeMagnification = 0.0; - mCumulativeRotation = 0.0; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (bool)shouldConsiderStartingSwipeFromEvent:(NSEvent*)anEvent -{ - // This method checks whether the AppleEnableSwipeNavigateWithScrolls global - // preference is set. If it isn't, fluid swipe tracking is disabled, and a - // horizontal two-finger gesture is always a scroll (even in Safari). This - // preference can't (currently) be set from the Preferences UI -- only using - // 'defaults write'. - if (![NSEvent isSwipeTrackingFromScrollEventsEnabled]) { - return false; - } - - // Only initiate horizontal tracking for gestures that have just begun -- - // otherwise a scroll to one side of the page can have a swipe tacked on - // to it. - NSEventPhase eventPhase = nsCocoaUtils::EventPhase(anEvent); - if ([anEvent type] != NSScrollWheel || - eventPhase != NSEventPhaseBegan || - ![anEvent hasPreciseScrollingDeltas]) { - return false; - } - - // Only initiate horizontal tracking for events whose horizontal element is - // at least eight times larger than its vertical element. This minimizes - // performance problems with vertical scrolls (by minimizing the possibility - // that they'll be misinterpreted as horizontal swipes), while still - // tolerating a small vertical element to a true horizontal swipe. The number - // '8' was arrived at by trial and error. - CGFloat deltaX = [anEvent scrollingDeltaX]; - CGFloat deltaY = [anEvent scrollingDeltaY]; - return std::abs(deltaX) > std::abs(deltaY) * 8; -} - -- (void)setUsingOMTCompositor:(BOOL)aUseOMTC -{ - mUsingOMTCompositor = aUseOMTC; -} - -// Returning NO from this method only disallows ordering on mousedown - in order -// to prevent it for mouseup too, we need to call [NSApp preventWindowOrdering] -// when handling the mousedown event. -- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent*)aEvent -{ - // Always using system-provided window ordering for normal windows. - if (![[self window] isKindOfClass:[PopupWindow class]]) - return NO; - - // Don't reorder when we don't have a parent window, like when we're a - // context menu or a tooltip. - return ![[self window] parentWindow]; -} - -- (void)mouseDown:(NSEvent*)theEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if ([self shouldDelayWindowOrderingForEvent:theEvent]) { - [NSApp preventWindowOrdering]; - } - - // If we've already seen this event due to direct dispatch from menuForEvent: - // just bail; if not, remember it. - if (mLastMouseDownEvent == theEvent) { - [mLastMouseDownEvent release]; - mLastMouseDownEvent = nil; - return; - } - else { - [mLastMouseDownEvent release]; - mLastMouseDownEvent = [theEvent retain]; - } - - [gLastDragMouseDownEvent release]; - gLastDragMouseDownEvent = [theEvent retain]; - - // We need isClickThrough because at this point the window we're in might - // already have become main, so the check for isMainWindow in - // WindowAcceptsEvent isn't enough. It also has to check isClickThrough. - BOOL isClickThrough = (theEvent == mClickThroughMouseDownEvent); - [mClickThroughMouseDownEvent release]; - mClickThroughMouseDownEvent = nil; - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - if ([self maybeRollup:theEvent] || - !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent, self, isClickThrough)) { - // Remember blocking because that means we want to block mouseup as well. - mBlockedLastMouseDown = YES; - return; - } - -#if USE_CLICK_HOLD_CONTEXTMENU - // fire off timer to check for click-hold after two seconds. retains |theEvent| - [self performSelector:@selector(clickHoldCallback:) withObject:theEvent afterDelay:2.0]; -#endif - - // in order to send gecko events we'll need a gecko widget - if (!mGeckoChild) - return; - if (mTextInputHandler->OnHandleEvent(theEvent)) { - return; - } - - NSUInteger modifierFlags = [theEvent modifierFlags]; - - WidgetMouseEvent geckoEvent(true, eMouseDown, mGeckoChild, - WidgetMouseEvent::eReal); - [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent]; - - NSInteger clickCount = [theEvent clickCount]; - if (mBlockedLastMouseDown && clickCount > 1) { - // Don't send a double click if the first click of the double click was - // blocked. - clickCount--; - } - geckoEvent.mClickCount = clickCount; - - if (modifierFlags & NSControlKeyMask) - geckoEvent.button = WidgetMouseEvent::eRightButton; - else - geckoEvent.button = WidgetMouseEvent::eLeftButton; - - mGeckoChild->DispatchInputEvent(&geckoEvent); - mBlockedLastMouseDown = NO; - - // XXX maybe call markedTextSelectionChanged:client: here? - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)mouseUp:(NSEvent *)theEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mGeckoChild || mBlockedLastMouseDown) - return; - if (mTextInputHandler->OnHandleEvent(theEvent)) { - return; - } - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - WidgetMouseEvent geckoEvent(true, eMouseUp, mGeckoChild, - WidgetMouseEvent::eReal); - [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent]; - if ([theEvent modifierFlags] & NSControlKeyMask) - geckoEvent.button = WidgetMouseEvent::eRightButton; - else - geckoEvent.button = WidgetMouseEvent::eLeftButton; - - // This might destroy our widget (and null out mGeckoChild). - bool defaultPrevented = - (mGeckoChild->DispatchInputEvent(&geckoEvent) == nsEventStatus_eConsumeNoDefault); - - // Check to see if we are double-clicking in the titlebar. - CGFloat locationInTitlebar = [[self window] frame].size.height - [theEvent locationInWindow].y; - LayoutDeviceIntPoint pos = geckoEvent.mRefPoint; - if (!defaultPrevented && [theEvent clickCount] == 2 && - !mGeckoChild->GetNonDraggableRegion().Contains(pos.x, pos.y) && - [[self window] isKindOfClass:[ToolbarWindow class]] && - (locationInTitlebar < [(ToolbarWindow*)[self window] titlebarHeight] || - locationInTitlebar < [(ToolbarWindow*)[self window] unifiedToolbarHeight])) { - if ([self shouldZoomOnDoubleClick]) { - [[self window] performZoom:nil]; - } else if ([self shouldMinimizeOnTitlebarDoubleClick]) { - NSButton *minimizeButton = [[self window] standardWindowButton:NSWindowMiniaturizeButton]; - [minimizeButton performClick:self]; - } - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)sendMouseEnterOrExitEvent:(NSEvent*)aEvent - enter:(BOOL)aEnter - exitFrom:(WidgetMouseEvent::ExitFrom)aExitFrom -{ - if (!mGeckoChild) - return; - - NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, [self window]); - NSPoint localEventLocation = [self convertPoint:windowEventLocation fromView:nil]; - - EventMessage msg = aEnter ? eMouseEnterIntoWidget : eMouseExitFromWidget; - WidgetMouseEvent event(true, msg, mGeckoChild, WidgetMouseEvent::eReal); - event.mRefPoint = mGeckoChild->CocoaPointsToDevPixels(localEventLocation); - - event.mExitFrom = aExitFrom; - - nsEventStatus status; // ignored - mGeckoChild->DispatchEvent(&event, status); -} - -- (void)handleMouseMoved:(NSEvent*)theEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mGeckoChild) - return; - if (mTextInputHandler->OnHandleEvent(theEvent)) { - return; - } - - WidgetMouseEvent geckoEvent(true, eMouseMove, mGeckoChild, - WidgetMouseEvent::eReal); - [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent]; - - mGeckoChild->DispatchInputEvent(&geckoEvent); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)mouseDragged:(NSEvent*)theEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mGeckoChild) - return; - if (mTextInputHandler->OnHandleEvent(theEvent)) { - return; - } - - gLastDragView = self; - - WidgetMouseEvent geckoEvent(true, eMouseMove, mGeckoChild, - WidgetMouseEvent::eReal); - [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent]; - - mGeckoChild->DispatchInputEvent(&geckoEvent); - - // Note, sending the above event might have destroyed our widget since we didn't retain. - // Fine so long as we don't access any local variables from here on. - gLastDragView = nil; - - // XXX maybe call markedTextSelectionChanged:client: here? - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)rightMouseDown:(NSEvent *)theEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - [self maybeRollup:theEvent]; - if (!mGeckoChild) - return; - if (mTextInputHandler->OnHandleEvent(theEvent)) { - return; - } - - // The right mouse went down, fire off a right mouse down event to gecko - WidgetMouseEvent geckoEvent(true, eMouseDown, mGeckoChild, - WidgetMouseEvent::eReal); - [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent]; - geckoEvent.button = WidgetMouseEvent::eRightButton; - geckoEvent.mClickCount = [theEvent clickCount]; - - mGeckoChild->DispatchInputEvent(&geckoEvent); - if (!mGeckoChild) - return; - - // Let the superclass do the context menu stuff. - [super rightMouseDown:theEvent]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)rightMouseUp:(NSEvent *)theEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mGeckoChild) - return; - if (mTextInputHandler->OnHandleEvent(theEvent)) { - return; - } - - WidgetMouseEvent geckoEvent(true, eMouseUp, mGeckoChild, - WidgetMouseEvent::eReal); - [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent]; - geckoEvent.button = WidgetMouseEvent::eRightButton; - geckoEvent.mClickCount = [theEvent clickCount]; - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - mGeckoChild->DispatchInputEvent(&geckoEvent); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)rightMouseDragged:(NSEvent*)theEvent -{ - if (!mGeckoChild) - return; - if (mTextInputHandler->OnHandleEvent(theEvent)) { - return; - } - - WidgetMouseEvent geckoEvent(true, eMouseMove, mGeckoChild, - WidgetMouseEvent::eReal); - [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent]; - geckoEvent.button = WidgetMouseEvent::eRightButton; - - // send event into Gecko by going directly to the - // the widget. - mGeckoChild->DispatchInputEvent(&geckoEvent); -} - -- (void)otherMouseDown:(NSEvent *)theEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - if ([self maybeRollup:theEvent] || - !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent, self)) - return; - - if (!mGeckoChild) - return; - if (mTextInputHandler->OnHandleEvent(theEvent)) { - return; - } - - WidgetMouseEvent geckoEvent(true, eMouseDown, mGeckoChild, - WidgetMouseEvent::eReal); - [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent]; - geckoEvent.button = WidgetMouseEvent::eMiddleButton; - geckoEvent.mClickCount = [theEvent clickCount]; - - mGeckoChild->DispatchInputEvent(&geckoEvent); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)otherMouseUp:(NSEvent *)theEvent -{ - if (!mGeckoChild) - return; - if (mTextInputHandler->OnHandleEvent(theEvent)) { - return; - } - - WidgetMouseEvent geckoEvent(true, eMouseUp, mGeckoChild, - WidgetMouseEvent::eReal); - [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent]; - geckoEvent.button = WidgetMouseEvent::eMiddleButton; - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - mGeckoChild->DispatchInputEvent(&geckoEvent); -} - -- (void)otherMouseDragged:(NSEvent*)theEvent -{ - if (!mGeckoChild) - return; - if (mTextInputHandler->OnHandleEvent(theEvent)) { - return; - } - - WidgetMouseEvent geckoEvent(true, eMouseMove, mGeckoChild, - WidgetMouseEvent::eReal); - [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent]; - geckoEvent.button = WidgetMouseEvent::eMiddleButton; - - // send event into Gecko by going directly to the - // the widget. - mGeckoChild->DispatchInputEvent(&geckoEvent); -} - -- (void)sendWheelStartOrStop:(EventMessage)msg forEvent:(NSEvent *)theEvent -{ - WidgetWheelEvent wheelEvent(true, msg, mGeckoChild); - [self convertCocoaMouseWheelEvent:theEvent toGeckoEvent:&wheelEvent]; - mExpectingWheelStop = (msg == eWheelOperationStart); - mGeckoChild->DispatchInputEvent(wheelEvent.AsInputEvent()); -} - -- (void)sendWheelCondition:(BOOL)condition - first:(EventMessage)first - second:(EventMessage)second - forEvent:(NSEvent *)theEvent -{ - if (mExpectingWheelStop == condition) { - [self sendWheelStartOrStop:first forEvent:theEvent]; - } - [self sendWheelStartOrStop:second forEvent:theEvent]; -} - -static PanGestureInput::PanGestureType -PanGestureTypeForEvent(NSEvent* aEvent) -{ - switch (nsCocoaUtils::EventPhase(aEvent)) { - case NSEventPhaseMayBegin: - return PanGestureInput::PANGESTURE_MAYSTART; - case NSEventPhaseCancelled: - return PanGestureInput::PANGESTURE_CANCELLED; - case NSEventPhaseBegan: - return PanGestureInput::PANGESTURE_START; - case NSEventPhaseChanged: - return PanGestureInput::PANGESTURE_PAN; - case NSEventPhaseEnded: - return PanGestureInput::PANGESTURE_END; - case NSEventPhaseNone: - switch (nsCocoaUtils::EventMomentumPhase(aEvent)) { - case NSEventPhaseBegan: - return PanGestureInput::PANGESTURE_MOMENTUMSTART; - case NSEventPhaseChanged: - return PanGestureInput::PANGESTURE_MOMENTUMPAN; - case NSEventPhaseEnded: - return PanGestureInput::PANGESTURE_MOMENTUMEND; - default: - NS_ERROR("unexpected event phase"); - return PanGestureInput::PANGESTURE_PAN; - } - default: - NS_ERROR("unexpected event phase"); - return PanGestureInput::PANGESTURE_PAN; - } -} - -static int32_t RoundUp(double aDouble) -{ - return aDouble < 0 ? static_cast<int32_t>(floor(aDouble)) : - static_cast<int32_t>(ceil(aDouble)); -} - -static int32_t -TakeLargestInt(gfx::Float* aFloat) -{ - int32_t result(*aFloat); // truncate towards zero - *aFloat -= result; - return result; -} - -static gfx::IntPoint -AccumulateIntegerDelta(NSEvent* aEvent) -{ - static gfx::Point sAccumulator(0.0f, 0.0f); - if (nsCocoaUtils::EventPhase(aEvent) == NSEventPhaseBegan) { - sAccumulator = gfx::Point(0.0f, 0.0f); - } - sAccumulator.x += [aEvent deltaX]; - sAccumulator.y += [aEvent deltaY]; - return gfx::IntPoint(TakeLargestInt(&sAccumulator.x), - TakeLargestInt(&sAccumulator.y)); -} - -static gfx::IntPoint -GetIntegerDeltaForEvent(NSEvent* aEvent) -{ - if (nsCocoaFeatures::OnSierraOrLater() && [aEvent hasPreciseScrollingDeltas]) { - // Pixel scroll events (events with hasPreciseScrollingDeltas == YES) - // carry pixel deltas in the scrollingDeltaX/Y fields and line scroll - // information in the deltaX/Y fields. - // Prior to 10.12, these line scroll fields would be zero for most pixel - // scroll events and non-zero for some, whenever at least a full line - // worth of pixel scrolling had accumulated. That's the behavior we want. - // Starting with 10.12 however, pixel scroll events no longer accumulate - // deltaX and deltaY; they just report floating point values for every - // single event. So we need to do our own accumulation. - return AccumulateIntegerDelta(aEvent); - } - - // For line scrolls, or pre-10.12, just use the rounded up value of deltaX / deltaY. - return gfx::IntPoint(RoundUp([aEvent deltaX]), RoundUp([aEvent deltaY])); -} - -- (void)scrollWheel:(NSEvent*)theEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - ChildViewMouseTracker::MouseScrolled(theEvent); - - if ([self maybeRollup:theEvent]) { - return; - } - - if (!mGeckoChild) { - return; - } - - NSEventPhase phase = nsCocoaUtils::EventPhase(theEvent); - // Fire eWheelOperationStart/End events when 2 fingers touch/release the - // touchpad. - if (phase & NSEventPhaseMayBegin) { - [self sendWheelCondition:YES - first:eWheelOperationEnd - second:eWheelOperationStart - forEvent:theEvent]; - } else if (phase & (NSEventPhaseEnded | NSEventPhaseCancelled)) { - [self sendWheelCondition:NO - first:eWheelOperationStart - second:eWheelOperationEnd - forEvent:theEvent]; - } - - if (!mGeckoChild) { - return; - } - RefPtr<nsChildView> geckoChildDeathGrip(mGeckoChild); - - NSPoint locationInWindow = nsCocoaUtils::EventLocationForWindow(theEvent, [self window]); - - // Use convertWindowCoordinatesRoundDown when converting the position to - // integer screen pixels in order to ensure that coordinates which are just - // inside the right / bottom edges of the window don't end up outside of the - // window after rounding. - ScreenPoint position = ViewAs<ScreenPixel>( - [self convertWindowCoordinatesRoundDown:locationInWindow], - PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent); - - bool usePreciseDeltas = nsCocoaUtils::HasPreciseScrollingDeltas(theEvent) && - Preferences::GetBool("mousewheel.enable_pixel_scrolling", true); - bool hasPhaseInformation = nsCocoaUtils::EventHasPhaseInformation(theEvent); - - gfx::IntPoint lineOrPageDelta = -GetIntegerDeltaForEvent(theEvent); - - Modifiers modifiers = nsCocoaUtils::ModifiersForEvent(theEvent); - - NSTimeInterval beforeNow = [[NSProcessInfo processInfo] systemUptime] - [theEvent timestamp]; - PRIntervalTime eventIntervalTime = PR_IntervalNow() - PR_MillisecondsToInterval(beforeNow * 1000); - TimeStamp eventTimeStamp = TimeStamp::Now() - TimeDuration::FromSeconds(beforeNow); - - ScreenPoint preciseDelta; - if (usePreciseDeltas) { - CGFloat pixelDeltaX = 0, pixelDeltaY = 0; - nsCocoaUtils::GetScrollingDeltas(theEvent, &pixelDeltaX, &pixelDeltaY); - double scale = geckoChildDeathGrip->BackingScaleFactor(); - preciseDelta = ScreenPoint(-pixelDeltaX * scale, -pixelDeltaY * scale); - } - - if (usePreciseDeltas && hasPhaseInformation) { - PanGestureInput panEvent(PanGestureTypeForEvent(theEvent), - eventIntervalTime, eventTimeStamp, - position, preciseDelta, modifiers); - panEvent.mLineOrPageDeltaX = lineOrPageDelta.x; - panEvent.mLineOrPageDeltaY = lineOrPageDelta.y; - - if (panEvent.mType == PanGestureInput::PANGESTURE_END) { - // Check if there's a momentum start event in the event queue, so that we - // can annotate this event. - NSEvent* nextWheelEvent = - [NSApp nextEventMatchingMask:NSScrollWheelMask - untilDate:[NSDate distantPast] - inMode:NSDefaultRunLoopMode - dequeue:NO]; - if (nextWheelEvent && - PanGestureTypeForEvent(nextWheelEvent) == PanGestureInput::PANGESTURE_MOMENTUMSTART) { - panEvent.mFollowedByMomentum = true; - } - } - - bool canTriggerSwipe = [self shouldConsiderStartingSwipeFromEvent:theEvent]; - panEvent.mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection = canTriggerSwipe; - geckoChildDeathGrip->DispatchAPZWheelInputEvent(panEvent, canTriggerSwipe); - } else if (usePreciseDeltas) { - // This is on 10.6 or old touchpads that don't have any phase information. - ScrollWheelInput wheelEvent(eventIntervalTime, eventTimeStamp, modifiers, - ScrollWheelInput::SCROLLMODE_INSTANT, - ScrollWheelInput::SCROLLDELTA_PIXEL, - position, - preciseDelta.x, - preciseDelta.y, - false); - wheelEvent.mLineOrPageDeltaX = lineOrPageDelta.x; - wheelEvent.mLineOrPageDeltaY = lineOrPageDelta.y; - wheelEvent.mIsMomentum = nsCocoaUtils::IsMomentumScrollEvent(theEvent); - geckoChildDeathGrip->DispatchAPZWheelInputEvent(wheelEvent, false); - } else { - ScrollWheelInput::ScrollMode scrollMode = ScrollWheelInput::SCROLLMODE_INSTANT; - if (gfxPrefs::SmoothScrollEnabled() && gfxPrefs::WheelSmoothScrollEnabled()) { - scrollMode = ScrollWheelInput::SCROLLMODE_SMOOTH; - } - ScrollWheelInput wheelEvent(eventIntervalTime, eventTimeStamp, modifiers, - scrollMode, - ScrollWheelInput::SCROLLDELTA_LINE, - position, - lineOrPageDelta.x, - lineOrPageDelta.y, - false); - wheelEvent.mLineOrPageDeltaX = lineOrPageDelta.x; - wheelEvent.mLineOrPageDeltaY = lineOrPageDelta.y; - geckoChildDeathGrip->DispatchAPZWheelInputEvent(wheelEvent, false); - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - --(NSMenu*)menuForEvent:(NSEvent*)theEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - if (!mGeckoChild) - return nil; - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - [self maybeRollup:theEvent]; - if (!mGeckoChild) - return nil; - - // Cocoa doesn't always dispatch a mouseDown: for a control-click event, - // depends on what we return from menuForEvent:. Gecko always expects one - // and expects the mouse down event before the context menu event, so - // get that event sent first if this is a left mouse click. - if ([theEvent type] == NSLeftMouseDown) { - [self mouseDown:theEvent]; - if (!mGeckoChild) - return nil; - } - - WidgetMouseEvent geckoEvent(true, eContextMenu, mGeckoChild, - WidgetMouseEvent::eReal); - [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent]; - geckoEvent.button = WidgetMouseEvent::eRightButton; - mGeckoChild->DispatchInputEvent(&geckoEvent); - if (!mGeckoChild) - return nil; - - [self maybeInitContextMenuTracking]; - - // Go up our view chain to fetch the correct menu to return. - return [self contextMenu]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (NSMenu*)contextMenu -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - NSView* superView = [self superview]; - if ([superView respondsToSelector:@selector(contextMenu)]) - return [(NSView<mozView>*)superView contextMenu]; - - return nil; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (void) convertCocoaMouseWheelEvent:(NSEvent*)aMouseEvent - toGeckoEvent:(WidgetWheelEvent*)outWheelEvent -{ - [self convertCocoaMouseEvent:aMouseEvent toGeckoEvent:outWheelEvent]; - - bool usePreciseDeltas = nsCocoaUtils::HasPreciseScrollingDeltas(aMouseEvent) && - Preferences::GetBool("mousewheel.enable_pixel_scrolling", true); - - outWheelEvent->mDeltaMode = - usePreciseDeltas ? nsIDOMWheelEvent::DOM_DELTA_PIXEL - : nsIDOMWheelEvent::DOM_DELTA_LINE; - outWheelEvent->mIsMomentum = nsCocoaUtils::IsMomentumScrollEvent(aMouseEvent); -} - -- (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent - toGeckoEvent:(WidgetInputEvent*)outGeckoEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - NS_ASSERTION(outGeckoEvent, "convertCocoaMouseEvent:toGeckoEvent: requires non-null aoutGeckoEvent"); - if (!outGeckoEvent) - return; - - nsCocoaUtils::InitInputEvent(*outGeckoEvent, aMouseEvent); - - // convert point to view coordinate system - NSPoint locationInWindow = nsCocoaUtils::EventLocationForWindow(aMouseEvent, [self window]); - - outGeckoEvent->mRefPoint = [self convertWindowCoordinates:locationInWindow]; - - WidgetMouseEventBase* mouseEvent = outGeckoEvent->AsMouseEventBase(); - mouseEvent->buttons = 0; - NSUInteger mouseButtons = [NSEvent pressedMouseButtons]; - - if (mouseButtons & 0x01) { - mouseEvent->buttons |= WidgetMouseEvent::eLeftButtonFlag; - } - if (mouseButtons & 0x02) { - mouseEvent->buttons |= WidgetMouseEvent::eRightButtonFlag; - } - if (mouseButtons & 0x04) { - mouseEvent->buttons |= WidgetMouseEvent::eMiddleButtonFlag; - } - if (mouseButtons & 0x08) { - mouseEvent->buttons |= WidgetMouseEvent::e4thButtonFlag; - } - if (mouseButtons & 0x10) { - mouseEvent->buttons |= WidgetMouseEvent::e5thButtonFlag; - } - - switch ([aMouseEvent type]) { - case NSLeftMouseDown: - case NSLeftMouseUp: - case NSLeftMouseDragged: - case NSRightMouseDown: - case NSRightMouseUp: - case NSRightMouseDragged: - case NSOtherMouseDown: - case NSOtherMouseUp: - case NSOtherMouseDragged: - if ([aMouseEvent subtype] == NSTabletPointEventSubtype) { - mouseEvent->pressure = [aMouseEvent pressure]; - MOZ_ASSERT(mouseEvent->pressure >= 0.0 && mouseEvent->pressure <= 1.0); - } - break; - - default: - // Don't check other NSEvents for pressure. - break; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (BOOL)shouldZoomOnDoubleClick -{ - if ([NSWindow respondsToSelector:@selector(_shouldZoomOnDoubleClick)]) { - return [NSWindow _shouldZoomOnDoubleClick]; - } - return nsCocoaFeatures::OnYosemiteOrLater(); -} - -- (BOOL)shouldMinimizeOnTitlebarDoubleClick -{ - NSString *MDAppleMiniaturizeOnDoubleClickKey = - @"AppleMiniaturizeOnDoubleClick"; - NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; - bool shouldMinimize = [[userDefaults - objectForKey:MDAppleMiniaturizeOnDoubleClickKey] boolValue]; - - return shouldMinimize; -} - -#pragma mark - -// NSTextInputClient implementation - -- (NSRange)markedRange -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - NS_ENSURE_TRUE(mTextInputHandler, NSMakeRange(NSNotFound, 0)); - return mTextInputHandler->MarkedRange(); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRange(0, 0)); -} - -- (NSRange)selectedRange -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - NS_ENSURE_TRUE(mTextInputHandler, NSMakeRange(NSNotFound, 0)); - return mTextInputHandler->SelectedRange(); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRange(0, 0)); -} - -- (BOOL)drawsVerticallyForCharacterAtIndex:(NSUInteger)charIndex -{ - NS_ENSURE_TRUE(mTextInputHandler, NO); - if (charIndex == NSNotFound) { - return NO; - } - return mTextInputHandler->DrawsVerticallyForCharacterAtIndex(charIndex); -} - -- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint -{ - NS_ENSURE_TRUE(mTextInputHandler, 0); - return mTextInputHandler->CharacterIndexForPoint(thePoint); -} - -- (NSArray*)validAttributesForMarkedText -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - NS_ENSURE_TRUE(mTextInputHandler, [NSArray array]); - return mTextInputHandler->GetValidAttributesForMarkedText(); - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - NS_ENSURE_TRUE_VOID(mGeckoChild); - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - NSAttributedString* attrStr; - if ([aString isKindOfClass:[NSAttributedString class]]) { - attrStr = static_cast<NSAttributedString*>(aString); - } else { - attrStr = [[[NSAttributedString alloc] initWithString:aString] autorelease]; - } - - mTextInputHandler->InsertText(attrStr, &replacementRange); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)doCommandBySelector:(SEL)aSelector -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mGeckoChild || !mTextInputHandler) { - return; - } - - const char* sel = reinterpret_cast<const char*>(aSelector); - if (!mTextInputHandler->DoCommandBySelector(sel)) { - [super doCommandBySelector:aSelector]; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)unmarkText -{ - NS_ENSURE_TRUE_VOID(mTextInputHandler); - mTextInputHandler->CommitIMEComposition(); -} - -- (BOOL) hasMarkedText -{ - NS_ENSURE_TRUE(mTextInputHandler, NO); - return mTextInputHandler->HasMarkedText(); -} - -- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange - replacementRange:(NSRange)replacementRange -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - NS_ENSURE_TRUE_VOID(mTextInputHandler); - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - NSAttributedString* attrStr; - if ([aString isKindOfClass:[NSAttributedString class]]) { - attrStr = static_cast<NSAttributedString*>(aString); - } else { - attrStr = [[[NSAttributedString alloc] initWithString:aString] autorelease]; - } - - mTextInputHandler->SetMarkedText(attrStr, selectedRange, &replacementRange); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)aRange - actualRange:(NSRangePointer)actualRange -{ - NS_ENSURE_TRUE(mTextInputHandler, nil); - return mTextInputHandler->GetAttributedSubstringFromRange(aRange, - actualRange); -} - -- (NSRect)firstRectForCharacterRange:(NSRange)aRange - actualRange:(NSRangePointer)actualRange -{ - NS_ENSURE_TRUE(mTextInputHandler, NSMakeRect(0.0, 0.0, 0.0, 0.0)); - return mTextInputHandler->FirstRectForCharacterRange(aRange, actualRange); -} - -- (void)quickLookWithEvent:(NSEvent*)event -{ - // Show dictionary by current point - WidgetContentCommandEvent - contentCommandEvent(true, eContentCommandLookUpDictionary, mGeckoChild); - NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; - contentCommandEvent.mRefPoint = mGeckoChild->CocoaPointsToDevPixels(point); - mGeckoChild->DispatchWindowEvent(contentCommandEvent); - // The widget might have been destroyed. -} - -- (NSInteger)windowLevel -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - NS_ENSURE_TRUE(mTextInputHandler, [[self window] level]); - return mTextInputHandler->GetWindowLevel(); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSNormalWindowLevel); -} - -#pragma mark - - -// This is a private API that Cocoa uses. -// Cocoa will call this after the menu system returns "NO" for "performKeyEquivalent:". -// We want all they key events we can get so just return YES. In particular, this fixes -// ctrl-tab - we don't get a "keyDown:" call for that without this. -- (BOOL)_wantsKeyDownForEvent:(NSEvent*)event -{ - return YES; -} - -- (NSEvent*)lastKeyDownEvent -{ - return mLastKeyDownEvent; -} - -- (void)keyDown:(NSEvent*)theEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [mLastKeyDownEvent release]; - mLastKeyDownEvent = [theEvent retain]; - - // Weird things can happen on keyboard input if the key window isn't in the - // current space. For example see bug 1056251. To get around this, always - // make sure that, if our window is key, it's also made frontmost. Doing - // this automatically switches to whatever space our window is in. Safari - // does something similar. Our window should normally always be key -- - // otherwise why is the OS sending us a key down event? But it's just - // possible we're in Gecko's hidden window, so we check first. - NSWindow *viewWindow = [self window]; - if (viewWindow && [viewWindow isKeyWindow]) { - [viewWindow orderWindow:NSWindowAbove relativeTo:0]; - } - -#if !defined(RELEASE_OR_BETA) || defined(DEBUG) - if (!Preferences::GetBool("intl.allow-insecure-text-input", false) && - mGeckoChild && mTextInputHandler && mTextInputHandler->IsFocused()) { - if (mGeckoChild->GetInputContext().IsPasswordEditor() && - !TextInputHandler::IsSecureEventInputEnabled()) { - #define CRASH_MESSAGE "A password editor has focus, but not in secure input mode" - MOZ_CRASH(CRASH_MESSAGE); - #undef CRASH_MESSAGE - } else if (!mGeckoChild->GetInputContext().IsPasswordEditor() && - TextInputHandler::IsSecureEventInputEnabled()) { - #define CRASH_MESSAGE "A non-password editor has focus, but in secure input mode" - MOZ_CRASH(CRASH_MESSAGE); - #undef CRASH_MESSAGE - } - } -#endif // #if !defined(RELEASE_OR_BETA) || defined(DEBUG) - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - bool handled = false; - if (mGeckoChild && mTextInputHandler) { - handled = mTextInputHandler->HandleKeyDownEvent(theEvent); - } - - // We always allow keyboard events to propagate to keyDown: but if they are not - // handled we give special Application menu items a chance to act. - if (!handled && sApplicationMenu) { - [sApplicationMenu performKeyEquivalent:theEvent]; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)keyUp:(NSEvent*)theEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - NS_ENSURE_TRUE(mGeckoChild, ); - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - mTextInputHandler->HandleKeyUpEvent(theEvent); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)insertNewline:(id)sender -{ - if (mTextInputHandler) { - NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:@"\n"]; - mTextInputHandler->InsertText(attrStr); - [attrStr release]; - } -} - -- (void)flagsChanged:(NSEvent*)theEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - NS_ENSURE_TRUE(mGeckoChild, ); - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - mTextInputHandler->HandleFlagsChanged(theEvent); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (BOOL) isFirstResponder -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - NSResponder* resp = [[self window] firstResponder]; - return (resp == (NSResponder*)self); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); -} - -- (BOOL)isDragInProgress -{ - if (!mDragService) - return NO; - - nsCOMPtr<nsIDragSession> dragSession; - mDragService->GetCurrentSession(getter_AddRefs(dragSession)); - return dragSession != nullptr; -} - -- (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent -{ - // If we're being destroyed assume the default -- return YES. - if (!mGeckoChild) - return YES; - - WidgetMouseEvent geckoEvent(true, eMouseActivate, mGeckoChild, - WidgetMouseEvent::eReal); - [self convertCocoaMouseEvent:aEvent toGeckoEvent:&geckoEvent]; - return (mGeckoChild->DispatchInputEvent(&geckoEvent) != nsEventStatus_eConsumeNoDefault); -} - -// We must always call through to our superclass, even when mGeckoChild is -// nil -- otherwise the keyboard focus can end up in the wrong NSView. -- (BOOL)becomeFirstResponder -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - return [super becomeFirstResponder]; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(YES); -} - -- (void)viewsWindowDidBecomeKey -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mGeckoChild) - return; - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - // check to see if the window implements the mozWindow protocol. This - // allows embedders to avoid re-entrant calls to -makeKeyAndOrderFront, - // which can happen because these activate calls propagate out - // to the embedder via nsIEmbeddingSiteWindow::SetFocus(). - BOOL isMozWindow = [[self window] respondsToSelector:@selector(setSuppressMakeKeyFront:)]; - if (isMozWindow) - [[self window] setSuppressMakeKeyFront:YES]; - - nsIWidgetListener* listener = mGeckoChild->GetWidgetListener(); - if (listener) - listener->WindowActivated(); - - if (isMozWindow) - [[self window] setSuppressMakeKeyFront:NO]; - - if (mGeckoChild->GetInputContext().IsPasswordEditor()) { - TextInputHandler::EnableSecureEventInput(); - } else { - TextInputHandler::EnsureSecureEventInputDisabled(); - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)viewsWindowDidResignKey -{ - if (!mGeckoChild) - return; - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - nsIWidgetListener* listener = mGeckoChild->GetWidgetListener(); - if (listener) - listener->WindowDeactivated(); - - TextInputHandler::EnsureSecureEventInputDisabled(); -} - -// If the call to removeFromSuperview isn't delayed from nsChildView:: -// TearDownView(), the NSView hierarchy might get changed during calls to -// [ChildView drawRect:], which leads to "beyond bounds" exceptions in -// NSCFArray. For more info see bmo bug 373122. Apple's docs claim that -// removeFromSuperviewWithoutNeedingDisplay "can be safely invoked during -// display" (whatever "display" means). But it's _not_ true that it can be -// safely invoked during calls to [NSView drawRect:]. We use -// removeFromSuperview here because there's no longer any danger of being -// "invoked during display", and because doing do clears up bmo bug 384343. -- (void)delayedTearDown -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [self removeFromSuperview]; - [self release]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -#pragma mark - - -// drag'n'drop stuff -#define kDragServiceContractID "@mozilla.org/widget/dragservice;1" - -- (NSDragOperation)dragOperationFromDragAction:(int32_t)aDragAction -{ - if (nsIDragService::DRAGDROP_ACTION_LINK & aDragAction) - return NSDragOperationLink; - if (nsIDragService::DRAGDROP_ACTION_COPY & aDragAction) - return NSDragOperationCopy; - if (nsIDragService::DRAGDROP_ACTION_MOVE & aDragAction) - return NSDragOperationGeneric; - return NSDragOperationNone; -} - -- (LayoutDeviceIntPoint)convertWindowCoordinates:(NSPoint)aPoint -{ - if (!mGeckoChild) { - return LayoutDeviceIntPoint(0, 0); - } - - NSPoint localPoint = [self convertPoint:aPoint fromView:nil]; - return mGeckoChild->CocoaPointsToDevPixels(localPoint); -} - -- (LayoutDeviceIntPoint)convertWindowCoordinatesRoundDown:(NSPoint)aPoint -{ - if (!mGeckoChild) { - return LayoutDeviceIntPoint(0, 0); - } - - NSPoint localPoint = [self convertPoint:aPoint fromView:nil]; - return mGeckoChild->CocoaPointsToDevPixelsRoundDown(localPoint); -} - -// This is a utility function used by NSView drag event methods -// to send events. It contains all of the logic needed for Gecko -// dragging to work. Returns the appropriate cocoa drag operation code. -- (NSDragOperation)doDragAction:(EventMessage)aMessage sender:(id)aSender -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - if (!mGeckoChild) - return NSDragOperationNone; - - MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView doDragAction: entered\n")); - - if (!mDragService) { - CallGetService(kDragServiceContractID, &mDragService); - NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!"); - if (!mDragService) - return NSDragOperationNone; - } - - if (aMessage == eDragEnter) { - mDragService->StartDragSession(); - } - - nsCOMPtr<nsIDragSession> dragSession; - mDragService->GetCurrentSession(getter_AddRefs(dragSession)); - if (dragSession) { - if (aMessage == eDragOver) { - // fire the drag event at the source. Just ignore whether it was - // cancelled or not as there isn't actually a means to stop the drag - mDragService->FireDragEventAtSource(eDrag); - dragSession->SetCanDrop(false); - } else if (aMessage == eDrop) { - // We make the assumption that the dragOver handlers have correctly set - // the |canDrop| property of the Drag Session. - bool canDrop = false; - if (!NS_SUCCEEDED(dragSession->GetCanDrop(&canDrop)) || !canDrop) { - [self doDragAction:eDragExit sender:aSender]; - - nsCOMPtr<nsIDOMNode> sourceNode; - dragSession->GetSourceNode(getter_AddRefs(sourceNode)); - if (!sourceNode) { - mDragService->EndDragSession(false); - } - return NSDragOperationNone; - } - } - - unsigned int modifierFlags = [[NSApp currentEvent] modifierFlags]; - uint32_t action = nsIDragService::DRAGDROP_ACTION_MOVE; - // force copy = option, alias = cmd-option, default is move - if (modifierFlags & NSAlternateKeyMask) { - if (modifierFlags & NSCommandKeyMask) - action = nsIDragService::DRAGDROP_ACTION_LINK; - else - action = nsIDragService::DRAGDROP_ACTION_COPY; - } - dragSession->SetDragAction(action); - } - - // set up gecko event - WidgetDragEvent geckoEvent(true, aMessage, mGeckoChild); - nsCocoaUtils::InitInputEvent(geckoEvent, [NSApp currentEvent]); - - // Use our own coordinates in the gecko event. - // Convert event from gecko global coords to gecko view coords. - NSPoint draggingLoc = [aSender draggingLocation]; - - geckoEvent.mRefPoint = [self convertWindowCoordinates:draggingLoc]; - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - mGeckoChild->DispatchInputEvent(&geckoEvent); - if (!mGeckoChild) - return NSDragOperationNone; - - if (dragSession) { - switch (aMessage) { - case eDragEnter: - case eDragOver: { - uint32_t dragAction; - dragSession->GetDragAction(&dragAction); - - // If TakeChildProcessDragAction returns something other than - // DRAGDROP_ACTION_UNINITIALIZED, it means that the last event was sent - // to the child process and this event is also being sent to the child - // process. In this case, use the last event's action instead. - nsDragService* dragService = static_cast<nsDragService *>(mDragService); - int32_t childDragAction = dragService->TakeChildProcessDragAction(); - if (childDragAction != nsIDragService::DRAGDROP_ACTION_UNINITIALIZED) { - dragAction = childDragAction; - } - - return [self dragOperationFromDragAction:dragAction]; - } - case eDragExit: - case eDrop: { - nsCOMPtr<nsIDOMNode> sourceNode; - dragSession->GetSourceNode(getter_AddRefs(sourceNode)); - if (!sourceNode) { - // We're leaving a window while doing a drag that was - // initiated in a different app. End the drag session, - // since we're done with it for now (until the user - // drags back into mozilla). - mDragService->EndDragSession(false); - } - } - default: - break; - } - } - - return NSDragOperationGeneric; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone); -} - -- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingEntered: entered\n")); - - // there should never be a globalDragPboard when "draggingEntered:" is - // called, but just in case we'll take care of it here. - [globalDragPboard release]; - - // Set the global drag pasteboard that will be used for this drag session. - // This will be set back to nil when the drag session ends (mouse exits - // the view or a drop happens within the view). - globalDragPboard = [[sender draggingPasteboard] retain]; - - return [self doDragAction:eDragEnter sender:sender]; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone); -} - -- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender -{ - MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingUpdated: entered\n")); - - return [self doDragAction:eDragOver sender:sender]; -} - -- (void)draggingExited:(id <NSDraggingInfo>)sender -{ - MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingExited: entered\n")); - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - [self doDragAction:eDragExit sender:sender]; - NS_IF_RELEASE(mDragService); -} - -- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender -{ - nsAutoRetainCocoaObject kungFuDeathGrip(self); - BOOL handled = [self doDragAction:eDrop sender:sender] != NSDragOperationNone; - NS_IF_RELEASE(mDragService); - return handled; -} - -// NSDraggingSource -- (void)draggedImage:(NSImage *)anImage movedTo:(NSPoint)aPoint -{ - // Get the drag service if it isn't already cached. The drag service - // isn't cached when dragging over a different application. - nsCOMPtr<nsIDragService> dragService = mDragService; - if (!dragService) { - dragService = do_GetService(kDragServiceContractID); - } - - if (dragService) { - NSPoint pnt = [NSEvent mouseLocation]; - FlipCocoaScreenCoordinate(pnt); - - LayoutDeviceIntPoint devPoint = mGeckoChild->CocoaPointsToDevPixels(pnt); - dragService->DragMoved(devPoint.x, devPoint.y); - } -} - -// NSDraggingSource -- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - gDraggedTransferables = nullptr; - - NSEvent *currentEvent = [NSApp currentEvent]; - gUserCancelledDrag = ([currentEvent type] == NSKeyDown && - [currentEvent keyCode] == kVK_Escape); - - if (!mDragService) { - CallGetService(kDragServiceContractID, &mDragService); - NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!"); - } - - if (mDragService) { - // set the dragend point from the current mouse location - nsDragService* dragService = static_cast<nsDragService *>(mDragService); - NSPoint pnt = [NSEvent mouseLocation]; - FlipCocoaScreenCoordinate(pnt); - dragService->SetDragEndPoint(gfx::IntPoint::Round(pnt.x, pnt.y)); - - // XXX: dropEffect should be updated per |operation|. - // As things stand though, |operation| isn't well handled within "our" - // events, that is, when the drop happens within the window: it is set - // either to NSDragOperationGeneric or to NSDragOperationNone. - // For that reason, it's not yet possible to override dropEffect per the - // given OS value, and it's also unclear what's the correct dropEffect - // value for NSDragOperationGeneric that is passed by other applications. - // All that said, NSDragOperationNone is still reliable. - if (operation == NSDragOperationNone) { - nsCOMPtr<nsIDOMDataTransfer> dataTransfer; - dragService->GetDataTransfer(getter_AddRefs(dataTransfer)); - if (dataTransfer) - dataTransfer->SetDropEffectInt(nsIDragService::DRAGDROP_ACTION_NONE); - } - - mDragService->EndDragSession(true); - NS_RELEASE(mDragService); - } - - [globalDragPboard release]; - globalDragPboard = nil; - [gLastDragMouseDownEvent release]; - gLastDragMouseDownEvent = nil; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// NSDraggingSource -// this is just implemented so we comply with the NSDraggingSource informal protocol -- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal -{ - return UINT_MAX; -} - -// This method is a callback typically invoked in response to a drag ending on the desktop -// or a Findow folder window; the argument passed is a path to the drop location, to be used -// in constructing a complete pathname for the file(s) we want to create as a result of -// the drag. -- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDestination -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - nsresult rv; - - MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView namesOfPromisedFilesDroppedAtDestination: entering callback for promised files\n")); - - nsCOMPtr<nsIFile> targFile; - NS_NewLocalFile(EmptyString(), true, getter_AddRefs(targFile)); - nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(targFile); - if (!macLocalFile) { - NS_ERROR("No Mac local file"); - return nil; - } - - if (!NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)dropDestination))) { - NS_ERROR("failed InitWithCFURL"); - return nil; - } - - if (!gDraggedTransferables) - return nil; - - uint32_t transferableCount; - rv = gDraggedTransferables->GetLength(&transferableCount); - if (NS_FAILED(rv)) - return nil; - - for (uint32_t i = 0; i < transferableCount; i++) { - nsCOMPtr<nsITransferable> item = do_QueryElementAt(gDraggedTransferables, i); - if (!item) { - NS_ERROR("no transferable"); - return nil; - } - - item->SetTransferData(kFilePromiseDirectoryMime, macLocalFile, sizeof(nsIFile*)); - - // now request the kFilePromiseMime data, which will invoke the data provider - // If successful, the returned data is a reference to the resulting file. - nsCOMPtr<nsISupports> fileDataPrimitive; - uint32_t dataSize = 0; - item->GetTransferData(kFilePromiseMime, getter_AddRefs(fileDataPrimitive), &dataSize); - } - - NSPasteboard* generalPboard = [NSPasteboard pasteboardWithName:NSDragPboard]; - NSData* data = [generalPboard dataForType:@"application/x-moz-file-promise-dest-filename"]; - NSString* name = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - NSArray* rslt = [NSArray arrayWithObject:name]; - - [name release]; - - return rslt; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -#pragma mark - - -// Support for the "Services" menu. We currently only support sending strings -// and HTML to system services. - -- (id)validRequestorForSendType:(NSString *)sendType - returnType:(NSString *)returnType -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - // sendType contains the type of data that the service would like this - // application to send to it. sendType is nil if the service is not - // requesting any data. - // - // returnType contains the type of data the the service would like to - // return to this application (e.g., to overwrite the selection). - // returnType is nil if the service will not return any data. - // - // The following condition thus triggers when the service expects a string - // or HTML from us or no data at all AND when the service will either not - // send back any data to us or will send a string or HTML back to us. - -#define IsSupportedType(typeStr) ([typeStr isEqual:NSStringPboardType] || [typeStr isEqual:NSHTMLPboardType]) - - id result = nil; - - if ((!sendType || IsSupportedType(sendType)) && - (!returnType || IsSupportedType(returnType))) { - if (mGeckoChild) { - // Assume that this object will be able to handle this request. - result = self; - - // Keep the ChildView alive during this operation. - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - if (sendType) { - // Determine if there is a current selection (chrome/content). - if (!nsClipboard::sSelectionCache) { - result = nil; - } - } - - // Determine if we can paste (if receiving data from the service). - if (mGeckoChild && returnType) { - WidgetContentCommandEvent command(true, - eContentCommandPasteTransferable, - mGeckoChild, true); - // This might possibly destroy our widget (and null out mGeckoChild). - mGeckoChild->DispatchWindowEvent(command); - if (!mGeckoChild || !command.mSucceeded || !command.mIsEnabled) - result = nil; - } - } - } - -#undef IsSupportedType - - // Give the superclass a chance if this object will not handle this request. - if (!result) - result = [super validRequestorForSendType:sendType returnType:returnType]; - - return result; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard - types:(NSArray *)types -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - - // Make sure that the service will accept strings or HTML. - if ([types containsObject:NSStringPboardType] == NO && - [types containsObject:NSHTMLPboardType] == NO) - return NO; - - // Bail out if there is no Gecko object. - if (!mGeckoChild) - return NO; - - // Transform the transferable to an NSDictionary. - NSDictionary* pasteboardOutputDict = nullptr; - - pasteboardOutputDict = nsClipboard:: - PasteboardDictFromTransferable(nsClipboard::sSelectionCache); - - if (!pasteboardOutputDict) - return NO; - - // Declare the pasteboard types. - unsigned int typeCount = [pasteboardOutputDict count]; - NSMutableArray* declaredTypes = [NSMutableArray arrayWithCapacity:typeCount]; - [declaredTypes addObjectsFromArray:[pasteboardOutputDict allKeys]]; - [pboard declareTypes:declaredTypes owner:nil]; - - // Write the data to the pasteboard. - for (unsigned int i = 0; i < typeCount; i++) { - NSString* currentKey = [declaredTypes objectAtIndex:i]; - id currentValue = [pasteboardOutputDict valueForKey:currentKey]; - - if (currentKey == NSStringPboardType || - currentKey == kCorePboardType_url || - currentKey == kCorePboardType_urld || - currentKey == kCorePboardType_urln) { - [pboard setString:currentValue forType:currentKey]; - } else if (currentKey == NSHTMLPboardType) { - [pboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue)) forType:currentKey]; - } else if (currentKey == NSTIFFPboardType) { - [pboard setData:currentValue forType:currentKey]; - } else if (currentKey == NSFilesPromisePboardType) { - [pboard setPropertyList:currentValue forType:currentKey]; - } - } - - return YES; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); -} - -// Called if the service wants us to replace the current selection. -- (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pboard -{ - nsresult rv; - nsCOMPtr<nsITransferable> trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); - if (NS_FAILED(rv)) - return NO; - trans->Init(nullptr); - - trans->AddDataFlavor(kUnicodeMime); - trans->AddDataFlavor(kHTMLMime); - - rv = nsClipboard::TransferableFromPasteboard(trans, pboard); - if (NS_FAILED(rv)) - return NO; - - NS_ENSURE_TRUE(mGeckoChild, false); - - WidgetContentCommandEvent command(true, - eContentCommandPasteTransferable, - mGeckoChild); - command.mTransferable = trans; - mGeckoChild->DispatchWindowEvent(command); - - return command.mSucceeded && command.mIsEnabled; -} - -NS_IMETHODIMP -nsChildView::GetSelectionAsPlaintext(nsAString& aResult) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (!nsClipboard::sSelectionCache) { - MOZ_ASSERT(aResult.IsEmpty()); - return NS_OK; - } - - // Get the current chrome or content selection. - NSDictionary* pasteboardOutputDict = nullptr; - pasteboardOutputDict = nsClipboard:: - PasteboardDictFromTransferable(nsClipboard::sSelectionCache); - - if (NS_WARN_IF(!pasteboardOutputDict)) { - return NS_ERROR_FAILURE; - } - - // Declare the pasteboard types. - unsigned int typeCount = [pasteboardOutputDict count]; - NSMutableArray* declaredTypes = [NSMutableArray arrayWithCapacity:typeCount]; - [declaredTypes addObjectsFromArray:[pasteboardOutputDict allKeys]]; - NSString* currentKey = [declaredTypes objectAtIndex:0]; - NSString* currentValue = [pasteboardOutputDict valueForKey:currentKey]; - const char* textSelection = [currentValue UTF8String]; - aResult = NS_ConvertUTF8toUTF16(textSelection); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -#pragma mark - - -#ifdef ACCESSIBILITY - -/* Every ChildView has a corresponding mozDocAccessible object that is doing all - the heavy lifting. The topmost ChildView corresponds to a mozRootAccessible - object. - - All ChildView needs to do is to route all accessibility calls (from the NSAccessibility APIs) - down to its object, pretending that they are the same. -*/ -- (id<mozAccessible>)accessible -{ - if (!mGeckoChild) - return nil; - - id<mozAccessible> nativeAccessible = nil; - - nsAutoRetainCocoaObject kungFuDeathGrip(self); - RefPtr<nsChildView> geckoChild(mGeckoChild); - RefPtr<a11y::Accessible> accessible = geckoChild->GetDocumentAccessible(); - if (!accessible) - return nil; - - accessible->GetNativeInterface((void**)&nativeAccessible); - -#ifdef DEBUG_hakan - NSAssert(![nativeAccessible isExpired], @"native acc is expired!!!"); -#endif - - return nativeAccessible; -} - -/* Implementation of formal mozAccessible formal protocol (enabling mozViews - to talk to mozAccessible objects in the accessibility module). */ - -- (BOOL)hasRepresentedView -{ - return YES; -} - -- (id)representedView -{ - return self; -} - -- (BOOL)isRoot -{ - return [[self accessible] isRoot]; -} - -#ifdef DEBUG -- (void)printHierarchy -{ - [[self accessible] printHierarchy]; -} -#endif - -#pragma mark - - -// general - -- (BOOL)accessibilityIsIgnored -{ - if (!mozilla::a11y::ShouldA11yBeEnabled()) - return [super accessibilityIsIgnored]; - - return [[self accessible] accessibilityIsIgnored]; -} - -- (id)accessibilityHitTest:(NSPoint)point -{ - if (!mozilla::a11y::ShouldA11yBeEnabled()) - return [super accessibilityHitTest:point]; - - return [[self accessible] accessibilityHitTest:point]; -} - -- (id)accessibilityFocusedUIElement -{ - if (!mozilla::a11y::ShouldA11yBeEnabled()) - return [super accessibilityFocusedUIElement]; - - return [[self accessible] accessibilityFocusedUIElement]; -} - -// actions - -- (NSArray*)accessibilityActionNames -{ - if (!mozilla::a11y::ShouldA11yBeEnabled()) - return [super accessibilityActionNames]; - - return [[self accessible] accessibilityActionNames]; -} - -- (NSString*)accessibilityActionDescription:(NSString*)action -{ - if (!mozilla::a11y::ShouldA11yBeEnabled()) - return [super accessibilityActionDescription:action]; - - return [[self accessible] accessibilityActionDescription:action]; -} - -- (void)accessibilityPerformAction:(NSString*)action -{ - if (!mozilla::a11y::ShouldA11yBeEnabled()) - return [super accessibilityPerformAction:action]; - - return [[self accessible] accessibilityPerformAction:action]; -} - -// attributes - -- (NSArray*)accessibilityAttributeNames -{ - if (!mozilla::a11y::ShouldA11yBeEnabled()) - return [super accessibilityAttributeNames]; - - return [[self accessible] accessibilityAttributeNames]; -} - -- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute -{ - if (!mozilla::a11y::ShouldA11yBeEnabled()) - return [super accessibilityIsAttributeSettable:attribute]; - - return [[self accessible] accessibilityIsAttributeSettable:attribute]; -} - -- (id)accessibilityAttributeValue:(NSString*)attribute -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - if (!mozilla::a11y::ShouldA11yBeEnabled()) - return [super accessibilityAttributeValue:attribute]; - - id<mozAccessible> accessible = [self accessible]; - - // if we're the root (topmost) accessible, we need to return our native AXParent as we - // traverse outside to the hierarchy of whoever embeds us. thus, fall back on NSView's - // default implementation for this attribute. - if ([attribute isEqualToString:NSAccessibilityParentAttribute] && [accessible isRoot]) { - id parentAccessible = [super accessibilityAttributeValue:attribute]; - return parentAccessible; - } - - return [accessible accessibilityAttributeValue:attribute]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -#endif /* ACCESSIBILITY */ - -@end - -@implementation PixelHostingView - -- (BOOL)isFlipped { - return YES; -} - -- (NSView*)hitTest:(NSPoint)aPoint { - return nil; -} - -- (void)drawRect:(NSRect)aRect { - [(ChildView*)[self superview] doDrawRect:aRect]; -} - -- (BOOL)wantsBestResolutionOpenGLSurface { - return nsCocoaUtils::HiDPIEnabled() ? YES : NO; -} - -@end - -#pragma mark - - -void -ChildViewMouseTracker::OnDestroyView(ChildView* aView) -{ - if (sLastMouseEventView == aView) { - sLastMouseEventView = nil; - [sLastMouseMoveEvent release]; - sLastMouseMoveEvent = nil; - } -} - -void -ChildViewMouseTracker::OnDestroyWindow(NSWindow* aWindow) -{ - if (sWindowUnderMouse == aWindow) { - sWindowUnderMouse = nil; - } -} - -void -ChildViewMouseTracker::MouseEnteredWindow(NSEvent* aEvent) -{ - sWindowUnderMouse = [aEvent window]; - ReEvaluateMouseEnterState(aEvent); -} - -void -ChildViewMouseTracker::MouseExitedWindow(NSEvent* aEvent) -{ - if (sWindowUnderMouse == [aEvent window]) { - sWindowUnderMouse = nil; - ReEvaluateMouseEnterState(aEvent); - } -} - -void -ChildViewMouseTracker::ReEvaluateMouseEnterState(NSEvent* aEvent, ChildView* aOldView) -{ - ChildView* oldView = aOldView ? aOldView : sLastMouseEventView; - sLastMouseEventView = ViewForEvent(aEvent); - if (sLastMouseEventView != oldView) { - // Send enter and / or exit events. - WidgetMouseEvent::ExitFrom exitFrom = - [sLastMouseEventView window] == [oldView window] ? - WidgetMouseEvent::eChild : WidgetMouseEvent::eTopLevel; - [oldView sendMouseEnterOrExitEvent:aEvent - enter:NO - exitFrom:exitFrom]; - // After the cursor exits the window set it to a visible regular arrow cursor. - if (exitFrom == WidgetMouseEvent::eTopLevel) { - [[nsCursorManager sharedInstance] setCursor:eCursor_standard]; - } - [sLastMouseEventView sendMouseEnterOrExitEvent:aEvent - enter:YES - exitFrom:exitFrom]; - } -} - -void -ChildViewMouseTracker::ResendLastMouseMoveEvent() -{ - if (sLastMouseMoveEvent) { - MouseMoved(sLastMouseMoveEvent); - } -} - -void -ChildViewMouseTracker::MouseMoved(NSEvent* aEvent) -{ - MouseEnteredWindow(aEvent); - [sLastMouseEventView handleMouseMoved:aEvent]; - if (sLastMouseMoveEvent != aEvent) { - [sLastMouseMoveEvent release]; - sLastMouseMoveEvent = [aEvent retain]; - } -} - -void -ChildViewMouseTracker::MouseScrolled(NSEvent* aEvent) -{ - if (!nsCocoaUtils::IsMomentumScrollEvent(aEvent)) { - // Store the position so we can pin future momentum scroll events. - sLastScrollEventScreenLocation = nsCocoaUtils::ScreenLocationForEvent(aEvent); - } -} - -ChildView* -ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent) -{ - NSWindow* window = sWindowUnderMouse; - if (!window) - return nil; - - NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, window); - NSView* view = [[[window contentView] superview] hitTest:windowEventLocation]; - - if (![view isKindOfClass:[ChildView class]]) - return nil; - - ChildView* childView = (ChildView*)view; - // If childView is being destroyed return nil. - if (![childView widget]) - return nil; - return WindowAcceptsEvent(window, aEvent, childView) ? childView : nil; -} - -BOOL -ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent, - ChildView* aView, BOOL aIsClickThrough) -{ - // Right mouse down events may get through to all windows, even to a top level - // window with an open sheet. - if (!aWindow || [aEvent type] == NSRightMouseDown) - return YES; - - id delegate = [aWindow delegate]; - if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) - return YES; - - nsIWidget *windowWidget = [(WindowDelegate *)delegate geckoWidget]; - if (!windowWidget) - return YES; - - NSWindow* topLevelWindow = nil; - - switch (windowWidget->WindowType()) { - case eWindowType_popup: - // If this is a context menu, it won't have a parent. So we'll always - // accept mouse move events on context menus even when none of our windows - // is active, which is the right thing to do. - // For panels, the parent window is the XUL window that owns the panel. - return WindowAcceptsEvent([aWindow parentWindow], aEvent, aView, aIsClickThrough); - - case eWindowType_toplevel: - case eWindowType_dialog: - if ([aWindow attachedSheet]) - return NO; - - topLevelWindow = aWindow; - break; - case eWindowType_sheet: { - nsIWidget* parentWidget = windowWidget->GetSheetWindowParent(); - if (!parentWidget) - return YES; - - topLevelWindow = (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW); - break; - } - - default: - return YES; - } - - if (!topLevelWindow || - ([topLevelWindow isMainWindow] && !aIsClickThrough) || - [aEvent type] == NSOtherMouseDown || - (([aEvent modifierFlags] & NSCommandKeyMask) != 0 && - [aEvent type] != NSMouseMoved)) - return YES; - - // If we're here then we're dealing with a left click or mouse move on an - // inactive window or something similar. Ask Gecko what to do. - return [aView inactiveWindowAcceptsMouseEvent:aEvent]; -} - -#pragma mark - - -@interface NSView (MethodSwizzling) -- (BOOL)nsChildView_NSView_mouseDownCanMoveWindow; -@end - -@implementation NSView (MethodSwizzling) - -// All top-level browser windows belong to the ToolbarWindow class and have -// NSTexturedBackgroundWindowMask turned on in their "style" (see particularly -// [ToolbarWindow initWithContentRect:...] in nsCocoaWindow.mm). This style -// normally means the window "may be moved by clicking and dragging anywhere -// in the window background", but we've suppressed this by giving the -// ChildView class a mouseDownCanMoveWindow method that always returns NO. -// Normally a ToolbarWindow's contentView (not a ChildView) returns YES when -// NSTexturedBackgroundWindowMask is turned on. But normally this makes no -// difference. However, under some (probably very unusual) circumstances -// (and only on Leopard) it *does* make a difference -- for example it -// triggers bmo bugs 431902 and 476393. So here we make sure that a -// ToolbarWindow's contentView always returns NO from the -// mouseDownCanMoveWindow method. -- (BOOL)nsChildView_NSView_mouseDownCanMoveWindow -{ - NSWindow *ourWindow = [self window]; - NSView *contentView = [ourWindow contentView]; - if ([ourWindow isKindOfClass:[ToolbarWindow class]] && (self == contentView)) - return [ourWindow isMovableByWindowBackground]; - return [self nsChildView_NSView_mouseDownCanMoveWindow]; -} - -@end diff --git a/widget/cocoa/nsClipboard.h b/widget/cocoa/nsClipboard.h deleted file mode 100644 index 45871efe10..0000000000 --- a/widget/cocoa/nsClipboard.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -*- 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 nsClipboard_h_ -#define nsClipboard_h_ - -#include "nsIClipboard.h" -#include "nsXPIDLString.h" -#include "mozilla/StaticPtr.h" - -#import <Cocoa/Cocoa.h> - -class nsITransferable; - -class nsClipboard : public nsIClipboard -{ - -public: - nsClipboard(); - - NS_DECL_ISUPPORTS - NS_DECL_NSICLIPBOARD - - // On macOS, cache the transferable of the current selection (chrome/content) - // in the parent process. This is needed for the services menu which - // requires synchronous access to the current selection. - static mozilla::StaticRefPtr<nsITransferable> sSelectionCache; - - // Helper methods, used also by nsDragService - static NSDictionary* PasteboardDictFromTransferable(nsITransferable *aTransferable); - static bool IsStringType(const nsCString& aMIMEType, NSString** aPasteboardType); - static NSString* WrapHtmlForSystemPasteboard(NSString* aString); - static nsresult TransferableFromPasteboard(nsITransferable *aTransferable, NSPasteboard *pboard); - -protected: - - // impelement the native clipboard behavior - NS_IMETHOD SetNativeClipboardData(int32_t aWhichClipboard); - NS_IMETHOD GetNativeClipboardData(nsITransferable * aTransferable, int32_t aWhichClipboard); - void ClearSelectionCache(); - void SetSelectionCache(nsITransferable* aTransferable); - -private: - virtual ~nsClipboard(); - int32_t mCachedClipboard; - int32_t mChangeCount; // Set to the native change count after any modification of the clipboard. - - bool mIgnoreEmptyNotification; - nsCOMPtr<nsIClipboardOwner> mClipboardOwner; - nsCOMPtr<nsITransferable> mTransferable; -}; - -#endif // nsClipboard_h_ diff --git a/widget/cocoa/nsClipboard.mm b/widget/cocoa/nsClipboard.mm deleted file mode 100644 index 4146f17851..0000000000 --- a/widget/cocoa/nsClipboard.mm +++ /dev/null @@ -1,775 +0,0 @@ -/* -*- 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/. */ - -#include "mozilla/Logging.h" - -#include "mozilla/Unused.h" - -#include "gfxPlatform.h" -#include "nsArrayUtils.h" -#include "nsCOMPtr.h" -#include "nsClipboard.h" -#include "nsString.h" -#include "nsISupportsPrimitives.h" -#include "nsXPIDLString.h" -#include "nsPrimitiveHelpers.h" -#include "nsMemory.h" -#include "nsIFile.h" -#include "nsStringStream.h" -#include "nsDragService.h" -#include "nsEscape.h" -#include "nsPrintfCString.h" -#include "nsObjCExceptions.h" -#include "imgIContainer.h" -#include "nsCocoaUtils.h" - -using mozilla::gfx::DataSourceSurface; -using mozilla::gfx::SourceSurface; -using mozilla::LogLevel; - -// Screenshots use the (undocumented) png pasteboard type. -#define IMAGE_PASTEBOARD_TYPES NSTIFFPboardType, @"Apple PNG pasteboard type", nil - -extern PRLogModuleInfo* sCocoaLog; - -extern void EnsureLogInitialized(); - -mozilla::StaticRefPtr<nsITransferable> nsClipboard::sSelectionCache; - -nsClipboard::nsClipboard() - : mCachedClipboard(-1) - , mChangeCount(0) - , mIgnoreEmptyNotification(false) -{ - EnsureLogInitialized(); -} - -nsClipboard::~nsClipboard() -{ - EmptyClipboard(kGlobalClipboard); - EmptyClipboard(kFindClipboard); - ClearSelectionCache(); -} - -NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard) - -// We separate this into its own function because after an @try, all local -// variables within that function get marked as volatile, and our C++ type -// system doesn't like volatile things. -static NSData* -GetDataFromPasteboard(NSPasteboard* aPasteboard, NSString* aType) -{ - NSData *data = nil; - @try { - data = [aPasteboard dataForType:aType]; - } @catch (NSException* e) { - NS_WARNING(nsPrintfCString("Exception raised while getting data from the pasteboard: \"%s - %s\"", - [[e name] UTF8String], [[e reason] UTF8String]).get()); - mozilla::Unused << e; - } - return data; -} - -void -nsClipboard::SetSelectionCache(nsITransferable *aTransferable) -{ - sSelectionCache = aTransferable; -} - -void -nsClipboard::ClearSelectionCache() -{ - sSelectionCache = nullptr; -} - -NS_IMETHODIMP -nsClipboard::SetNativeClipboardData(int32_t aWhichClipboard) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if ((aWhichClipboard != kGlobalClipboard && aWhichClipboard != kFindClipboard) || !mTransferable) - return NS_ERROR_FAILURE; - - mIgnoreEmptyNotification = true; - - NSDictionary* pasteboardOutputDict = PasteboardDictFromTransferable(mTransferable); - if (!pasteboardOutputDict) - return NS_ERROR_FAILURE; - - unsigned int outputCount = [pasteboardOutputDict count]; - NSArray* outputKeys = [pasteboardOutputDict allKeys]; - NSPasteboard* cocoaPasteboard; - if (aWhichClipboard == kFindClipboard) { - cocoaPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard]; - [cocoaPasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; - } else { - // Write everything else out to the general pasteboard. - cocoaPasteboard = [NSPasteboard generalPasteboard]; - [cocoaPasteboard declareTypes:outputKeys owner:nil]; - } - - for (unsigned int i = 0; i < outputCount; i++) { - NSString* currentKey = [outputKeys objectAtIndex:i]; - id currentValue = [pasteboardOutputDict valueForKey:currentKey]; - if (aWhichClipboard == kFindClipboard) { - if (currentKey == NSStringPboardType) - [cocoaPasteboard setString:currentValue forType:currentKey]; - } else { - if (currentKey == NSStringPboardType || - currentKey == kCorePboardType_url || - currentKey == kCorePboardType_urld || - currentKey == kCorePboardType_urln) { - [cocoaPasteboard setString:currentValue forType:currentKey]; - } else if (currentKey == NSHTMLPboardType) { - [cocoaPasteboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue)) - forType:currentKey]; - } else { - [cocoaPasteboard setData:currentValue forType:currentKey]; - } - } - } - - mCachedClipboard = aWhichClipboard; - mChangeCount = [cocoaPasteboard changeCount]; - - mIgnoreEmptyNotification = false; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -nsresult -nsClipboard::TransferableFromPasteboard(nsITransferable *aTransferable, NSPasteboard *cocoaPasteboard) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // get flavor list that includes all acceptable flavors (including ones obtained through conversion) - nsCOMPtr<nsIArray> flavorList; - nsresult rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList)); - if (NS_FAILED(rv)) - return NS_ERROR_FAILURE; - - uint32_t flavorCount; - flavorList->GetLength(&flavorCount); - - for (uint32_t i = 0; i < flavorCount; i++) { - nsCOMPtr<nsISupportsCString> currentFlavor = do_QueryElementAt(flavorList, i); - if (!currentFlavor) - continue; - - nsXPIDLCString flavorStr; - currentFlavor->ToString(getter_Copies(flavorStr)); // i has a flavr - - // printf("looking for clipboard data of type %s\n", flavorStr.get()); - - NSString *pboardType = nil; - if (nsClipboard::IsStringType(flavorStr, &pboardType)) { - NSString* pString = [cocoaPasteboard stringForType:pboardType]; - if (!pString) - continue; - - NSData* stringData; - if ([pboardType isEqualToString:NSRTFPboardType]) { - stringData = [pString dataUsingEncoding:NSASCIIStringEncoding]; - } else { - stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding]; - } - unsigned int dataLength = [stringData length]; - void* clipboardDataPtr = malloc(dataLength); - if (!clipboardDataPtr) - return NS_ERROR_OUT_OF_MEMORY; - [stringData getBytes:clipboardDataPtr]; - - // The DOM only wants LF, so convert from MacOS line endings to DOM line endings. - int32_t signedDataLength = dataLength; - nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr, &clipboardDataPtr, &signedDataLength); - dataLength = signedDataLength; - - // skip BOM (Byte Order Mark to distinguish little or big endian) - char16_t* clipboardDataPtrNoBOM = (char16_t*)clipboardDataPtr; - if ((dataLength > 2) && - ((clipboardDataPtrNoBOM[0] == 0xFEFF) || - (clipboardDataPtrNoBOM[0] == 0xFFFE))) { - dataLength -= sizeof(char16_t); - clipboardDataPtrNoBOM += 1; - } - - nsCOMPtr<nsISupports> genericDataWrapper; - nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtrNoBOM, dataLength, - getter_AddRefs(genericDataWrapper)); - aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLength); - free(clipboardDataPtr); - break; - } - else if (flavorStr.EqualsLiteral(kCustomTypesMime)) { - NSString* type = [cocoaPasteboard availableTypeFromArray:[NSArray arrayWithObject:kCustomTypesPboardType]]; - if (!type) { - continue; - } - - NSData* pasteboardData = GetDataFromPasteboard(cocoaPasteboard, type); - if (!pasteboardData) { - continue; - } - - unsigned int dataLength = [pasteboardData length]; - void* clipboardDataPtr = malloc(dataLength); - if (!clipboardDataPtr) { - return NS_ERROR_OUT_OF_MEMORY; - } - [pasteboardData getBytes:clipboardDataPtr]; - - nsCOMPtr<nsISupports> genericDataWrapper; - nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtr, dataLength, - getter_AddRefs(genericDataWrapper)); - - aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLength); - free(clipboardDataPtr); - } - else if (flavorStr.EqualsLiteral(kJPEGImageMime) || - flavorStr.EqualsLiteral(kJPGImageMime) || - flavorStr.EqualsLiteral(kPNGImageMime) || - flavorStr.EqualsLiteral(kGIFImageMime)) { - // Figure out if there's data on the pasteboard we can grab (sanity check) - NSString *type = [cocoaPasteboard availableTypeFromArray:[NSArray arrayWithObjects:IMAGE_PASTEBOARD_TYPES]]; - if (!type) - continue; - - // Read data off the clipboard - NSData *pasteboardData = GetDataFromPasteboard(cocoaPasteboard, type); - if (!pasteboardData) - continue; - - // Figure out what type we're converting to - CFStringRef outputType = NULL; - if (flavorStr.EqualsLiteral(kJPEGImageMime) || - flavorStr.EqualsLiteral(kJPGImageMime)) - outputType = CFSTR("public.jpeg"); - else if (flavorStr.EqualsLiteral(kPNGImageMime)) - outputType = CFSTR("public.png"); - else if (flavorStr.EqualsLiteral(kGIFImageMime)) - outputType = CFSTR("com.compuserve.gif"); - else - continue; - - // Use ImageIO to interpret the data on the clipboard and transcode. - // Note that ImageIO, like all CF APIs, allows NULLs to propagate freely - // and safely in most cases (like ObjC). A notable exception is CFRelease. - NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: - (NSNumber*)kCFBooleanTrue, kCGImageSourceShouldAllowFloat, - (type == NSTIFFPboardType ? @"public.tiff" : @"public.png"), - kCGImageSourceTypeIdentifierHint, nil]; - - CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)pasteboardData, - (CFDictionaryRef)options); - NSMutableData *encodedData = [NSMutableData data]; - CGImageDestinationRef dest = CGImageDestinationCreateWithData((CFMutableDataRef)encodedData, - outputType, - 1, NULL); - CGImageDestinationAddImageFromSource(dest, source, 0, NULL); - bool successfullyConverted = CGImageDestinationFinalize(dest); - - if (successfullyConverted) { - // Put the converted data in a form Gecko can understand - nsCOMPtr<nsIInputStream> byteStream; - NS_NewByteInputStream(getter_AddRefs(byteStream), (const char*)[encodedData bytes], - [encodedData length], NS_ASSIGNMENT_COPY); - - aTransferable->SetTransferData(flavorStr, byteStream, sizeof(nsIInputStream*)); - } - - if (dest) - CFRelease(dest); - if (source) - CFRelease(source); - - if (successfullyConverted) - break; - else - continue; - } - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, int32_t aWhichClipboard) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if ((aWhichClipboard != kGlobalClipboard && aWhichClipboard != kFindClipboard) || !aTransferable) - return NS_ERROR_FAILURE; - - NSPasteboard* cocoaPasteboard; - if (aWhichClipboard == kFindClipboard) { - cocoaPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard]; - } else { - cocoaPasteboard = [NSPasteboard generalPasteboard]; - } - if (!cocoaPasteboard) - return NS_ERROR_FAILURE; - - // get flavor list that includes all acceptable flavors (including ones obtained through conversion) - nsCOMPtr<nsIArray> flavorList; - nsresult rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList)); - if (NS_FAILED(rv)) - return NS_ERROR_FAILURE; - - uint32_t flavorCount; - flavorList->GetLength(&flavorCount); - - // If we were the last ones to put something on the pasteboard, then just use the cached - // transferable. Otherwise clear it because it isn't relevant any more. - if (mCachedClipboard == aWhichClipboard && - mChangeCount == [cocoaPasteboard changeCount]) { - if (mTransferable) { - for (uint32_t i = 0; i < flavorCount; i++) { - nsCOMPtr<nsISupportsCString> currentFlavor = do_QueryElementAt(flavorList, i); - if (!currentFlavor) - continue; - - nsXPIDLCString flavorStr; - currentFlavor->ToString(getter_Copies(flavorStr)); - - nsCOMPtr<nsISupports> dataSupports; - uint32_t dataSize = 0; - rv = mTransferable->GetTransferData(flavorStr, getter_AddRefs(dataSupports), &dataSize); - if (NS_SUCCEEDED(rv)) { - aTransferable->SetTransferData(flavorStr, dataSupports, dataSize); - return NS_OK; // maybe try to fill in more types? Is there a point? - } - } - } - } else { - EmptyClipboard(aWhichClipboard); - } - - // at this point we can't satisfy the request from cache data so let's look - // for things other people put on the system clipboard - - return nsClipboard::TransferableFromPasteboard(aTransferable, cocoaPasteboard); - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// returns true if we have *any* of the passed in flavors available for pasting -NS_IMETHODIMP -nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, uint32_t aLength, - int32_t aWhichClipboard, bool* outResult) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - *outResult = false; - - if ((aWhichClipboard != kGlobalClipboard) || !aFlavorList) - return NS_OK; - - // first see if we have data for this in our cached transferable - if (mTransferable) { - nsCOMPtr<nsIArray> transferableFlavorList; - nsresult rv = mTransferable->FlavorsTransferableCanImport(getter_AddRefs(transferableFlavorList)); - if (NS_SUCCEEDED(rv)) { - uint32_t transferableFlavorCount; - transferableFlavorList->GetLength(&transferableFlavorCount); - for (uint32_t j = 0; j < transferableFlavorCount; j++) { - nsCOMPtr<nsISupportsCString> currentTransferableFlavor = - do_QueryElementAt(transferableFlavorList, j); - if (!currentTransferableFlavor) - continue; - nsXPIDLCString transferableFlavorStr; - currentTransferableFlavor->ToString(getter_Copies(transferableFlavorStr)); - - for (uint32_t k = 0; k < aLength; k++) { - if (transferableFlavorStr.Equals(aFlavorList[k])) { - *outResult = true; - return NS_OK; - } - } - } - } - } - - NSPasteboard* generalPBoard = [NSPasteboard generalPasteboard]; - - for (uint32_t i = 0; i < aLength; i++) { - nsDependentCString mimeType(aFlavorList[i]); - NSString *pboardType = nil; - - if (nsClipboard::IsStringType(mimeType, &pboardType)) { - NSString* availableType = [generalPBoard availableTypeFromArray:[NSArray arrayWithObject:pboardType]]; - if (availableType && [availableType isEqualToString:pboardType]) { - *outResult = true; - break; - } - } else if (!strcmp(aFlavorList[i], kCustomTypesMime)) { - NSString* availableType = [generalPBoard availableTypeFromArray:[NSArray arrayWithObject:kCustomTypesPboardType]]; - if (availableType) { - *outResult = true; - break; - } - } else if (!strcmp(aFlavorList[i], kJPEGImageMime) || - !strcmp(aFlavorList[i], kJPGImageMime) || - !strcmp(aFlavorList[i], kPNGImageMime) || - !strcmp(aFlavorList[i], kGIFImageMime)) { - NSString* availableType = [generalPBoard availableTypeFromArray: - [NSArray arrayWithObjects:IMAGE_PASTEBOARD_TYPES]]; - if (availableType) { - *outResult = true; - break; - } - } - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsClipboard::SupportsFindClipboard(bool *_retval) -{ - NS_ENSURE_ARG_POINTER(_retval); - *_retval = true; - return NS_OK; -} - -// This function converts anything that other applications might understand into the system format -// and puts it into a dictionary which it returns. -// static -NSDictionary* -nsClipboard::PasteboardDictFromTransferable(nsITransferable* aTransferable) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - if (!aTransferable) - return nil; - - NSMutableDictionary* pasteboardOutputDict = [NSMutableDictionary dictionary]; - - nsCOMPtr<nsIArray> flavorList; - nsresult rv = aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList)); - if (NS_FAILED(rv)) - return nil; - - uint32_t flavorCount; - flavorList->GetLength(&flavorCount); - for (uint32_t i = 0; i < flavorCount; i++) { - nsCOMPtr<nsISupportsCString> currentFlavor = do_QueryElementAt(flavorList, i); - if (!currentFlavor) - continue; - - nsXPIDLCString flavorStr; - currentFlavor->ToString(getter_Copies(flavorStr)); - - MOZ_LOG(sCocoaLog, LogLevel::Info, ("writing out clipboard data of type %s (%d)\n", flavorStr.get(), i)); - - NSString *pboardType = nil; - - if (nsClipboard::IsStringType(flavorStr, &pboardType)) { - void* data = nullptr; - uint32_t dataSize = 0; - nsCOMPtr<nsISupports> genericDataWrapper; - rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericDataWrapper), &dataSize); - nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr, genericDataWrapper, &data, dataSize); - - NSString* nativeString; - if (data) - nativeString = [NSString stringWithCharacters:(const unichar*)data length:(dataSize / sizeof(char16_t))]; - else - nativeString = [NSString string]; - - // be nice to Carbon apps, normalize the receiver's contents using Form C. - nativeString = [nativeString precomposedStringWithCanonicalMapping]; - - [pasteboardOutputDict setObject:nativeString forKey:pboardType]; - - free(data); - } - else if (flavorStr.EqualsLiteral(kCustomTypesMime)) { - void* data = nullptr; - uint32_t dataSize = 0; - nsCOMPtr<nsISupports> genericDataWrapper; - rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericDataWrapper), &dataSize); - nsPrimitiveHelpers::CreateDataFromPrimitive(flavorStr, genericDataWrapper, &data, dataSize); - - if (data) { - NSData* nativeData = [NSData dataWithBytes:data length:dataSize]; - - [pasteboardOutputDict setObject:nativeData forKey:kCustomTypesPboardType]; - free(data); - } - } - else if (flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) || - flavorStr.EqualsLiteral(kJPGImageMime) || flavorStr.EqualsLiteral(kGIFImageMime) || - flavorStr.EqualsLiteral(kNativeImageMime)) { - uint32_t dataSize = 0; - nsCOMPtr<nsISupports> transferSupports; - aTransferable->GetTransferData(flavorStr, getter_AddRefs(transferSupports), &dataSize); - nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive(do_QueryInterface(transferSupports)); - if (!ptrPrimitive) - continue; - - nsCOMPtr<nsISupports> primitiveData; - ptrPrimitive->GetData(getter_AddRefs(primitiveData)); - - nsCOMPtr<imgIContainer> image(do_QueryInterface(primitiveData)); - if (!image) { - NS_WARNING("Image isn't an imgIContainer in transferable"); - continue; - } - - RefPtr<SourceSurface> surface = - image->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_SYNC_DECODE); - if (!surface) { - continue; - } - CGImageRef imageRef = NULL; - rv = nsCocoaUtils::CreateCGImageFromSurface(surface, &imageRef); - if (NS_FAILED(rv) || !imageRef) { - continue; - } - - // Convert the CGImageRef to TIFF data. - CFMutableDataRef tiffData = CFDataCreateMutable(kCFAllocatorDefault, 0); - CGImageDestinationRef destRef = CGImageDestinationCreateWithData(tiffData, - CFSTR("public.tiff"), - 1, - NULL); - CGImageDestinationAddImage(destRef, imageRef, NULL); - bool successfullyConverted = CGImageDestinationFinalize(destRef); - - CGImageRelease(imageRef); - if (destRef) - CFRelease(destRef); - - if (!successfullyConverted) { - if (tiffData) - CFRelease(tiffData); - continue; - } - - [pasteboardOutputDict setObject:(NSMutableData*)tiffData forKey:NSTIFFPboardType]; - if (tiffData) - CFRelease(tiffData); - } - else if (flavorStr.EqualsLiteral(kFileMime)) { - uint32_t len = 0; - nsCOMPtr<nsISupports> genericFile; - rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericFile), &len); - if (NS_FAILED(rv)) { - continue; - } - - nsCOMPtr<nsIFile> file(do_QueryInterface(genericFile)); - if (!file) { - nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericFile)); - - if (ptr) { - ptr->GetData(getter_AddRefs(genericFile)); - file = do_QueryInterface(genericFile); - } - } - - if (!file) { - continue; - } - - nsAutoString fileURI; - rv = file->GetPath(fileURI); - if (NS_FAILED(rv)) { - continue; - } - - NSString* str = nsCocoaUtils::ToNSString(fileURI); - NSArray* fileList = [NSArray arrayWithObjects:str, nil]; - [pasteboardOutputDict setObject:fileList forKey:NSFilenamesPboardType]; - } - else if (flavorStr.EqualsLiteral(kFilePromiseMime)) { - [pasteboardOutputDict setObject:[NSArray arrayWithObject:@""] forKey:NSFilesPromisePboardType]; - } - else if (flavorStr.EqualsLiteral(kURLMime)) { - uint32_t len = 0; - nsCOMPtr<nsISupports> genericURL; - rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericURL), &len); - nsCOMPtr<nsISupportsString> urlObject(do_QueryInterface(genericURL)); - - nsAutoString url; - urlObject->GetData(url); - - // A newline embedded in the URL means that the form is actually URL + title. - int32_t newlinePos = url.FindChar(char16_t('\n')); - if (newlinePos >= 0) { - url.Truncate(newlinePos); - - nsAutoString urlTitle; - urlObject->GetData(urlTitle); - urlTitle.Mid(urlTitle, newlinePos + 1, len - (newlinePos + 1)); - - NSString *nativeTitle = [[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(urlTitle.get()) - length:urlTitle.Length()]; - // be nice to Carbon apps, normalize the receiver's contents using Form C. - [pasteboardOutputDict setObject:[nativeTitle precomposedStringWithCanonicalMapping] forKey:kCorePboardType_urln]; - // Also put the title out as 'urld', since some recipients will look for that. - [pasteboardOutputDict setObject:[nativeTitle precomposedStringWithCanonicalMapping] forKey:kCorePboardType_urld]; - [nativeTitle release]; - } - - // The Finder doesn't like getting random binary data aka - // Unicode, so change it into an escaped URL containing only - // ASCII. - nsAutoCString utf8Data = NS_ConvertUTF16toUTF8(url.get(), url.Length()); - nsAutoCString escData; - NS_EscapeURL(utf8Data.get(), utf8Data.Length(), esc_OnlyNonASCII|esc_AlwaysCopy, escData); - - // printf("Escaped url is %s, length %d\n", escData.get(), escData.Length()); - - NSString *nativeURL = [NSString stringWithUTF8String:escData.get()]; - [pasteboardOutputDict setObject:nativeURL forKey:kCorePboardType_url]; - } - // If it wasn't a type that we recognize as exportable we don't put it on the system - // clipboard. We'll just access it from our cached transferable when we need it. - } - - return pasteboardOutputDict; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -bool nsClipboard::IsStringType(const nsCString& aMIMEType, NSString** aPasteboardType) -{ - if (aMIMEType.EqualsLiteral(kUnicodeMime)) { - *aPasteboardType = NSStringPboardType; - return true; - } else if (aMIMEType.EqualsLiteral(kRTFMime)) { - *aPasteboardType = NSRTFPboardType; - return true; - } else if (aMIMEType.EqualsLiteral(kHTMLMime)) { - *aPasteboardType = NSHTMLPboardType; - return true; - } else { - return false; - } -} - -NSString* nsClipboard::WrapHtmlForSystemPasteboard(NSString* aString) -{ - NSString* wrapped = - [NSString stringWithFormat: - @"<html>" - "<head>" - "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">" - "</head>" - "<body>" - "%@" - "</body>" - "</html>", aString]; - return wrapped; -} - -/** - * Sets the transferable object - * - */ -NS_IMETHODIMP -nsClipboard::SetData(nsITransferable* aTransferable, nsIClipboardOwner* anOwner, - int32_t aWhichClipboard) -{ - NS_ASSERTION (aTransferable, "clipboard given a null transferable"); - - if (aWhichClipboard == kSelectionCache) { - if (aTransferable) { - SetSelectionCache(aTransferable); - return NS_OK; - } - return NS_ERROR_FAILURE; - } - - if (aTransferable == mTransferable && anOwner == mClipboardOwner) { - return NS_OK; - } - bool selectClipPresent; - SupportsSelectionClipboard(&selectClipPresent); - bool findClipPresent; - SupportsFindClipboard(&findClipPresent); - if (!selectClipPresent && !findClipPresent && aWhichClipboard != kGlobalClipboard) { - return NS_ERROR_FAILURE; - } - - EmptyClipboard(aWhichClipboard); - - mClipboardOwner = anOwner; - mTransferable = aTransferable; - - nsresult rv = NS_ERROR_FAILURE; - if (mTransferable) { - rv = SetNativeClipboardData(aWhichClipboard); - } - - return rv; -} - -/** - * Gets the transferable object - * - */ -NS_IMETHODIMP -nsClipboard::GetData(nsITransferable* aTransferable, int32_t aWhichClipboard) -{ - NS_ASSERTION (aTransferable, "clipboard given a null transferable"); - - bool selectClipPresent; - SupportsSelectionClipboard(&selectClipPresent); - bool findClipPresent; - SupportsFindClipboard(&findClipPresent); - if (!selectClipPresent && !findClipPresent && aWhichClipboard != kGlobalClipboard) - return NS_ERROR_FAILURE; - - if (aTransferable) { - return GetNativeClipboardData(aTransferable, aWhichClipboard); - } - - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -nsClipboard::EmptyClipboard(int32_t aWhichClipboard) -{ - if (aWhichClipboard == kSelectionCache) { - ClearSelectionCache(); - return NS_OK; - } - - bool selectClipPresent; - SupportsSelectionClipboard(&selectClipPresent); - bool findClipPresent; - SupportsFindClipboard(&findClipPresent); - if (!selectClipPresent && !findClipPresent && aWhichClipboard != kGlobalClipboard) { - return NS_ERROR_FAILURE; - } - - if (mIgnoreEmptyNotification) { - return NS_OK; - } - - if (mClipboardOwner) { - mClipboardOwner->LosingOwnership(mTransferable); - mClipboardOwner = nullptr; - } - - mTransferable = nullptr; - return NS_OK; -} - -NS_IMETHODIMP -nsClipboard::SupportsSelectionClipboard(bool* _retval) -{ - *_retval = false; // we don't support the selection clipboard by default. - return NS_OK; -} diff --git a/widget/cocoa/nsCocoaDebugUtils.h b/widget/cocoa/nsCocoaDebugUtils.h deleted file mode 100644 index 814f060878..0000000000 --- a/widget/cocoa/nsCocoaDebugUtils.h +++ /dev/null @@ -1,136 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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 nsCocoaDebugUtils_h_ -#define nsCocoaDebugUtils_h_ - -#include <CoreServices/CoreServices.h> - -// Definitions and declarations of stuff used by us from the CoreSymbolication -// framework. This is an undocumented, private framework available on OS X -// 10.6 and up. It's used by Apple utilities like dtrace, atos, ReportCrash -// and crashreporterd. - -typedef struct _CSTypeRef { - unsigned long type; - void* contents; -} CSTypeRef; - -typedef CSTypeRef CSSymbolicatorRef; -typedef CSTypeRef CSSymbolOwnerRef; -typedef CSTypeRef CSSymbolRef; -typedef CSTypeRef CSSourceInfoRef; - -typedef struct _CSRange { - unsigned long long location; - unsigned long long length; -} CSRange; - -typedef unsigned long long CSArchitecture; - -#define kCSNow LONG_MAX - -extern "C" { - -CSSymbolicatorRef -CSSymbolicatorCreateWithPid(pid_t pid); - -CSSymbolicatorRef -CSSymbolicatorCreateWithPidFlagsAndNotification(pid_t pid, - uint32_t flags, - uint32_t notification); - -CSArchitecture -CSSymbolicatorGetArchitecture(CSSymbolicatorRef symbolicator); - -CSSymbolOwnerRef -CSSymbolicatorGetSymbolOwnerWithAddressAtTime(CSSymbolicatorRef symbolicator, - unsigned long long address, - long time); - -const char* -CSSymbolOwnerGetName(CSSymbolOwnerRef owner); - -unsigned long long -CSSymbolOwnerGetBaseAddress(CSSymbolOwnerRef owner); - -CSSymbolRef -CSSymbolOwnerGetSymbolWithAddress(CSSymbolOwnerRef owner, - unsigned long long address); - -CSSourceInfoRef -CSSymbolOwnerGetSourceInfoWithAddress(CSSymbolOwnerRef owner, - unsigned long long address); - -const char* -CSSymbolGetName(CSSymbolRef symbol); - -CSRange -CSSymbolGetRange(CSSymbolRef symbol); - -const char* -CSSourceInfoGetFilename(CSSourceInfoRef info); - -uint32_t -CSSourceInfoGetLineNumber(CSSourceInfoRef info); - -CSTypeRef -CSRetain(CSTypeRef); - -void -CSRelease(CSTypeRef); - -bool -CSIsNull(CSTypeRef); - -void -CSShow(CSTypeRef); - -const char* -CSArchitectureGetFamilyName(CSArchitecture); - -} // extern "C" - -class nsCocoaDebugUtils -{ -public: - // Like NSLog() but records more information (for example the full path to - // the executable and the "thread name"). Like NSLog(), writes to both - // stdout and the system log. - static void DebugLog(const char* aFormat, ...); - - // Logs a stack trace of the current point of execution, to both stdout and - // the system log. - static void PrintStackTrace(); - - // Returns the name of the module that "owns" aAddress. This must be - // free()ed by the caller. - static char* GetOwnerName(void* aAddress); - - // Returns a symbolicated representation of aAddress. This must be - // free()ed by the caller. - static char* GetAddressString(void* aAddress); - -private: - static void DebugLogInt(bool aDecorate, const char* aFormat, ...); - static void DebugLogV(bool aDecorate, CFStringRef aFormat, va_list aArgs); - - static void PrintAddress(void* aAddress); - - // The values returned by GetOwnerNameInt() and GetAddressStringInt() must - // be free()ed by the caller. - static char* GetOwnerNameInt(void* aAddress, - CSTypeRef aOwner = sInitializer); - static char* GetAddressStringInt(void* aAddress, - CSTypeRef aOwner = sInitializer); - - static CSSymbolicatorRef GetSymbolicatorRef(); - static void ReleaseSymbolicator(); - - static CSTypeRef sInitializer; - static CSSymbolicatorRef sSymbolicator; -}; - -#endif // nsCocoaDebugUtils_h_ diff --git a/widget/cocoa/nsCocoaDebugUtils.mm b/widget/cocoa/nsCocoaDebugUtils.mm deleted file mode 100644 index 35896dc401..0000000000 --- a/widget/cocoa/nsCocoaDebugUtils.mm +++ /dev/null @@ -1,284 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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/. */ - -#include "nsCocoaDebugUtils.h" - -#include <pthread.h> -#include <libproc.h> -#include <stdarg.h> -#include <time.h> -#include <execinfo.h> -#include <asl.h> - -static char gProcPath[PROC_PIDPATHINFO_MAXSIZE] = {0}; -static char gBundleID[MAXPATHLEN] = {0}; - -static void MaybeGetPathAndID() -{ - if (!gProcPath[0]) { - proc_pidpath(getpid(), gProcPath, sizeof(gProcPath)); - } - if (!gBundleID[0]) { - // Apple's CFLog() uses "com.apple.console" (in its call to asl_open()) if - // it can't find the bundle id. - CFStringRef bundleID = NULL; - CFBundleRef mainBundle = CFBundleGetMainBundle(); - if (mainBundle) { - bundleID = CFBundleGetIdentifier(mainBundle); - } - if (!bundleID) { - strcpy(gBundleID, "com.apple.console"); - } else { - CFStringGetCString(bundleID, gBundleID, sizeof(gBundleID), - kCFStringEncodingUTF8); - } - } -} - -static void GetThreadName(char* aName, size_t aSize) -{ - pthread_getname_np(pthread_self(), aName, aSize); -} - -void -nsCocoaDebugUtils::DebugLog(const char* aFormat, ...) -{ - va_list args; - va_start(args, aFormat); - CFStringRef formatCFSTR = - CFStringCreateWithCString(kCFAllocatorDefault, aFormat, - kCFStringEncodingUTF8); - DebugLogV(true, formatCFSTR, args); - CFRelease(formatCFSTR); - va_end(args); -} - -void -nsCocoaDebugUtils::DebugLogInt(bool aDecorate, const char* aFormat, ...) -{ - va_list args; - va_start(args, aFormat); - CFStringRef formatCFSTR = - CFStringCreateWithCString(kCFAllocatorDefault, aFormat, - kCFStringEncodingUTF8); - DebugLogV(aDecorate, formatCFSTR, args); - CFRelease(formatCFSTR); - va_end(args); -} - -void -nsCocoaDebugUtils::DebugLogV(bool aDecorate, CFStringRef aFormat, - va_list aArgs) -{ - MaybeGetPathAndID(); - - CFStringRef message = - CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, - aFormat, aArgs); - - int msgLength = - CFStringGetMaximumSizeForEncoding(CFStringGetLength(message), - kCFStringEncodingUTF8); - char* msgUTF8 = (char*) calloc(msgLength, 1); - CFStringGetCString(message, msgUTF8, msgLength, kCFStringEncodingUTF8); - CFRelease(message); - - int finishedLength = msgLength + PROC_PIDPATHINFO_MAXSIZE; - char* finished = (char*) calloc(finishedLength, 1); - const time_t currentTime = time(NULL); - char timestamp[30] = {0}; - ctime_r(¤tTime, timestamp); - if (aDecorate) { - char threadName[MAXPATHLEN] = {0}; - GetThreadName(threadName, sizeof(threadName)); - snprintf(finished, finishedLength, "(%s) %s[%u] %s[%p] %s\n", - timestamp, gProcPath, getpid(), threadName, pthread_self(), msgUTF8); - } else { - snprintf(finished, finishedLength, "%s\n", msgUTF8); - } - free(msgUTF8); - - fputs(finished, stdout); - - // Use the Apple System Log facility, as NSLog and CFLog do. - aslclient asl = asl_open(NULL, gBundleID, ASL_OPT_NO_DELAY); - aslmsg msg = asl_new(ASL_TYPE_MSG); - asl_set(msg, ASL_KEY_LEVEL, "4"); // kCFLogLevelWarning, used by NSLog() - asl_set(msg, ASL_KEY_MSG, finished); - asl_send(asl, msg); - asl_free(msg); - asl_close(asl); - - free(finished); -} - -CSTypeRef -nsCocoaDebugUtils::sInitializer = {0}; - -CSSymbolicatorRef -nsCocoaDebugUtils::sSymbolicator = {0}; - -#define STACK_MAX 256 - -void -nsCocoaDebugUtils::PrintStackTrace() -{ - void** addresses = (void**) calloc(STACK_MAX, sizeof(void*)); - if (!addresses) { - return; - } - - CSSymbolicatorRef symbolicator = GetSymbolicatorRef(); - if (CSIsNull(symbolicator)) { - free(addresses); - return; - } - - uint32_t count = backtrace(addresses, STACK_MAX); - for (uint32_t i = 0; i < count; ++i) { - PrintAddress(addresses[i]); - } - - ReleaseSymbolicator(); - free(addresses); -} - -void -nsCocoaDebugUtils::PrintAddress(void* aAddress) -{ - const char* ownerName = "unknown"; - const char* addressString = "unknown + 0"; - - char* allocatedOwnerName = nullptr; - char* allocatedAddressString = nullptr; - - CSSymbolOwnerRef owner = {0}; - CSSymbolicatorRef symbolicator = GetSymbolicatorRef(); - - if (!CSIsNull(symbolicator)) { - owner = - CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, - (unsigned long long) aAddress, - kCSNow); - } - if (!CSIsNull(owner)) { - ownerName = allocatedOwnerName = GetOwnerNameInt(aAddress, owner); - addressString = allocatedAddressString = GetAddressStringInt(aAddress, owner); - } - DebugLogInt(false, " (%s) %s", ownerName, addressString); - - free(allocatedOwnerName); - free(allocatedAddressString); - - ReleaseSymbolicator(); -} - -char* -nsCocoaDebugUtils::GetOwnerName(void* aAddress) -{ - return GetOwnerNameInt(aAddress); -} - -char* -nsCocoaDebugUtils::GetOwnerNameInt(void* aAddress, CSTypeRef aOwner) -{ - char* retval = (char*) calloc(MAXPATHLEN, 1); - - const char* ownerName = "unknown"; - - CSSymbolicatorRef symbolicator = GetSymbolicatorRef(); - CSTypeRef owner = aOwner; - - if (CSIsNull(owner) && !CSIsNull(symbolicator)) { - owner = - CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, - (unsigned long long) aAddress, - kCSNow); - } - - if (!CSIsNull(owner)) { - ownerName = CSSymbolOwnerGetName(owner); - } - - snprintf(retval, MAXPATHLEN, "%s", ownerName); - ReleaseSymbolicator(); - - return retval; -} - -char* -nsCocoaDebugUtils::GetAddressString(void* aAddress) -{ - return GetAddressStringInt(aAddress); -} - -char* -nsCocoaDebugUtils::GetAddressStringInt(void* aAddress, CSTypeRef aOwner) -{ - char* retval = (char*) calloc(MAXPATHLEN, 1); - - const char* addressName = "unknown"; - unsigned long long addressOffset = 0; - - CSSymbolicatorRef symbolicator = GetSymbolicatorRef(); - CSTypeRef owner = aOwner; - - if (CSIsNull(owner) && !CSIsNull(symbolicator)) { - owner = - CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, - (unsigned long long) aAddress, - kCSNow); - } - - if (!CSIsNull(owner)) { - CSSymbolRef symbol = - CSSymbolOwnerGetSymbolWithAddress(owner, - (unsigned long long) aAddress); - if (!CSIsNull(symbol)) { - addressName = CSSymbolGetName(symbol); - CSRange range = CSSymbolGetRange(symbol); - addressOffset = (unsigned long long) aAddress - range.location; - } else { - addressOffset = (unsigned long long) - aAddress - CSSymbolOwnerGetBaseAddress(owner); - } - } - - snprintf(retval, MAXPATHLEN, "%s + 0x%llx", - addressName, addressOffset); - ReleaseSymbolicator(); - - return retval; -} - -CSSymbolicatorRef -nsCocoaDebugUtils::GetSymbolicatorRef() -{ - if (CSIsNull(sSymbolicator)) { - // 0x40e0000 is the value returned by - // uint32_t CSSymbolicatorGetFlagsForNListOnlyData(void). We don't use - // this method directly because it doesn't exist on OS X 10.6. Unless - // we limit ourselves to NList data, it will take too long to get a - // stack trace where Dwarf debugging info is available (about 15 seconds - // with Firefox). This means we won't be able to get a CSSourceInfoRef, - // or line number information. Oh well. - sSymbolicator = - CSSymbolicatorCreateWithPidFlagsAndNotification(getpid(), - 0x40e0000, 0); - } - // Retaining just after creation prevents crashes when calling symbolicator - // code (for example from PrintStackTrace()) as Firefox is quitting. Not - // sure why. Doing this may mean that we leak sSymbolicator on quitting - // (if we ever created it). No particular harm in that, though. - return CSRetain(sSymbolicator); -} - -void -nsCocoaDebugUtils::ReleaseSymbolicator() -{ - if (!CSIsNull(sSymbolicator)) { - CSRelease(sSymbolicator); - } -} diff --git a/widget/cocoa/nsCocoaFeatures.h b/widget/cocoa/nsCocoaFeatures.h deleted file mode 100644 index a9cab95d56..0000000000 --- a/widget/cocoa/nsCocoaFeatures.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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 nsCocoaFeatures_h_ -#define nsCocoaFeatures_h_ - -#include <stdint.h> - -/// Note that this class assumes we support the platform we are running on. -/// For better or worse, if the version is unknown or less than what we -/// support, we set it to the minimum supported version. GetSystemVersion -/// is the only call that returns the unadjusted values. -class nsCocoaFeatures { -public: - static int32_t macOSVersion(); - static int32_t macOSVersionMajor(); - static int32_t macOSVersionMinor(); - static int32_t macOSVersionBugFix(); - static bool OnYosemiteOrLater(); - static bool OnElCapitanOrLater(); - static bool OnSierraOrLater(); - static bool OnHighSierraOrLater(); - static bool OnMojaveOrLater(); - static bool OnCatalinaOrLater(); - static bool OnBigSurOrLater(); - - static bool IsAtLeastVersion(int32_t aMajor, int32_t aMinor, int32_t aBugFix=0); - - // These are utilities that do not change or depend on the value of mOSXVersion - // and instead just encapsulate the encoding algorithm. Note that GetVersion - // actually adjusts to the lowest supported OS, so it will always return - // a "supported" version. GetSystemVersion does not make any modifications. - static void GetSystemVersion(int &aMajor, int &aMinor, int &aBugFix); - static int32_t GetVersion(int32_t aMajor, int32_t aMinor, int32_t aBugFix); - static int32_t ExtractMajorVersion(int32_t aVersion); - static int32_t ExtractMinorVersion(int32_t aVersion); - static int32_t ExtractBugFixVersion(int32_t aVersion); - -private: - static void InitializeVersionNumbers(); - - static int32_t mOSVersion; -}; -#endif // nsCocoaFeatures_h_ diff --git a/widget/cocoa/nsCocoaFeatures.mm b/widget/cocoa/nsCocoaFeatures.mm deleted file mode 100644 index e0fafb7d96..0000000000 --- a/widget/cocoa/nsCocoaFeatures.mm +++ /dev/null @@ -1,209 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * 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/. */ - -// This file makes some assumptions about the versions of macOS. -// We are assuming that the major, minor and bugfix versions are each less than -// 256. -// There are MOZ_ASSERTs for that. - -// The formula for the version integer is (major << 16) + (minor << 8) + bugfix. - -#define MACOS_VERSION_MASK 0x00FFFFFF -#define MACOS_MAJOR_VERSION_MASK 0x00FFFFFF -#define MACOS_MINOR_VERSION_MASK 0x00FFFFFF -#define MACOS_BUGFIX_VERSION_MASK 0x00FFFFFF -#define MACOS_VERSION_10_0_HEX 0x000A0000 -#define MACOS_VERSION_10_7_HEX 0x000A0700 -#define MACOS_VERSION_10_8_HEX 0x000A0800 -#define MACOS_VERSION_10_9_HEX 0x000A0900 -#define MACOS_VERSION_10_10_HEX 0x000A0A00 -#define MACOS_VERSION_10_11_HEX 0x000A0B00 -#define MACOS_VERSION_10_12_HEX 0x000A0C00 -#define MACOS_VERSION_10_13_HEX 0x000A0D00 -#define MACOS_VERSION_10_14_HEX 0x000A0E00 -#define MACOS_VERSION_10_15_HEX 0x000A0F00 -#define MACOS_VERSION_10_16_HEX 0x000A1000 -#define MACOS_VERSION_11_0_HEX 0x000B0000 - -#include "nsCocoaFeatures.h" -#include "nsCocoaUtils.h" -#include "nsDebug.h" -#include "nsObjCExceptions.h" - -#import <Cocoa/Cocoa.h> - -int32_t nsCocoaFeatures::mOSVersion = 0; - -// This should not be called with unchecked aMajor, which should be >= 10. -inline int32_t AssembleVersion(int32_t aMajor, int32_t aMinor, int32_t aBugFix) -{ - MOZ_ASSERT(aMajor >= 10); - return (aMajor << 16) + (aMinor << 8) + aBugFix; -} - -int32_t nsCocoaFeatures::ExtractMajorVersion(int32_t aVersion) -{ - MOZ_ASSERT((aVersion & MACOS_VERSION_MASK) == aVersion); - return (aVersion & 0xFF0000) >> 16; -} - -int32_t nsCocoaFeatures::ExtractMinorVersion(int32_t aVersion) -{ - MOZ_ASSERT((aVersion & MACOS_VERSION_MASK) == aVersion); - return (aVersion & 0xFF00) >> 8; -} - -int32_t nsCocoaFeatures::ExtractBugFixVersion(int32_t aVersion) -{ - MOZ_ASSERT((aVersion & MACOS_VERSION_MASK) == aVersion); - return aVersion & 0xFF; -} - -static int intAtStringIndex(NSArray *array, int index) -{ - return [(NSString*)[array objectAtIndex:index] integerValue]; -} - -void nsCocoaFeatures::GetSystemVersion(int &major, int &minor, int &bugfix) -{ - major = minor = bugfix = 0; - - NSString* versionString = [[NSDictionary dictionaryWithContentsOfFile: - @"/System/Library/CoreServices/SystemVersion.plist"] objectForKey:@"ProductVersion"]; - NSArray* versions = [versionString componentsSeparatedByString:@"."]; - NSUInteger count = [versions count]; - if (count > 0) { - major = intAtStringIndex(versions, 0); - if (count > 1) { - minor = intAtStringIndex(versions, 1); - if (count > 2) { - bugfix = intAtStringIndex(versions, 2); - } - } - } -} - -int32_t nsCocoaFeatures::GetVersion(int32_t aMajor, int32_t aMinor, int32_t aBugFix) -{ - int32_t macOSVersion; - if (aMajor < 10) { - aMajor = 10; - NS_ERROR("Couldn't determine macOS version, assuming 10.7"); - macOSVersion = MACOS_VERSION_10_7_HEX; - } else if (aMajor == 10 && aMinor < 7) { - aMinor = 7; - NS_ERROR("macOS version too old, assuming 10.7"); - macOSVersion = MACOS_VERSION_10_7_HEX; - } else { - MOZ_ASSERT(aMajor >= 10); - MOZ_ASSERT(aMajor < 256); - MOZ_ASSERT(aMinor >= 0); - MOZ_ASSERT(aMinor < 256); - MOZ_ASSERT(aBugFix >= 0); - MOZ_ASSERT(aBugFix < 256); - macOSVersion = AssembleVersion(aMajor, aMinor, aBugFix); - } - MOZ_ASSERT(aMajor == ExtractMajorVersion(macOSVersion)); - MOZ_ASSERT(aMinor == ExtractMinorVersion(macOSVersion)); - MOZ_ASSERT(aBugFix == ExtractBugFixVersion(macOSVersion)); - return macOSVersion; -} - -/*static*/ void -nsCocoaFeatures::InitializeVersionNumbers() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Provide an autorelease pool to avoid leaking Cocoa objects, - // as this gets called before the main autorelease pool is in place. - nsAutoreleasePool localPool; - - int major, minor, bugfix; - GetSystemVersion(major, minor, bugfix); - mOSVersion = GetVersion(major, minor, bugfix); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -/* static */ int32_t -nsCocoaFeatures::macOSVersion() -{ - // Don't let this be called while we're first setting the value... - MOZ_ASSERT((mOSVersion & MACOS_VERSION_MASK) >= 0); - if (!mOSVersion) { - mOSVersion = -1; - InitializeVersionNumbers(); - } - return mOSVersion; -} - -/* static */ int32_t -nsCocoaFeatures::macOSVersionMajor() -{ - return ExtractMajorVersion(macOSVersion()); -} - -/* static */ int32_t -nsCocoaFeatures::macOSVersionMinor() -{ - return ExtractMinorVersion(macOSVersion()); -} - -/* static */ int32_t -nsCocoaFeatures::macOSVersionBugFix() -{ - return ExtractBugFixVersion(macOSVersion()); -} - -/* static */ bool -nsCocoaFeatures::OnYosemiteOrLater() -{ - return (macOSVersion() >= MACOS_VERSION_10_10_HEX); -} - -/* static */ bool -nsCocoaFeatures::OnElCapitanOrLater() -{ - return (macOSVersion() >= MACOS_VERSION_10_11_HEX); -} - -/* static */ bool -nsCocoaFeatures::OnSierraOrLater() -{ - return (macOSVersion() >= MACOS_VERSION_10_12_HEX); -} - -/* static */ bool -nsCocoaFeatures::OnHighSierraOrLater() -{ - return (macOSVersion() >= MACOS_VERSION_10_13_HEX); -} - -/* static */ bool -nsCocoaFeatures::OnMojaveOrLater() -{ - return (macOSVersion() >= MACOS_VERSION_10_14_HEX); -} - -/* static */ bool -nsCocoaFeatures::OnCatalinaOrLater() -{ - return (macOSVersion() >= MACOS_VERSION_10_15_HEX); -} - -/* static */ bool -nsCocoaFeatures::OnBigSurOrLater() -{ - // Account for the version being 10.16 (which occurs when the - // application is linked with an older SDK) or 11.0 on Big Sur. - return ((macOSVersion() >= MACOS_VERSION_10_16_HEX) || - (macOSVersion() >= MACOS_VERSION_11_0_HEX)); -} - -/* static */ bool -nsCocoaFeatures::IsAtLeastVersion(int32_t aMajor, int32_t aMinor, int32_t aBugFix) -{ - return macOSVersion() >= GetVersion(aMajor, aMinor, aBugFix); -} diff --git a/widget/cocoa/nsCocoaUtils.h b/widget/cocoa/nsCocoaUtils.h deleted file mode 100644 index 139e76b4ad..0000000000 --- a/widget/cocoa/nsCocoaUtils.h +++ /dev/null @@ -1,389 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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 nsCocoaUtils_h_ -#define nsCocoaUtils_h_ - -#import <Cocoa/Cocoa.h> - -#include "nsRect.h" -#include "imgIContainer.h" -#include "npapi.h" -#include "nsTArray.h" -#include "Units.h" - -// This must be the last include: -#include "nsObjCExceptions.h" - -#include "mozilla/EventForwards.h" - -// Declare the backingScaleFactor method that we want to call -// on NSView/Window/Screen objects, if they recognize it. -@interface NSObject (BackingScaleFactorCategory) -- (CGFloat)backingScaleFactor; -@end - -#if !defined(MAC_OS_X_VERSION_10_8) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8 -enum { - NSEventPhaseMayBegin = 0x1 << 5 -}; -#endif - -class nsIWidget; - -namespace mozilla { -namespace gfx { -class SourceSurface; -} // namespace gfx -} // namespace mozilla - -// Used to retain a Cocoa object for the remainder of a method's execution. -class nsAutoRetainCocoaObject { -public: -explicit nsAutoRetainCocoaObject(id anObject) -{ - mObject = NS_OBJC_TRY_EXPR_ABORT([anObject retain]); -} -~nsAutoRetainCocoaObject() -{ - NS_OBJC_TRY_ABORT([mObject release]); -} -private: - id mObject; // [STRONG] -}; - -// Provide a local autorelease pool for the remainder of a method's execution. -class nsAutoreleasePool { -public: - nsAutoreleasePool() - { - mLocalPool = [[NSAutoreleasePool alloc] init]; - } - ~nsAutoreleasePool() - { - [mLocalPool release]; - } -private: - NSAutoreleasePool *mLocalPool; -}; - -@interface NSApplication (Undocumented) - -// Present in all versions of OS X from (at least) 10.2.8 through 10.5. -- (BOOL)_isRunningModal; -- (BOOL)_isRunningAppModal; - -// It's sometimes necessary to explicitly remove a window from the "window -// cache" in order to deactivate it. The "window cache" is an undocumented -// subsystem, all of whose methods are included in the NSWindowCache category -// of the NSApplication class (in header files generated using class-dump). -// Present in all versions of OS X from (at least) 10.2.8 through 10.5. -- (void)_removeWindowFromCache:(NSWindow *)aWindow; - -// Send an event to the current Cocoa app-modal session. Present in all -// versions of OS X from (at least) 10.2.8 through 10.5. -- (void)_modalSession:(NSModalSession)aSession sendEvent:(NSEvent *)theEvent; - -@end - -struct KeyBindingsCommand -{ - SEL selector; - id data; -}; - -@interface NativeKeyBindingsRecorder : NSResponder -{ -@private - nsTArray<KeyBindingsCommand>* mCommands; -} - -- (void)startRecording:(nsTArray<KeyBindingsCommand>&)aCommands; - -- (void)doCommandBySelector:(SEL)aSelector; - -- (void)insertText:(id)aString; - -@end // NativeKeyBindingsRecorder - -class nsCocoaUtils -{ - typedef mozilla::gfx::SourceSurface SourceSurface; - typedef mozilla::LayoutDeviceIntPoint LayoutDeviceIntPoint; - typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect; - -public: - - // Get the backing scale factor from an object that supports this selector - // (NSView/Window/Screen, on 10.7 or later), returning 1.0 if not supported - static CGFloat - GetBackingScaleFactor(id aObject) - { - if (HiDPIEnabled() && - [aObject respondsToSelector:@selector(backingScaleFactor)]) { - return [aObject backingScaleFactor]; - } - return 1.0; - } - - // Conversions between Cocoa points and device pixels, given the backing - // scale factor from a view/window/screen. - static int32_t - CocoaPointsToDevPixels(CGFloat aPts, CGFloat aBackingScale) - { - return NSToIntRound(aPts * aBackingScale); - } - - static LayoutDeviceIntPoint - CocoaPointsToDevPixels(const NSPoint& aPt, CGFloat aBackingScale) - { - return LayoutDeviceIntPoint(NSToIntRound(aPt.x * aBackingScale), - NSToIntRound(aPt.y * aBackingScale)); - } - - static LayoutDeviceIntPoint - CocoaPointsToDevPixelsRoundDown(const NSPoint& aPt, CGFloat aBackingScale) - { - return LayoutDeviceIntPoint(NSToIntFloor(aPt.x * aBackingScale), - NSToIntFloor(aPt.y * aBackingScale)); - } - - static LayoutDeviceIntRect - CocoaPointsToDevPixels(const NSRect& aRect, CGFloat aBackingScale) - { - return LayoutDeviceIntRect(NSToIntRound(aRect.origin.x * aBackingScale), - NSToIntRound(aRect.origin.y * aBackingScale), - NSToIntRound(aRect.size.width * aBackingScale), - NSToIntRound(aRect.size.height * aBackingScale)); - } - - static CGFloat - DevPixelsToCocoaPoints(int32_t aPixels, CGFloat aBackingScale) - { - return (CGFloat)aPixels / aBackingScale; - } - - static NSPoint - DevPixelsToCocoaPoints(const mozilla::LayoutDeviceIntPoint& aPt, - CGFloat aBackingScale) - { - return NSMakePoint((CGFloat)aPt.x / aBackingScale, - (CGFloat)aPt.y / aBackingScale); - } - - // Implements an NSPoint equivalent of -[NSWindow convertRectFromScreen:]. - static NSPoint - ConvertPointFromScreen(NSWindow* aWindow, const NSPoint& aPt) - { - return [aWindow convertRectFromScreen:NSMakeRect(aPt.x, aPt.y, 0, 0)].origin; - } - - // Implements an NSPoint equivalent of -[NSWindow convertRectToScreen:]. - static NSPoint - ConvertPointToScreen(NSWindow* aWindow, const NSPoint& aPt) - { - return [aWindow convertRectToScreen:NSMakeRect(aPt.x, aPt.y, 0, 0)].origin; - } - - static NSRect - DevPixelsToCocoaPoints(const LayoutDeviceIntRect& aRect, - CGFloat aBackingScale) - { - return NSMakeRect((CGFloat)aRect.x / aBackingScale, - (CGFloat)aRect.y / aBackingScale, - (CGFloat)aRect.width / aBackingScale, - (CGFloat)aRect.height / aBackingScale); - } - - // Returns the given y coordinate, which must be in screen coordinates, - // flipped from Gecko to Cocoa or Cocoa to Gecko. - static float FlippedScreenY(float y); - - // The following functions come in "DevPix" variants that work with - // backing-store (device pixel) coordinates, as well as the original - // versions that expect coordinates in Cocoa points/CSS pixels. - // The difference becomes important in HiDPI display modes, where Cocoa - // points and backing-store pixels are no longer 1:1. - - // Gecko rects (nsRect) contain an origin (x,y) in a coordinate - // system with (0,0) in the top-left of the primary screen. Cocoa rects - // (NSRect) contain an origin (x,y) in a coordinate system with (0,0) - // in the bottom-left of the primary screen. Both nsRect and NSRect - // contain width/height info, with no difference in their use. - // This function does no scaling, so the Gecko coordinates are - // expected to be desktop pixels, which are equal to Cocoa points - // (by definition). - static NSRect GeckoRectToCocoaRect(const mozilla::DesktopIntRect &geckoRect); - - // Converts aGeckoRect in dev pixels to points in Cocoa coordinates - static NSRect - GeckoRectToCocoaRectDevPix(const mozilla::LayoutDeviceIntRect &aGeckoRect, - CGFloat aBackingScale); - - // See explanation for geckoRectToCocoaRect, guess what this does... - static mozilla::DesktopIntRect CocoaRectToGeckoRect(const NSRect &cocoaRect); - - static mozilla::LayoutDeviceIntRect CocoaRectToGeckoRectDevPix( - const NSRect& aCocoaRect, CGFloat aBackingScale); - - // Gives the location for the event in screen coordinates. Do not call this - // unless the window the event was originally targeted at is still alive! - // anEvent may be nil -- in that case the current mouse location is returned. - static NSPoint ScreenLocationForEvent(NSEvent* anEvent); - - // Determines if an event happened over a window, whether or not the event - // is for the window. Does not take window z-order into account. - static BOOL IsEventOverWindow(NSEvent* anEvent, NSWindow* aWindow); - - // Events are set up so that their coordinates refer to the window to which they - // were originally sent. If we reroute the event somewhere else, we'll have - // to get the window coordinates this way. Do not call this unless the window - // the event was originally targeted at is still alive! - static NSPoint EventLocationForWindow(NSEvent* anEvent, NSWindow* aWindow); - - // Compatibility wrappers for the -[NSEvent phase], -[NSEvent momentumPhase], - // -[NSEvent hasPreciseScrollingDeltas] and -[NSEvent scrollingDeltaX/Y] APIs - // that became availaible starting with the 10.7 SDK. - // All of these can be removed once we drop support for 10.6. - static NSEventPhase EventPhase(NSEvent* aEvent); - static NSEventPhase EventMomentumPhase(NSEvent* aEvent); - static BOOL IsMomentumScrollEvent(NSEvent* aEvent); - static BOOL HasPreciseScrollingDeltas(NSEvent* aEvent); - static void GetScrollingDeltas(NSEvent* aEvent, CGFloat* aOutDeltaX, CGFloat* aOutDeltaY); - static BOOL EventHasPhaseInformation(NSEvent* aEvent); - - // Hides the Menu bar and the Dock. Multiple hide/show requests can be nested. - static void HideOSChromeOnScreen(bool aShouldHide); - - static nsIWidget* GetHiddenWindowWidget(); - - static void PrepareForNativeAppModalDialog(); - static void CleanUpAfterNativeAppModalDialog(); - - // 3 utility functions to go from a frame of imgIContainer to CGImage and then to NSImage - // Convert imgIContainer -> CGImageRef, caller owns result - - /** Creates a <code>CGImageRef</code> from a frame contained in an <code>imgIContainer</code>. - Copies the pixel data from the indicated frame of the <code>imgIContainer</code> into a new <code>CGImageRef</code>. - The caller owns the <code>CGImageRef</code>. - @param aFrame the frame to convert - @param aResult the resulting CGImageRef - @return NS_OK if the conversion worked, NS_ERROR_FAILURE otherwise - */ - static nsresult CreateCGImageFromSurface(SourceSurface* aSurface, - CGImageRef* aResult); - - /** Creates a Cocoa <code>NSImage</code> from a <code>CGImageRef</code>. - Copies the pixel data from the <code>CGImageRef</code> into a new <code>NSImage</code>. - The caller owns the <code>NSImage</code>. - @param aInputImage the image to convert - @param aResult the resulting NSImage - @return NS_OK if the conversion worked, NS_ERROR_FAILURE otherwise - */ - static nsresult CreateNSImageFromCGImage(CGImageRef aInputImage, NSImage **aResult); - - /** Creates a Cocoa <code>NSImage</code> from a frame of an <code>imgIContainer</code>. - Combines the two methods above. The caller owns the <code>NSImage</code>. - @param aImage the image to extract a frame from - @param aWhichFrame the frame to extract (see imgIContainer FRAME_*) - @param aResult the resulting NSImage - @param scaleFactor the desired scale factor of the NSImage (2 for a retina display) - @return NS_OK if the conversion worked, NS_ERROR_FAILURE otherwise - */ - static nsresult CreateNSImageFromImageContainer(imgIContainer *aImage, uint32_t aWhichFrame, NSImage **aResult, CGFloat scaleFactor); - - /** - * Returns nsAString for aSrc. - */ - static void GetStringForNSString(const NSString *aSrc, nsAString& aDist); - - /** - * Makes NSString instance for aString. - */ - static NSString* ToNSString(const nsAString& aString); - - /** - * Returns NSRect for aGeckoRect. - * Just copies values between the two types; it does no coordinate-system - * conversion, so both rects must have the same coordinate origin/direction. - */ - static void GeckoRectToNSRect(const nsIntRect& aGeckoRect, - NSRect& aOutCocoaRect); - - /** - * Returns Gecko rect for aCocoaRect. - * Just copies values between the two types; it does no coordinate-system - * conversion, so both rects must have the same coordinate origin/direction. - */ - static void NSRectToGeckoRect(const NSRect& aCocoaRect, - nsIntRect& aOutGeckoRect); - - /** - * Makes NSEvent instance for aEventTytpe and aEvent. - */ - static NSEvent* MakeNewCocoaEventWithType(NSEventType aEventType, - NSEvent *aEvent); - - /** - * Initializes aNPCocoaEvent. - */ - static void InitNPCocoaEvent(NPCocoaEvent* aNPCocoaEvent); - - /** - * Initializes WidgetInputEvent for aNativeEvent or aModifiers. - */ - static void InitInputEvent(mozilla::WidgetInputEvent &aInputEvent, - NSEvent* aNativeEvent); - - /** - * Converts the native modifiers from aNativeEvent into WidgetMouseEvent - * Modifiers. aNativeEvent can be null. - */ - static mozilla::Modifiers ModifiersForEvent(NSEvent* aNativeEvent); - - /** - * ConvertToCarbonModifier() returns carbon modifier flags for the cocoa - * modifier flags. - * NOTE: The result never includes right*Key. - */ - static UInt32 ConvertToCarbonModifier(NSUInteger aCocoaModifier); - - /** - * Whether to support HiDPI rendering. For testing purposes, to be removed - * once we're comfortable with the HiDPI behavior. - */ - static bool HiDPIEnabled(); - - /** - * Keys can optionally be bound by system or user key bindings to one or more - * commands based on selectors. This collects any such commands in the - * provided array. - */ - static void GetCommandsFromKeyEvent(NSEvent* aEvent, - nsTArray<KeyBindingsCommand>& aCommands); - - /** - * Converts the string name of a Gecko key (like "VK_HOME") to the - * corresponding Cocoa Unicode character. - */ - static uint32_t ConvertGeckoNameToMacCharCode(const nsAString& aKeyCodeName); - - /** - * Converts a Gecko key code (like NS_VK_HOME) to the corresponding Cocoa - * Unicode character. - */ - static uint32_t ConvertGeckoKeyCodeToMacCharCode(uint32_t aKeyCode); - - /** - * Convert string with font attribute to NSMutableAttributedString - */ - static NSMutableAttributedString* GetNSMutableAttributedString( - const nsAString& aText, - const nsTArray<mozilla::FontRange>& aFontRanges, - const bool aIsVertical, - const CGFloat aBackingScaleFactor); -}; - -#endif // nsCocoaUtils_h_ diff --git a/widget/cocoa/nsCocoaUtils.mm b/widget/cocoa/nsCocoaUtils.mm deleted file mode 100644 index 3138245aa7..0000000000 --- a/widget/cocoa/nsCocoaUtils.mm +++ /dev/null @@ -1,1022 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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/. */ - -#include <cmath> - -#include "gfx2DGlue.h" -#include "gfxPlatform.h" -#include "gfxUtils.h" -#include "ImageRegion.h" -#include "nsCocoaUtils.h" -#include "nsChildView.h" -#include "nsMenuBarX.h" -#include "nsCocoaWindow.h" -#include "nsCOMPtr.h" -#include "nsIInterfaceRequestorUtils.h" -#include "nsIAppShellService.h" -#include "nsIXULWindow.h" -#include "nsIBaseWindow.h" -#include "nsIServiceManager.h" -#include "nsMenuUtilsX.h" -#include "nsToolkit.h" -#include "nsCRT.h" -#include "SVGImageContext.h" -#include "mozilla/gfx/2D.h" -#include "mozilla/MiscEvents.h" -#include "mozilla/Preferences.h" -#include "mozilla/TextEvents.h" - -using namespace mozilla; -using namespace mozilla::widget; - -using mozilla::gfx::BackendType; -using mozilla::gfx::DataSourceSurface; -using mozilla::gfx::DrawTarget; -using mozilla::gfx::Factory; -using mozilla::gfx::SamplingFilter; -using mozilla::gfx::IntPoint; -using mozilla::gfx::IntRect; -using mozilla::gfx::IntSize; -using mozilla::gfx::SurfaceFormat; -using mozilla::gfx::SourceSurface; -using mozilla::image::ImageRegion; -using std::ceil; - -static float -MenuBarScreenHeight() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - NSArray* allScreens = [NSScreen screens]; - if ([allScreens count]) { - return [[allScreens objectAtIndex:0] frame].size.height; - } - - return 0.0; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0.0); -} - -float -nsCocoaUtils::FlippedScreenY(float y) -{ - return MenuBarScreenHeight() - y; -} - -NSRect nsCocoaUtils::GeckoRectToCocoaRect(const DesktopIntRect &geckoRect) -{ - // We only need to change the Y coordinate by starting with the primary screen - // height and subtracting the gecko Y coordinate of the bottom of the rect. - return NSMakeRect(geckoRect.x, - MenuBarScreenHeight() - geckoRect.YMost(), - geckoRect.width, - geckoRect.height); -} - -NSRect -nsCocoaUtils::GeckoRectToCocoaRectDevPix(const LayoutDeviceIntRect &aGeckoRect, - CGFloat aBackingScale) -{ - return NSMakeRect(aGeckoRect.x / aBackingScale, - MenuBarScreenHeight() - aGeckoRect.YMost() / aBackingScale, - aGeckoRect.width / aBackingScale, - aGeckoRect.height / aBackingScale); -} - -DesktopIntRect nsCocoaUtils::CocoaRectToGeckoRect(const NSRect &cocoaRect) -{ - // We only need to change the Y coordinate by starting with the primary screen - // height and subtracting both the cocoa y origin and the height of the - // cocoa rect. - DesktopIntRect rect; - rect.x = NSToIntRound(cocoaRect.origin.x); - rect.y = NSToIntRound(FlippedScreenY(cocoaRect.origin.y + cocoaRect.size.height)); - rect.width = NSToIntRound(cocoaRect.origin.x + cocoaRect.size.width) - rect.x; - rect.height = NSToIntRound(FlippedScreenY(cocoaRect.origin.y)) - rect.y; - return rect; -} - -LayoutDeviceIntRect nsCocoaUtils::CocoaRectToGeckoRectDevPix( - const NSRect& aCocoaRect, CGFloat aBackingScale) -{ - LayoutDeviceIntRect rect; - rect.x = NSToIntRound(aCocoaRect.origin.x * aBackingScale); - rect.y = NSToIntRound(FlippedScreenY(aCocoaRect.origin.y + aCocoaRect.size.height) * aBackingScale); - rect.width = NSToIntRound((aCocoaRect.origin.x + aCocoaRect.size.width) * aBackingScale) - rect.x; - rect.height = NSToIntRound(FlippedScreenY(aCocoaRect.origin.y) * aBackingScale) - rect.y; - return rect; -} - -NSPoint nsCocoaUtils::ScreenLocationForEvent(NSEvent* anEvent) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - // Don't trust mouse locations of mouse move events, see bug 443178. - if (!anEvent || [anEvent type] == NSMouseMoved) - return [NSEvent mouseLocation]; - - // Pin momentum scroll events to the location of the last user-controlled - // scroll event. - if (IsMomentumScrollEvent(anEvent)) - return ChildViewMouseTracker::sLastScrollEventScreenLocation; - - return nsCocoaUtils::ConvertPointToScreen([anEvent window], [anEvent locationInWindow]); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakePoint(0.0, 0.0)); -} - -BOOL nsCocoaUtils::IsEventOverWindow(NSEvent* anEvent, NSWindow* aWindow) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - return NSPointInRect(ScreenLocationForEvent(anEvent), [aWindow frame]); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); -} - -NSPoint nsCocoaUtils::EventLocationForWindow(NSEvent* anEvent, NSWindow* aWindow) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - return nsCocoaUtils::ConvertPointFromScreen(aWindow, ScreenLocationForEvent(anEvent)); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakePoint(0.0, 0.0)); -} - -@interface NSEvent (ScrollPhase) -// 10.5 and 10.6 -- (long long)_scrollPhase; -// 10.7 and above -- (NSEventPhase)phase; -- (NSEventPhase)momentumPhase; -@end - -NSEventPhase nsCocoaUtils::EventPhase(NSEvent* aEvent) -{ - if ([aEvent respondsToSelector:@selector(phase)]) { - return [aEvent phase]; - } - return NSEventPhaseNone; -} - -NSEventPhase nsCocoaUtils::EventMomentumPhase(NSEvent* aEvent) -{ - if ([aEvent respondsToSelector:@selector(momentumPhase)]) { - return [aEvent momentumPhase]; - } - if ([aEvent respondsToSelector:@selector(_scrollPhase)]) { - switch ([aEvent _scrollPhase]) { - case 1: return NSEventPhaseBegan; - case 2: return NSEventPhaseChanged; - case 3: return NSEventPhaseEnded; - default: return NSEventPhaseNone; - } - } - return NSEventPhaseNone; -} - -BOOL nsCocoaUtils::IsMomentumScrollEvent(NSEvent* aEvent) -{ - return [aEvent type] == NSScrollWheel && - EventMomentumPhase(aEvent) != NSEventPhaseNone; -} - -@interface NSEvent (HasPreciseScrollingDeltas) -// 10.7 and above -- (BOOL)hasPreciseScrollingDeltas; -// For 10.6 and below, see the comment in nsChildView.h about _eventRef -- (EventRef)_eventRef; -@end - -BOOL nsCocoaUtils::HasPreciseScrollingDeltas(NSEvent* aEvent) -{ - if ([aEvent respondsToSelector:@selector(hasPreciseScrollingDeltas)]) { - return [aEvent hasPreciseScrollingDeltas]; - } - - // For events that don't contain pixel scrolling information, the event - // kind of their underlaying carbon event is kEventMouseWheelMoved instead - // of kEventMouseScroll. - EventRef carbonEvent = [aEvent _eventRef]; - return carbonEvent && ::GetEventKind(carbonEvent) == kEventMouseScroll; -} - -@interface NSEvent (ScrollingDeltas) -// 10.6 and below -- (CGFloat)deviceDeltaX; -- (CGFloat)deviceDeltaY; -// 10.7 and above -- (CGFloat)scrollingDeltaX; -- (CGFloat)scrollingDeltaY; -@end - -void nsCocoaUtils::GetScrollingDeltas(NSEvent* aEvent, CGFloat* aOutDeltaX, CGFloat* aOutDeltaY) -{ - if ([aEvent respondsToSelector:@selector(scrollingDeltaX)]) { - *aOutDeltaX = [aEvent scrollingDeltaX]; - *aOutDeltaY = [aEvent scrollingDeltaY]; - return; - } - if ([aEvent respondsToSelector:@selector(deviceDeltaX)] && - HasPreciseScrollingDeltas(aEvent)) { - // Calling deviceDeltaX/Y on those events that do not contain pixel - // scrolling information triggers a Cocoa assertion and an - // Objective-C NSInternalInconsistencyException. - *aOutDeltaX = [aEvent deviceDeltaX]; - *aOutDeltaY = [aEvent deviceDeltaY]; - return; - } - - // This is only hit pre-10.7 when we are called on a scroll event that does - // not contain pixel scrolling information. - CGFloat lineDeltaPixels = 12; - *aOutDeltaX = [aEvent deltaX] * lineDeltaPixels; - *aOutDeltaY = [aEvent deltaY] * lineDeltaPixels; -} - -BOOL nsCocoaUtils::EventHasPhaseInformation(NSEvent* aEvent) -{ - if (![aEvent respondsToSelector:@selector(phase)]) { - return NO; - } - return EventPhase(aEvent) != NSEventPhaseNone || - EventMomentumPhase(aEvent) != NSEventPhaseNone; -} - -void nsCocoaUtils::HideOSChromeOnScreen(bool aShouldHide) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Keep track of how many hiding requests have been made, so that they can - // be nested. - static int sHiddenCount = 0; - - sHiddenCount += aShouldHide ? 1 : -1; - NS_ASSERTION(sHiddenCount >= 0, "Unbalanced HideMenuAndDockForWindow calls"); - - NSApplicationPresentationOptions options = - sHiddenCount <= 0 ? NSApplicationPresentationDefault : - NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar; - [NSApp setPresentationOptions:options]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -#define NS_APPSHELLSERVICE_CONTRACTID "@mozilla.org/appshell/appShellService;1" -nsIWidget* nsCocoaUtils::GetHiddenWindowWidget() -{ - nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID)); - if (!appShell) { - NS_WARNING("Couldn't get AppShellService in order to get hidden window ref"); - return nullptr; - } - - nsCOMPtr<nsIXULWindow> hiddenWindow; - appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow)); - if (!hiddenWindow) { - // Don't warn, this happens during shutdown, bug 358607. - return nullptr; - } - - nsCOMPtr<nsIBaseWindow> baseHiddenWindow; - baseHiddenWindow = do_GetInterface(hiddenWindow); - if (!baseHiddenWindow) { - NS_WARNING("Couldn't get nsIBaseWindow from hidden window (nsIXULWindow)"); - return nullptr; - } - - nsCOMPtr<nsIWidget> hiddenWindowWidget; - if (NS_FAILED(baseHiddenWindow->GetMainWidget(getter_AddRefs(hiddenWindowWidget)))) { - NS_WARNING("Couldn't get nsIWidget from hidden window (nsIBaseWindow)"); - return nullptr; - } - - return hiddenWindowWidget; -} - -void nsCocoaUtils::PrepareForNativeAppModalDialog() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Don't do anything if this is embedding. We'll assume that if there is no hidden - // window we shouldn't do anything, and that should cover the embedding case. - nsMenuBarX* hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar(); - if (!hiddenWindowMenuBar) - return; - - // First put up the hidden window menu bar so that app menu event handling is correct. - hiddenWindowMenuBar->Paint(); - - NSMenu* mainMenu = [NSApp mainMenu]; - NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!"); - - // Create new menu bar for use with modal dialog - NSMenu* newMenuBar = [[NSMenu alloc] initWithTitle:@""]; - - // Swap in our app menu. Note that the event target is whatever window is up when - // the app modal dialog goes up. - NSMenuItem* firstMenuItem = [[mainMenu itemAtIndex:0] retain]; - [mainMenu removeItemAtIndex:0]; - [newMenuBar insertItem:firstMenuItem atIndex:0]; - [firstMenuItem release]; - - // Add standard edit menu - [newMenuBar addItem:nsMenuUtilsX::GetStandardEditMenuItem()]; - - // Show the new menu bar - [NSApp setMainMenu:newMenuBar]; - [newMenuBar release]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void nsCocoaUtils::CleanUpAfterNativeAppModalDialog() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Don't do anything if this is embedding. We'll assume that if there is no hidden - // window we shouldn't do anything, and that should cover the embedding case. - nsMenuBarX* hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar(); - if (!hiddenWindowMenuBar) - return; - - NSWindow* mainWindow = [NSApp mainWindow]; - if (!mainWindow) - hiddenWindowMenuBar->Paint(); - else - [WindowDelegate paintMenubarForWindow:mainWindow]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void data_ss_release_callback(void *aDataSourceSurface, - const void *data, - size_t size) -{ - if (aDataSourceSurface) { - static_cast<DataSourceSurface*>(aDataSourceSurface)->Unmap(); - static_cast<DataSourceSurface*>(aDataSourceSurface)->Release(); - } -} - -nsresult nsCocoaUtils::CreateCGImageFromSurface(SourceSurface* aSurface, - CGImageRef* aResult) -{ - RefPtr<DataSourceSurface> dataSurface; - - if (aSurface->GetFormat() == SurfaceFormat::B8G8R8A8) { - dataSurface = aSurface->GetDataSurface(); - } else { - // CGImageCreate only supports 16- and 32-bit bit-depth - // Convert format to SurfaceFormat::B8G8R8A8 - dataSurface = gfxUtils:: - CopySurfaceToDataSourceSurfaceWithFormat(aSurface, - SurfaceFormat::B8G8R8A8); - } - - NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE); - - int32_t width = dataSurface->GetSize().width; - int32_t height = dataSurface->GetSize().height; - if (height < 1 || width < 1) { - return NS_ERROR_FAILURE; - } - - DataSourceSurface::MappedSurface map; - if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) { - return NS_ERROR_FAILURE; - } - // The Unmap() call happens in data_ss_release_callback - - // Create a CGImageRef with the bits from the image, taking into account - // the alpha ordering and endianness of the machine so we don't have to - // touch the bits ourselves. - CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(dataSurface.forget().take(), - map.mData, - map.mStride * height, - data_ss_release_callback); - CGColorSpaceRef colorSpace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); - *aResult = ::CGImageCreate(width, - height, - 8, - 32, - map.mStride, - colorSpace, - kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst, - dataProvider, - NULL, - 0, - kCGRenderingIntentDefault); - ::CGColorSpaceRelease(colorSpace); - ::CGDataProviderRelease(dataProvider); - return *aResult ? NS_OK : NS_ERROR_FAILURE; -} - -nsresult nsCocoaUtils::CreateNSImageFromCGImage(CGImageRef aInputImage, NSImage **aResult) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // Be very careful when creating the NSImage that the backing NSImageRep is - // exactly 1:1 with the input image. On a retina display, both [NSImage - // lockFocus] and [NSImage initWithCGImage:size:] will create an image with a - // 2x backing NSImageRep. This prevents NSCursor from recognizing a retina - // cursor, which only occurs if pixelsWide and pixelsHigh are exactly 2x the - // size of the NSImage. - // - // For example, if a 32x32 SVG cursor is rendered on a retina display, then - // aInputImage will be 64x64. The resulting NSImage will be scaled back down - // to 32x32 so it stays the correct size on the screen by changing its size - // (resizing a NSImage only scales the image and doesn't resample the data). - // If aInputImage is converted using [NSImage initWithCGImage:size:] then the - // bitmap will be 128x128 and NSCursor won't recognize a retina cursor, since - // it will expect a 64x64 bitmap. - - int32_t width = ::CGImageGetWidth(aInputImage); - int32_t height = ::CGImageGetHeight(aInputImage); - NSRect imageRect = ::NSMakeRect(0.0, 0.0, width, height); - - NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:NULL - pixelsWide:width - pixelsHigh:height - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bitmapFormat:NSAlphaFirstBitmapFormat - bytesPerRow:0 - bitsPerPixel:0]; - - NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep]; - [NSGraphicsContext saveGraphicsState]; - [NSGraphicsContext setCurrentContext:context]; - - // Get the Quartz context and draw. - CGContextRef imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; - ::CGContextDrawImage(imageContext, *(CGRect*)&imageRect, aInputImage); - - [NSGraphicsContext restoreGraphicsState]; - - *aResult = [[NSImage alloc] initWithSize:NSMakeSize(width, height)]; - [*aResult addRepresentation:offscreenRep]; - [offscreenRep release]; - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -nsresult nsCocoaUtils::CreateNSImageFromImageContainer(imgIContainer *aImage, uint32_t aWhichFrame, NSImage **aResult, CGFloat scaleFactor) -{ - RefPtr<SourceSurface> surface; - int32_t width = 0, height = 0; - aImage->GetWidth(&width); - aImage->GetHeight(&height); - - // Render a vector image at the correct resolution on a retina display - if (aImage->GetType() == imgIContainer::TYPE_VECTOR && scaleFactor != 1.0f) { - IntSize scaledSize = IntSize::Ceil(width * scaleFactor, height * scaleFactor); - - RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()-> - CreateOffscreenContentDrawTarget(scaledSize, SurfaceFormat::B8G8R8A8); - if (!drawTarget || !drawTarget->IsValid()) { - NS_ERROR("Failed to create valid DrawTarget"); - return NS_ERROR_FAILURE; - } - - RefPtr<gfxContext> context = gfxContext::CreateOrNull(drawTarget); - MOZ_ASSERT(context); - - mozilla::image::DrawResult res = - aImage->Draw(context, scaledSize, ImageRegion::Create(scaledSize), - aWhichFrame, SamplingFilter::POINT, - /* no SVGImageContext */ Nothing(), - imgIContainer::FLAG_SYNC_DECODE); - - if (res != mozilla::image::DrawResult::SUCCESS) { - return NS_ERROR_FAILURE; - } - - surface = drawTarget->Snapshot(); - } else { - surface = aImage->GetFrame(aWhichFrame, imgIContainer::FLAG_SYNC_DECODE); - } - - NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE); - - CGImageRef imageRef = NULL; - nsresult rv = nsCocoaUtils::CreateCGImageFromSurface(surface, &imageRef); - if (NS_FAILED(rv) || !imageRef) { - return NS_ERROR_FAILURE; - } - - rv = nsCocoaUtils::CreateNSImageFromCGImage(imageRef, aResult); - if (NS_FAILED(rv) || !aResult) { - return NS_ERROR_FAILURE; - } - ::CGImageRelease(imageRef); - - // Ensure the image will be rendered the correct size on a retina display - NSSize size = NSMakeSize(width, height); - [*aResult setSize:size]; - [[[*aResult representations] objectAtIndex:0] setSize:size]; - return NS_OK; -} - -// static -void -nsCocoaUtils::GetStringForNSString(const NSString *aSrc, nsAString& aDist) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!aSrc) { - aDist.Truncate(); - return; - } - - aDist.SetLength([aSrc length]); - [aSrc getCharacters: reinterpret_cast<unichar*>(aDist.BeginWriting())]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// static -NSString* -nsCocoaUtils::ToNSString(const nsAString& aString) -{ - if (aString.IsEmpty()) { - return [NSString string]; - } - return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(aString.BeginReading()) - length:aString.Length()]; -} - -// static -void -nsCocoaUtils::GeckoRectToNSRect(const nsIntRect& aGeckoRect, - NSRect& aOutCocoaRect) -{ - aOutCocoaRect.origin.x = aGeckoRect.x; - aOutCocoaRect.origin.y = aGeckoRect.y; - aOutCocoaRect.size.width = aGeckoRect.width; - aOutCocoaRect.size.height = aGeckoRect.height; -} - -// static -void -nsCocoaUtils::NSRectToGeckoRect(const NSRect& aCocoaRect, - nsIntRect& aOutGeckoRect) -{ - aOutGeckoRect.x = NSToIntRound(aCocoaRect.origin.x); - aOutGeckoRect.y = NSToIntRound(aCocoaRect.origin.y); - aOutGeckoRect.width = NSToIntRound(aCocoaRect.origin.x + aCocoaRect.size.width) - aOutGeckoRect.x; - aOutGeckoRect.height = NSToIntRound(aCocoaRect.origin.y + aCocoaRect.size.height) - aOutGeckoRect.y; -} - -// static -NSEvent* -nsCocoaUtils::MakeNewCocoaEventWithType(NSEventType aEventType, NSEvent *aEvent) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - NSEvent* newEvent = - [NSEvent keyEventWithType:aEventType - location:[aEvent locationInWindow] - modifierFlags:[aEvent modifierFlags] - timestamp:[aEvent timestamp] - windowNumber:[aEvent windowNumber] - context:[aEvent context] - characters:[aEvent characters] - charactersIgnoringModifiers:[aEvent charactersIgnoringModifiers] - isARepeat:[aEvent isARepeat] - keyCode:[aEvent keyCode]]; - return newEvent; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -// static -void -nsCocoaUtils::InitNPCocoaEvent(NPCocoaEvent* aNPCocoaEvent) -{ - memset(aNPCocoaEvent, 0, sizeof(NPCocoaEvent)); -} - -// static -void -nsCocoaUtils::InitInputEvent(WidgetInputEvent& aInputEvent, - NSEvent* aNativeEvent) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - aInputEvent.mModifiers = ModifiersForEvent(aNativeEvent); - aInputEvent.mTime = PR_IntervalNow(); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// static -Modifiers -nsCocoaUtils::ModifiersForEvent(NSEvent* aNativeEvent) -{ - NSUInteger modifiers = - aNativeEvent ? [aNativeEvent modifierFlags] : [NSEvent modifierFlags]; - Modifiers result = 0; - if (modifiers & NSShiftKeyMask) { - result |= MODIFIER_SHIFT; - } - if (modifiers & NSControlKeyMask) { - result |= MODIFIER_CONTROL; - } - if (modifiers & NSAlternateKeyMask) { - result |= MODIFIER_ALT; - // Mac's option key is similar to other platforms' AltGr key. - // Let's set AltGr flag when option key is pressed for consistency with - // other platforms. - result |= MODIFIER_ALTGRAPH; - } - if (modifiers & NSCommandKeyMask) { - result |= MODIFIER_META; - } - - if (modifiers & NSAlphaShiftKeyMask) { - result |= MODIFIER_CAPSLOCK; - } - // Mac doesn't have NumLock key. We can assume that NumLock is always locked - // if user is using a keyboard which has numpad. Otherwise, if user is using - // a keyboard which doesn't have numpad, e.g., MacBook's keyboard, we can - // assume that NumLock is always unlocked. - // Unfortunately, we cannot know whether current keyboard has numpad or not. - // We should notify locked state only when keys in numpad are pressed. - // By this, web applications may not be confused by unexpected numpad key's - // key event with unlocked state. - if (modifiers & NSNumericPadKeyMask) { - result |= MODIFIER_NUMLOCK; - } - - // Be aware, NSFunctionKeyMask is included when arrow keys, home key or some - // other keys are pressed. We cannot check whether 'fn' key is pressed or - // not by the flag. - - return result; -} - -// static -UInt32 -nsCocoaUtils::ConvertToCarbonModifier(NSUInteger aCocoaModifier) -{ - UInt32 carbonModifier = 0; - if (aCocoaModifier & NSAlphaShiftKeyMask) { - carbonModifier |= alphaLock; - } - if (aCocoaModifier & NSControlKeyMask) { - carbonModifier |= controlKey; - } - if (aCocoaModifier & NSAlternateKeyMask) { - carbonModifier |= optionKey; - } - if (aCocoaModifier & NSShiftKeyMask) { - carbonModifier |= shiftKey; - } - if (aCocoaModifier & NSCommandKeyMask) { - carbonModifier |= cmdKey; - } - if (aCocoaModifier & NSNumericPadKeyMask) { - carbonModifier |= kEventKeyModifierNumLockMask; - } - if (aCocoaModifier & NSFunctionKeyMask) { - carbonModifier |= kEventKeyModifierFnMask; - } - return carbonModifier; -} - -// While HiDPI support is not 100% complete and tested, we'll have a pref -// to allow it to be turned off in case of problems (or for testing purposes). - -// gfx.hidpi.enabled is an integer with the meaning: -// <= 0 : HiDPI support is disabled -// 1 : HiDPI enabled provided all screens have the same backing resolution -// > 1 : HiDPI enabled even if there are a mixture of screen modes - -// All the following code is to be removed once HiDPI work is more complete. - -static bool sHiDPIEnabled = false; -static bool sHiDPIPrefInitialized = false; - -// static -bool -nsCocoaUtils::HiDPIEnabled() -{ - if (!sHiDPIPrefInitialized) { - sHiDPIPrefInitialized = true; - - int prefSetting = Preferences::GetInt("gfx.hidpi.enabled", 1); - if (prefSetting <= 0) { - return false; - } - - // prefSetting is at least 1, need to check attached screens... - - int scaleFactors = 0; // used as a bitset to track the screen types found - NSEnumerator *screenEnum = [[NSScreen screens] objectEnumerator]; - while (NSScreen *screen = [screenEnum nextObject]) { - NSDictionary *desc = [screen deviceDescription]; - if ([desc objectForKey:NSDeviceIsScreen] == nil) { - continue; - } - CGFloat scale = - [screen respondsToSelector:@selector(backingScaleFactor)] ? - [screen backingScaleFactor] : 1.0; - // Currently, we only care about differentiating "1.0" and "2.0", - // so we set one of the two low bits to record which. - if (scale > 1.0) { - scaleFactors |= 2; - } else { - scaleFactors |= 1; - } - } - - // Now scaleFactors will be: - // 0 if no screens (supporting backingScaleFactor) found - // 1 if only lo-DPI screens - // 2 if only hi-DPI screens - // 3 if both lo- and hi-DPI screens - // We'll enable HiDPI support if there's only a single screen type, - // OR if the pref setting is explicitly greater than 1. - sHiDPIEnabled = (scaleFactors <= 2) || (prefSetting > 1); - } - - return sHiDPIEnabled; -} - -void -nsCocoaUtils::GetCommandsFromKeyEvent(NSEvent* aEvent, - nsTArray<KeyBindingsCommand>& aCommands) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - MOZ_ASSERT(aEvent); - - static NativeKeyBindingsRecorder* sNativeKeyBindingsRecorder; - if (!sNativeKeyBindingsRecorder) { - sNativeKeyBindingsRecorder = [NativeKeyBindingsRecorder new]; - } - - [sNativeKeyBindingsRecorder startRecording:aCommands]; - - // This will trigger 0 - N calls to doCommandBySelector: and insertText: - [sNativeKeyBindingsRecorder - interpretKeyEvents:[NSArray arrayWithObject:aEvent]]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -@implementation NativeKeyBindingsRecorder - -- (void)startRecording:(nsTArray<KeyBindingsCommand>&)aCommands -{ - mCommands = &aCommands; - mCommands->Clear(); -} - -- (void)doCommandBySelector:(SEL)aSelector -{ - KeyBindingsCommand command = { - aSelector, - nil - }; - - mCommands->AppendElement(command); -} - -- (void)insertText:(id)aString -{ - KeyBindingsCommand command = { - @selector(insertText:), - aString - }; - - mCommands->AppendElement(command); -} - -@end // NativeKeyBindingsRecorder - -struct KeyConversionData -{ - const char* str; - size_t strLength; - uint32_t geckoKeyCode; - uint32_t charCode; -}; - -static const KeyConversionData gKeyConversions[] = { - -#define KEYCODE_ENTRY(aStr, aCode) \ - {#aStr, sizeof(#aStr) - 1, NS_##aStr, aCode} - -// Some keycodes may have different name in nsIDOMKeyEvent from its key name. -#define KEYCODE_ENTRY2(aStr, aNSName, aCode) \ - {#aStr, sizeof(#aStr) - 1, NS_##aNSName, aCode} - - KEYCODE_ENTRY(VK_CANCEL, 0x001B), - KEYCODE_ENTRY(VK_DELETE, NSDeleteFunctionKey), - KEYCODE_ENTRY(VK_BACK, NSBackspaceCharacter), - KEYCODE_ENTRY2(VK_BACK_SPACE, VK_BACK, NSBackspaceCharacter), - KEYCODE_ENTRY(VK_TAB, NSTabCharacter), - KEYCODE_ENTRY(VK_CLEAR, NSClearLineFunctionKey), - KEYCODE_ENTRY(VK_RETURN, NSEnterCharacter), - KEYCODE_ENTRY(VK_SHIFT, 0), - KEYCODE_ENTRY(VK_CONTROL, 0), - KEYCODE_ENTRY(VK_ALT, 0), - KEYCODE_ENTRY(VK_PAUSE, NSPauseFunctionKey), - KEYCODE_ENTRY(VK_CAPS_LOCK, 0), - KEYCODE_ENTRY(VK_ESCAPE, 0), - KEYCODE_ENTRY(VK_SPACE, ' '), - KEYCODE_ENTRY(VK_PAGE_UP, NSPageUpFunctionKey), - KEYCODE_ENTRY(VK_PAGE_DOWN, NSPageDownFunctionKey), - KEYCODE_ENTRY(VK_END, NSEndFunctionKey), - KEYCODE_ENTRY(VK_HOME, NSHomeFunctionKey), - KEYCODE_ENTRY(VK_LEFT, NSLeftArrowFunctionKey), - KEYCODE_ENTRY(VK_UP, NSUpArrowFunctionKey), - KEYCODE_ENTRY(VK_RIGHT, NSRightArrowFunctionKey), - KEYCODE_ENTRY(VK_DOWN, NSDownArrowFunctionKey), - KEYCODE_ENTRY(VK_PRINTSCREEN, NSPrintScreenFunctionKey), - KEYCODE_ENTRY(VK_INSERT, NSInsertFunctionKey), - KEYCODE_ENTRY(VK_HELP, NSHelpFunctionKey), - KEYCODE_ENTRY(VK_0, '0'), - KEYCODE_ENTRY(VK_1, '1'), - KEYCODE_ENTRY(VK_2, '2'), - KEYCODE_ENTRY(VK_3, '3'), - KEYCODE_ENTRY(VK_4, '4'), - KEYCODE_ENTRY(VK_5, '5'), - KEYCODE_ENTRY(VK_6, '6'), - KEYCODE_ENTRY(VK_7, '7'), - KEYCODE_ENTRY(VK_8, '8'), - KEYCODE_ENTRY(VK_9, '9'), - KEYCODE_ENTRY(VK_SEMICOLON, ':'), - KEYCODE_ENTRY(VK_EQUALS, '='), - KEYCODE_ENTRY(VK_A, 'A'), - KEYCODE_ENTRY(VK_B, 'B'), - KEYCODE_ENTRY(VK_C, 'C'), - KEYCODE_ENTRY(VK_D, 'D'), - KEYCODE_ENTRY(VK_E, 'E'), - KEYCODE_ENTRY(VK_F, 'F'), - KEYCODE_ENTRY(VK_G, 'G'), - KEYCODE_ENTRY(VK_H, 'H'), - KEYCODE_ENTRY(VK_I, 'I'), - KEYCODE_ENTRY(VK_J, 'J'), - KEYCODE_ENTRY(VK_K, 'K'), - KEYCODE_ENTRY(VK_L, 'L'), - KEYCODE_ENTRY(VK_M, 'M'), - KEYCODE_ENTRY(VK_N, 'N'), - KEYCODE_ENTRY(VK_O, 'O'), - KEYCODE_ENTRY(VK_P, 'P'), - KEYCODE_ENTRY(VK_Q, 'Q'), - KEYCODE_ENTRY(VK_R, 'R'), - KEYCODE_ENTRY(VK_S, 'S'), - KEYCODE_ENTRY(VK_T, 'T'), - KEYCODE_ENTRY(VK_U, 'U'), - KEYCODE_ENTRY(VK_V, 'V'), - KEYCODE_ENTRY(VK_W, 'W'), - KEYCODE_ENTRY(VK_X, 'X'), - KEYCODE_ENTRY(VK_Y, 'Y'), - KEYCODE_ENTRY(VK_Z, 'Z'), - KEYCODE_ENTRY(VK_CONTEXT_MENU, NSMenuFunctionKey), - KEYCODE_ENTRY(VK_NUMPAD0, '0'), - KEYCODE_ENTRY(VK_NUMPAD1, '1'), - KEYCODE_ENTRY(VK_NUMPAD2, '2'), - KEYCODE_ENTRY(VK_NUMPAD3, '3'), - KEYCODE_ENTRY(VK_NUMPAD4, '4'), - KEYCODE_ENTRY(VK_NUMPAD5, '5'), - KEYCODE_ENTRY(VK_NUMPAD6, '6'), - KEYCODE_ENTRY(VK_NUMPAD7, '7'), - KEYCODE_ENTRY(VK_NUMPAD8, '8'), - KEYCODE_ENTRY(VK_NUMPAD9, '9'), - KEYCODE_ENTRY(VK_MULTIPLY, '*'), - KEYCODE_ENTRY(VK_ADD, '+'), - KEYCODE_ENTRY(VK_SEPARATOR, 0), - KEYCODE_ENTRY(VK_SUBTRACT, '-'), - KEYCODE_ENTRY(VK_DECIMAL, '.'), - KEYCODE_ENTRY(VK_DIVIDE, '/'), - KEYCODE_ENTRY(VK_F1, NSF1FunctionKey), - KEYCODE_ENTRY(VK_F2, NSF2FunctionKey), - KEYCODE_ENTRY(VK_F3, NSF3FunctionKey), - KEYCODE_ENTRY(VK_F4, NSF4FunctionKey), - KEYCODE_ENTRY(VK_F5, NSF5FunctionKey), - KEYCODE_ENTRY(VK_F6, NSF6FunctionKey), - KEYCODE_ENTRY(VK_F7, NSF7FunctionKey), - KEYCODE_ENTRY(VK_F8, NSF8FunctionKey), - KEYCODE_ENTRY(VK_F9, NSF9FunctionKey), - KEYCODE_ENTRY(VK_F10, NSF10FunctionKey), - KEYCODE_ENTRY(VK_F11, NSF11FunctionKey), - KEYCODE_ENTRY(VK_F12, NSF12FunctionKey), - KEYCODE_ENTRY(VK_F13, NSF13FunctionKey), - KEYCODE_ENTRY(VK_F14, NSF14FunctionKey), - KEYCODE_ENTRY(VK_F15, NSF15FunctionKey), - KEYCODE_ENTRY(VK_F16, NSF16FunctionKey), - KEYCODE_ENTRY(VK_F17, NSF17FunctionKey), - KEYCODE_ENTRY(VK_F18, NSF18FunctionKey), - KEYCODE_ENTRY(VK_F19, NSF19FunctionKey), - KEYCODE_ENTRY(VK_F20, NSF20FunctionKey), - KEYCODE_ENTRY(VK_F21, NSF21FunctionKey), - KEYCODE_ENTRY(VK_F22, NSF22FunctionKey), - KEYCODE_ENTRY(VK_F23, NSF23FunctionKey), - KEYCODE_ENTRY(VK_F24, NSF24FunctionKey), - KEYCODE_ENTRY(VK_NUM_LOCK, NSClearLineFunctionKey), - KEYCODE_ENTRY(VK_SCROLL_LOCK, NSScrollLockFunctionKey), - KEYCODE_ENTRY(VK_COMMA, ','), - KEYCODE_ENTRY(VK_PERIOD, '.'), - KEYCODE_ENTRY(VK_SLASH, '/'), - KEYCODE_ENTRY(VK_BACK_QUOTE, '`'), - KEYCODE_ENTRY(VK_OPEN_BRACKET, '['), - KEYCODE_ENTRY(VK_BACK_SLASH, '\\'), - KEYCODE_ENTRY(VK_CLOSE_BRACKET, ']'), - KEYCODE_ENTRY(VK_QUOTE, '\'') - -#undef KEYCODE_ENTRY - -}; - -uint32_t -nsCocoaUtils::ConvertGeckoNameToMacCharCode(const nsAString& aKeyCodeName) -{ - if (aKeyCodeName.IsEmpty()) { - return 0; - } - - nsAutoCString keyCodeName; - keyCodeName.AssignWithConversion(aKeyCodeName); - // We want case-insensitive comparison with data stored as uppercase. - ToUpperCase(keyCodeName); - - uint32_t keyCodeNameLength = keyCodeName.Length(); - const char* keyCodeNameStr = keyCodeName.get(); - for (uint16_t i = 0; i < ArrayLength(gKeyConversions); ++i) { - if (keyCodeNameLength == gKeyConversions[i].strLength && - nsCRT::strcmp(gKeyConversions[i].str, keyCodeNameStr) == 0) { - return gKeyConversions[i].charCode; - } - } - - return 0; -} - -uint32_t -nsCocoaUtils::ConvertGeckoKeyCodeToMacCharCode(uint32_t aKeyCode) -{ - if (!aKeyCode) { - return 0; - } - - for (uint16_t i = 0; i < ArrayLength(gKeyConversions); ++i) { - if (gKeyConversions[i].geckoKeyCode == aKeyCode) { - return gKeyConversions[i].charCode; - } - } - - return 0; -} - -NSMutableAttributedString* -nsCocoaUtils::GetNSMutableAttributedString( - const nsAString& aText, - const nsTArray<mozilla::FontRange>& aFontRanges, - const bool aIsVertical, - const CGFloat aBackingScaleFactor) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL - - NSString* nsstr = nsCocoaUtils::ToNSString(aText); - NSMutableAttributedString* attrStr = - [[[NSMutableAttributedString alloc] initWithString:nsstr - attributes:nil] autorelease]; - - int32_t lastOffset = aText.Length(); - for (auto i = aFontRanges.Length(); i > 0; --i) { - const FontRange& fontRange = aFontRanges[i - 1]; - NSString* fontName = nsCocoaUtils::ToNSString(fontRange.mFontName); - CGFloat fontSize = fontRange.mFontSize / aBackingScaleFactor; - NSFont* font = [NSFont fontWithName:fontName size:fontSize]; - if (!font) { - font = [NSFont systemFontOfSize:fontSize]; - } - - NSDictionary* attrs = @{ NSFontAttributeName: font }; - NSRange range = NSMakeRange(fontRange.mStartOffset, - lastOffset - fontRange.mStartOffset); - [attrStr setAttributes:attrs range:range]; - lastOffset = fontRange.mStartOffset; - } - - if (aIsVertical) { - [attrStr addAttribute:NSVerticalGlyphFormAttributeName - value:[NSNumber numberWithInt: 1] - range:NSMakeRange(0, [attrStr length])]; - } - - return attrStr; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL -} diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h deleted file mode 100644 index 1913696b8c..0000000000 --- a/widget/cocoa/nsCocoaWindow.h +++ /dev/null @@ -1,426 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; 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 nsCocoaWindow_h_ -#define nsCocoaWindow_h_ - -#undef DARWIN - -#import <Cocoa/Cocoa.h> - -#include "mozilla/RefPtr.h" -#include "nsBaseWidget.h" -#include "nsPIWidgetCocoa.h" -#include "nsCocoaUtils.h" - -class nsCocoaWindow; -class nsChildView; -class nsMenuBarX; -@class ChildView; - -typedef struct _nsCocoaWindowList { - _nsCocoaWindowList() : prev(nullptr), window(nullptr) {} - struct _nsCocoaWindowList *prev; - nsCocoaWindow *window; // Weak -} nsCocoaWindowList; - -// NSWindow subclass that is the base class for all of our own window classes. -// Among other things, this class handles the storage of those settings that -// need to be persisted across window destruction and reconstruction, i.e. when -// switching to and from fullscreen mode. -// We don't save shadow, transparency mode or background color because it's not -// worth the hassle - Gecko will reset them anyway as soon as the window is -// resized. -@interface BaseWindow : NSWindow -{ - // Data Storage - NSMutableDictionary* mState; - BOOL mDrawsIntoWindowFrame; - NSColor* mActiveTitlebarColor; - NSColor* mInactiveTitlebarColor; - - // Shadow - BOOL mScheduledShadowInvalidation; - - // Invalidation disabling - BOOL mDisabledNeedsDisplay; - - // DPI cache. Getting the physical screen size (CGDisplayScreenSize) - // is ridiculously slow, so we cache it in the toplevel window for all - // descendants to use. - float mDPI; - - NSTrackingArea* mTrackingArea; - - NSRect mDirtyRect; - - BOOL mBeingShown; - BOOL mDrawTitle; - BOOL mBrightTitlebarForeground; - BOOL mUseMenuStyle; -} - -- (void)importState:(NSDictionary*)aState; -- (NSMutableDictionary*)exportState; -- (void)setDrawsContentsIntoWindowFrame:(BOOL)aState; -- (BOOL)drawsContentsIntoWindowFrame; -- (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive; -- (NSColor*)titlebarColorForActiveWindow:(BOOL)aActive; - -- (void)deferredInvalidateShadow; -- (void)invalidateShadow; -- (float)getDPI; - -- (void)mouseEntered:(NSEvent*)aEvent; -- (void)mouseExited:(NSEvent*)aEvent; -- (void)mouseMoved:(NSEvent*)aEvent; -- (void)updateTrackingArea; -- (NSView*)trackingAreaView; - -- (void)setBeingShown:(BOOL)aValue; -- (BOOL)isBeingShown; -- (BOOL)isVisibleOrBeingShown; - -- (ChildView*)mainChildView; - -- (NSArray*)titlebarControls; - -- (void)setWantsTitleDrawn:(BOOL)aDrawTitle; -- (BOOL)wantsTitleDrawn; - -- (void)setUseBrightTitlebarForeground:(BOOL)aBrightForeground; -- (BOOL)useBrightTitlebarForeground; - -- (void)disableSetNeedsDisplay; -- (void)enableSetNeedsDisplay; - -- (NSRect)getAndResetNativeDirtyRect; - -- (void)setUseMenuStyle:(BOOL)aValue; - -@end - -@interface NSWindow (Undocumented) - -// If a window has been explicitly removed from the "window cache" (to -// deactivate it), it's sometimes necessary to "reset" it to reactivate it -// (and put it back in the "window cache"). One way to do this, which Apple -// often uses, is to set the "window number" to '-1' and then back to its -// original value. -- (void)_setWindowNumber:(NSInteger)aNumber; - -// If we set the window's stylemask to be textured, the corners on the bottom of -// the window are rounded by default. We use this private method to make -// the corners square again, a la Safari. Starting with 10.7, all windows have -// rounded bottom corners, so this call doesn't have any effect there. -- (void)setBottomCornerRounded:(BOOL)rounded; -- (BOOL)bottomCornerRounded; - -// Present in the same form on OS X since at least OS X 10.5. -- (NSRect)contentRectForFrameRect:(NSRect)windowFrame styleMask:(NSUInteger)windowStyle; -- (NSRect)frameRectForContentRect:(NSRect)windowContentRect styleMask:(NSUInteger)windowStyle; - -// Present since at least OS X 10.5. The OS calls this method on NSWindow -// (and its subclasses) to find out which NSFrameView subclass to instantiate -// to create its "frame view". -+ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; - -@end - -@interface PopupWindow : BaseWindow -{ -@private - BOOL mIsContextMenu; -} - -- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask - backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation; -- (BOOL)isContextMenu; -- (void)setIsContextMenu:(BOOL)flag; -- (BOOL)canBecomeMainWindow; - -@end - -@interface BorderlessWindow : BaseWindow -{ -} - -- (BOOL)canBecomeKeyWindow; -- (BOOL)canBecomeMainWindow; - -@end - -@interface WindowDelegate : NSObject <NSWindowDelegate> -{ - nsCocoaWindow* mGeckoWindow; // [WEAK] (we are owned by the window) - // Used to avoid duplication when we send NS_ACTIVATE and - // NS_DEACTIVATE to Gecko for toplevel widgets. Starts out - // false. - bool mToplevelActiveState; - BOOL mHasEverBeenZoomed; -} -+ (void)paintMenubarForWindow:(NSWindow*)aWindow; -- (id)initWithGeckoWindow:(nsCocoaWindow*)geckoWind; -- (void)windowDidResize:(NSNotification*)aNotification; -- (nsCocoaWindow*)geckoWidget; -- (bool)toplevelActiveState; -- (void)sendToplevelActivateEvents; -- (void)sendToplevelDeactivateEvents; -@end - -@class ToolbarWindow; - -// NSColor subclass that allows us to draw separate colors both in the titlebar -// and for background of the window. -@interface TitlebarAndBackgroundColor : NSColor -{ - ToolbarWindow *mWindow; // [WEAK] (we are owned by the window) -} - -- (id)initWithWindow:(ToolbarWindow*)aWindow; - -@end - -// NSWindow subclass for handling windows with toolbars. -@interface ToolbarWindow : BaseWindow -{ - TitlebarAndBackgroundColor *mColor; // strong - CGFloat mUnifiedToolbarHeight; - NSColor *mBackgroundColor; // strong - NSView *mTitlebarView; // strong - NSRect mWindowButtonsRect; - NSRect mFullScreenButtonRect; -} -// Pass nil here to get the default appearance. -- (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive; -- (void)setUnifiedToolbarHeight:(CGFloat)aHeight; -- (CGFloat)unifiedToolbarHeight; -- (CGFloat)titlebarHeight; -- (NSRect)titlebarRect; -- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect sync:(BOOL)aSync; -- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect; -- (void)setDrawsContentsIntoWindowFrame:(BOOL)aState; -- (void)setSheetAttachmentPosition:(CGFloat)aY; -- (void)placeWindowButtons:(NSRect)aRect; -- (void)placeFullScreenButton:(NSRect)aRect; -- (NSPoint)windowButtonsPositionWithDefaultPosition:(NSPoint)aDefaultPosition; -- (NSPoint)fullScreenButtonPositionWithDefaultPosition:(NSPoint)aDefaultPosition; -- (void)setTemporaryBackgroundColor; -- (void)restoreBackgroundColor; -@end - -class nsCocoaWindow : public nsBaseWidget, public nsPIWidgetCocoa -{ -private: - typedef nsBaseWidget Inherited; - -public: - - nsCocoaWindow(); - - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSPIWIDGETCOCOA - - virtual MOZ_MUST_USE nsresult Create(nsIWidget* aParent, - nsNativeWidget aNativeParent, - const DesktopIntRect& aRect, - nsWidgetInitData* aInitData = nullptr) - override; - - virtual MOZ_MUST_USE nsresult Create(nsIWidget* aParent, - nsNativeWidget aNativeParent, - const LayoutDeviceIntRect& aRect, - nsWidgetInitData* aInitData = nullptr) - override; - - virtual void Destroy() override; - - NS_IMETHOD Show(bool aState) override; - virtual nsIWidget* GetSheetWindowParent(void) override; - NS_IMETHOD Enable(bool aState) override; - virtual bool IsEnabled() const override; - virtual void SetModal(bool aState) override; - virtual void SetFakeModal(bool aState) override; - virtual bool IsRunningAppModal() override; - virtual bool IsVisible() const override; - NS_IMETHOD SetFocus(bool aState=false) override; - virtual LayoutDeviceIntPoint WidgetToScreenOffset() override; - virtual LayoutDeviceIntPoint GetClientOffset() override; - virtual LayoutDeviceIntSize - ClientToWindowSize(const LayoutDeviceIntSize& aClientSize) override; - - virtual void* GetNativeData(uint32_t aDataType) override; - - virtual void ConstrainPosition(bool aAllowSlop, - int32_t *aX, int32_t *aY) override; - virtual void SetSizeConstraints(const SizeConstraints& aConstraints) override; - NS_IMETHOD Move(double aX, double aY) override; - virtual void SetSizeMode(nsSizeMode aMode) override; - NS_IMETHOD HideWindowChrome(bool aShouldHide) override; - - void EnteredFullScreen(bool aFullScreen, bool aNativeMode = true); - virtual bool PrepareForFullscreenTransition(nsISupports** aData) override; - virtual void PerformFullscreenTransition(FullscreenTransitionStage aStage, - uint16_t aDuration, - nsISupports* aData, - nsIRunnable* aCallback) override; - virtual nsresult MakeFullScreen( - bool aFullScreen, nsIScreen* aTargetScreen = nullptr) override final; - NS_IMETHOD MakeFullScreenWithNativeTransition( - bool aFullScreen, nsIScreen* aTargetScreen = nullptr) override final; - NSAnimation* FullscreenTransitionAnimation() const { return mFullscreenTransitionAnimation; } - void ReleaseFullscreenTransitionAnimation() - { - MOZ_ASSERT(mFullscreenTransitionAnimation, - "Should only be called when there is animation"); - [mFullscreenTransitionAnimation release]; - mFullscreenTransitionAnimation = nil; - } - - NS_IMETHOD Resize(double aWidth, double aHeight, bool aRepaint) override; - NS_IMETHOD Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) override; - virtual LayoutDeviceIntRect GetClientBounds() override; - virtual LayoutDeviceIntRect GetScreenBounds() override; - void ReportMoveEvent(); - void ReportSizeEvent(); - NS_IMETHOD SetCursor(nsCursor aCursor) override; - NS_IMETHOD SetCursor(imgIContainer* aCursor, uint32_t aHotspotX, uint32_t aHotspotY) override; - - CGFloat BackingScaleFactor(); - void BackingScaleFactorChanged(); - virtual double GetDefaultScaleInternal() override; - virtual int32_t RoundsWidgetCoordinatesTo() override; - - mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() final { - return mozilla::DesktopToLayoutDeviceScale(BackingScaleFactor()); - } - - NS_IMETHOD SetTitle(const nsAString& aTitle) override; - - NS_IMETHOD Invalidate(const LayoutDeviceIntRect& aRect) override; - virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations) override; - virtual LayerManager* GetLayerManager(PLayerTransactionChild* aShadowManager = nullptr, - LayersBackend aBackendHint = mozilla::layers::LayersBackend::LAYERS_NONE, - LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT) override; - NS_IMETHOD DispatchEvent(mozilla::WidgetGUIEvent* aEvent, - nsEventStatus& aStatus) override; - virtual void CaptureRollupEvents(nsIRollupListener * aListener, - bool aDoCapture) override; - NS_IMETHOD GetAttention(int32_t aCycleCount) override; - virtual bool HasPendingInputEvent() override; - virtual nsTransparencyMode GetTransparencyMode() override; - virtual void SetTransparencyMode(nsTransparencyMode aMode) override; - virtual void SetWindowShadowStyle(int32_t aStyle) override; - virtual void SetShowsToolbarButton(bool aShow) override; - virtual void SetShowsFullScreenButton(bool aShow) override; - virtual void SetWindowAnimationType(WindowAnimationType aType) override; - virtual void SetDrawsTitle(bool aDrawTitle) override; - virtual void SetUseBrightTitlebarForeground(bool aBrightForeground) override; - NS_IMETHOD SetNonClientMargins(LayoutDeviceIntMargin& aMargins) override; - virtual void SetWindowTitlebarColor(nscolor aColor, bool aActive) override; - virtual void SetDrawsInTitlebar(bool aState) override; - virtual void UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) override; - virtual nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint, - uint32_t aNativeMessage, - uint32_t aModifierFlags, - nsIObserver* aObserver) override; - - void DispatchSizeModeEvent(); - - // be notified that a some form of drag event needs to go into Gecko - virtual bool DragEvent(unsigned int aMessage, mozilla::gfx::Point aMouseGlobal, UInt16 aKeyModifiers); - - bool HasModalDescendents() { return mNumModalDescendents > 0; } - NSWindow *GetCocoaWindow() { return mWindow; } - - void SetMenuBar(nsMenuBarX* aMenuBar); - nsMenuBarX *GetMenuBar(); - - NS_IMETHOD_(void) SetInputContext( - const InputContext& aContext, - const InputContextAction& aAction) override; - NS_IMETHOD_(InputContext) GetInputContext() override - { - return mInputContext; - } - NS_IMETHOD_(bool) ExecuteNativeKeyBinding( - NativeKeyBindingsType aType, - const mozilla::WidgetKeyboardEvent& aEvent, - DoCommandCallback aCallback, - void* aCallbackData) override; - - void SetPopupWindowLevel(); - -protected: - virtual ~nsCocoaWindow(); - - nsresult CreateNativeWindow(const NSRect &aRect, - nsBorderStyle aBorderStyle, - bool aRectIsFrameRect); - nsresult CreatePopupContentView(const LayoutDeviceIntRect &aRect, - nsWidgetInitData* aInitData); - void DestroyNativeWindow(); - void AdjustWindowShadow(); - void SetWindowBackgroundBlur(); - void UpdateBounds(); - - nsresult DoResize(double aX, double aY, double aWidth, double aHeight, - bool aRepaint, bool aConstrainToCurrentScreen); - - inline bool ShouldToggleNativeFullscreen(bool aFullScreen, - bool aUseSystemTransition); - nsresult DoMakeFullScreen(bool aFullScreen, bool aUseSystemTransition); - - virtual already_AddRefed<nsIWidget> - AllocateChildPopupWidget() override - { - static NS_DEFINE_IID(kCPopUpCID, NS_POPUP_CID); - nsCOMPtr<nsIWidget> widget = do_CreateInstance(kCPopUpCID); - return widget.forget(); - } - - nsIWidget* mParent; // if we're a popup, this is our parent [WEAK] - nsIWidget* mAncestorLink; // link to traverse ancestors [WEAK] - BaseWindow* mWindow; // our cocoa window [STRONG] - WindowDelegate* mDelegate; // our delegate for processing window msgs [STRONG] - RefPtr<nsMenuBarX> mMenuBar; - NSWindow* mSheetWindowParent; // if this is a sheet, this is the NSWindow it's attached to - nsChildView* mPopupContentView; // if this is a popup, this is its content widget - // if this is a toplevel window, and there is any ongoing fullscreen - // transition, it is the animation object. - NSAnimation* mFullscreenTransitionAnimation; - int32_t mShadowStyle; - - CGFloat mBackingScaleFactor; - - WindowAnimationType mAnimationType; - - bool mWindowMadeHere; // true if we created the window, false for embedding - bool mSheetNeedsShow; // if this is a sheet, are we waiting to be shown? - // this is used for sibling sheet contention only - bool mInFullScreenMode; - bool mInFullScreenTransition; // true from the request to enter/exit fullscreen - // (MakeFullScreen() call) to EnteredFullScreen() - bool mModal; - bool mFakeModal; - - // Only true on 10.7+ if SetShowsFullScreenButton(true) is called. - bool mSupportsNativeFullScreen; - // Whether we are currently using native fullscreen. It could be false because - // we are in the DOM fullscreen where we do not use the native fullscreen. - bool mInNativeFullScreenMode; - - bool mIsAnimationSuppressed; - - bool mInReportMoveEvent; // true if in a call to ReportMoveEvent(). - bool mInResize; // true if in a call to DoResize(). - - bool mAlwaysOnTop; - - int32_t mNumModalDescendents; - InputContext mInputContext; -}; - -#endif // nsCocoaWindow_h_ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm deleted file mode 100644 index a437504fd1..0000000000 --- a/widget/cocoa/nsCocoaWindow.mm +++ /dev/null @@ -1,3881 +0,0 @@ -/* -*- Mode: Objective-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/. */ - -#include "nsCocoaWindow.h" - -#include "NativeKeyBindings.h" -#include "TextInputHandler.h" -#include "nsObjCExceptions.h" -#include "nsCOMPtr.h" -#include "nsWidgetsCID.h" -#include "nsIRollupListener.h" -#include "nsChildView.h" -#include "nsWindowMap.h" -#include "nsAppShell.h" -#include "nsIAppShellService.h" -#include "nsIBaseWindow.h" -#include "nsIInterfaceRequestorUtils.h" -#include "nsIXULWindow.h" -#include "nsToolkit.h" -#include "nsIDOMWindow.h" -#include "nsPIDOMWindow.h" -#include "nsIDOMElement.h" -#include "nsThreadUtils.h" -#include "nsMenuBarX.h" -#include "nsMenuUtilsX.h" -#include "nsStyleConsts.h" -#include "nsNativeThemeColors.h" -#include "nsNativeThemeCocoa.h" -#include "nsChildView.h" -#include "nsCocoaFeatures.h" -#include "nsIScreenManager.h" -#include "nsIWidgetListener.h" -#include "nsIPresShell.h" -#include "nsScreenCocoa.h" -#include "VibrancyManager.h" - -#include "gfxPlatform.h" -#include "qcms.h" - -#include "mozilla/AutoRestore.h" -#include "mozilla/BasicEvents.h" -#include "mozilla/Preferences.h" -#include <algorithm> - -namespace mozilla { -namespace layers { -class LayerManager; -} // namespace layers -} // namespace mozilla -using namespace mozilla::layers; -using namespace mozilla::widget; -using namespace mozilla; - -int32_t gXULModalLevel = 0; - -// In principle there should be only one app-modal window at any given time. -// But sometimes, despite our best efforts, another window appears above the -// current app-modal window. So we need to keep a linked list of app-modal -// windows. (A non-sheet window that appears above an app-modal window is -// also made app-modal.) See nsCocoaWindow::SetModal(). -nsCocoaWindowList *gGeckoAppModalWindowList = NULL; - -// defined in nsMenuBarX.mm -extern NSMenu* sApplicationMenu; // Application menu shared by all menubars - -// defined in nsChildView.mm -extern BOOL gSomeMenuBarPainted; - -#if !defined(MAC_OS_X_VERSION_10_12) || \ - MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 - -@interface NSWindow(AutomaticWindowTabbing) -+ (void)setAllowsAutomaticWindowTabbing:(BOOL)allow; -@end - -#endif - -extern "C" { - // CGSPrivate.h - typedef NSInteger CGSConnection; - typedef NSInteger CGSWindow; - typedef NSUInteger CGSWindowFilterRef; - extern CGSConnection _CGSDefaultConnection(void); - extern CGError CGSSetWindowShadowAndRimParameters(const CGSConnection cid, CGSWindow wid, float standardDeviation, float density, int offsetX, int offsetY, unsigned int flags); - extern CGError CGSSetWindowBackgroundBlurRadius(CGSConnection cid, CGSWindow wid, NSUInteger blur); -} - -#define NS_APPSHELLSERVICE_CONTRACTID "@mozilla.org/appshell/appShellService;1" - -NS_IMPL_ISUPPORTS_INHERITED(nsCocoaWindow, Inherited, nsPIWidgetCocoa) - -// A note on testing to see if your object is a sheet... -// |mWindowType == eWindowType_sheet| is true if your gecko nsIWidget is a sheet -// widget - whether or not the sheet is showing. |[mWindow isSheet]| will return -// true *only when the sheet is actually showing*. Choose your test wisely. - -static void RollUpPopups() -{ - nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); - NS_ENSURE_TRUE_VOID(rollupListener); - nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget(); - if (!rollupWidget) - return; - rollupListener->Rollup(0, true, nullptr, nullptr); -} - -nsCocoaWindow::nsCocoaWindow() -: mParent(nullptr) -, mAncestorLink(nullptr) -, mWindow(nil) -, mDelegate(nil) -, mSheetWindowParent(nil) -, mPopupContentView(nil) -, mFullscreenTransitionAnimation(nil) -, mShadowStyle(NS_STYLE_WINDOW_SHADOW_DEFAULT) -, mBackingScaleFactor(0.0) -, mAnimationType(nsIWidget::eGenericWindowAnimation) -, mWindowMadeHere(false) -, mSheetNeedsShow(false) -, mInFullScreenMode(false) -, mInFullScreenTransition(false) -, mModal(false) -, mFakeModal(false) -, mSupportsNativeFullScreen(false) -, mInNativeFullScreenMode(false) -, mIsAnimationSuppressed(false) -, mInReportMoveEvent(false) -, mInResize(false) -, mAlwaysOnTop(false) -, mNumModalDescendents(0) -{ - if ([NSWindow respondsToSelector:@selector(setAllowsAutomaticWindowTabbing:)]) { - // Disable automatic tabbing on 10.12. We need to do this before we - // orderFront any of our windows. - [NSWindow setAllowsAutomaticWindowTabbing:NO]; - } -} - -void nsCocoaWindow::DestroyNativeWindow() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mWindow) - return; - - // We want to unhook the delegate here because we don't want events - // sent to it after this object has been destroyed. - [mWindow setDelegate:nil]; - [mWindow close]; - mWindow = nil; - [mDelegate autorelease]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -nsCocoaWindow::~nsCocoaWindow() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Notify the children that we're gone. Popup windows (e.g. tooltips) can - // have nsChildView children. 'kid' is an nsChildView object if and only if - // its 'type' is 'eWindowType_child'. - // childView->ResetParent() can change our list of children while it's - // being iterated, so the way we iterate the list must allow for this. - for (nsIWidget* kid = mLastChild; kid;) { - nsWindowType kidType = kid->WindowType(); - if (kidType == eWindowType_child) { - nsChildView* childView = static_cast<nsChildView*>(kid); - kid = kid->GetPrevSibling(); - childView->ResetParent(); - } else { - nsCocoaWindow* childWindow = static_cast<nsCocoaWindow*>(kid); - childWindow->mParent = nullptr; - childWindow->mAncestorLink = mAncestorLink; - kid = kid->GetPrevSibling(); - } - } - - if (mWindow && mWindowMadeHere) { - DestroyNativeWindow(); - } - - NS_IF_RELEASE(mPopupContentView); - - // Deal with the possiblity that we're being destroyed while running modal. - if (mModal) { - NS_WARNING("Widget destroyed while running modal!"); - --gXULModalLevel; - NS_ASSERTION(gXULModalLevel >= 0, "Weirdness setting modality!"); - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// Find the screen that overlaps aRect the most, -// if none are found default to the mainScreen. -static NSScreen* -FindTargetScreenForRect(const DesktopIntRect& aRect) -{ - NSScreen *targetScreen = [NSScreen mainScreen]; - NSEnumerator *screenEnum = [[NSScreen screens] objectEnumerator]; - int largestIntersectArea = 0; - while (NSScreen *screen = [screenEnum nextObject]) { - DesktopIntRect screenRect = - nsCocoaUtils::CocoaRectToGeckoRect([screen visibleFrame]); - screenRect = screenRect.Intersect(aRect); - int area = screenRect.width * screenRect.height; - if (area > largestIntersectArea) { - largestIntersectArea = area; - targetScreen = screen; - } - } - return targetScreen; -} - -// fits the rect to the screen that contains the largest area of it, -// or to aScreen if a screen is passed in -// NB: this operates with aRect in desktop pixels -static void -FitRectToVisibleAreaForScreen(DesktopIntRect& aRect, NSScreen* aScreen) -{ - if (!aScreen) { - aScreen = FindTargetScreenForRect(aRect); - } - - DesktopIntRect screenBounds = - nsCocoaUtils::CocoaRectToGeckoRect([aScreen visibleFrame]); - - if (aRect.width > screenBounds.width) { - aRect.width = screenBounds.width; - } - if (aRect.height > screenBounds.height) { - aRect.height = screenBounds.height; - } - - if (aRect.x - screenBounds.x + aRect.width > screenBounds.width) { - aRect.x += screenBounds.width - (aRect.x - screenBounds.x + aRect.width); - } - if (aRect.y - screenBounds.y + aRect.height > screenBounds.height) { - aRect.y += screenBounds.height - (aRect.y - screenBounds.y + aRect.height); - } - - // If the left/top edge of the window is off the screen in either direction, - // then set the window to start at the left/top edge of the screen. - if (aRect.x < screenBounds.x || aRect.x > (screenBounds.x + screenBounds.width)) { - aRect.x = screenBounds.x; - } - if (aRect.y < screenBounds.y || aRect.y > (screenBounds.y + screenBounds.height)) { - aRect.y = screenBounds.y; - } -} - -// Some applications use native popup windows -// (native context menus, native tooltips) -static bool UseNativePopupWindows() -{ -#ifdef MOZ_USE_NATIVE_POPUP_WINDOWS - return true; -#else - return false; -#endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */ -} - -// aRect here is specified in desktop pixels -nsresult -nsCocoaWindow::Create(nsIWidget* aParent, - nsNativeWidget aNativeParent, - const DesktopIntRect& aRect, - nsWidgetInitData* aInitData) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // Because the hidden window is created outside of an event loop, - // we have to provide an autorelease pool (see bug 559075). - nsAutoreleasePool localPool; - - DesktopIntRect newBounds = aRect; - FitRectToVisibleAreaForScreen(newBounds, nullptr); - - // Set defaults which can be overriden from aInitData in BaseCreate - mWindowType = eWindowType_toplevel; - mBorderStyle = eBorderStyle_default; - - // Ensure that the toolkit is created. - nsToolkit::GetToolkit(); - - Inherited::BaseCreate(aParent, aInitData); - - mParent = aParent; - mAncestorLink = aParent; - - // Applications that use native popups don't want us to create popup windows. - if ((mWindowType == eWindowType_popup) && UseNativePopupWindows()) - return NS_OK; - - nsresult rv = - CreateNativeWindow(nsCocoaUtils::GeckoRectToCocoaRect(newBounds), - mBorderStyle, false); - NS_ENSURE_SUCCESS(rv, rv); - - if (mWindowType == eWindowType_popup) { - if (aInitData->mMouseTransparent) { - [mWindow setIgnoresMouseEvents:YES]; - } else { - [mWindow setIgnoresMouseEvents:NO]; - } - // now we can convert newBounds to device pixels for the window we created, - // as the child view expects a rect expressed in the dev pix of its parent - LayoutDeviceIntRect devRect = - RoundedToInt(newBounds * GetDesktopToDeviceScale()); - return CreatePopupContentView(devRect, aInitData); - } - - mIsAnimationSuppressed = aInitData->mIsAnimationSuppressed; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -nsresult -nsCocoaWindow::Create(nsIWidget* aParent, - nsNativeWidget aNativeParent, - const LayoutDeviceIntRect& aRect, - nsWidgetInitData* aInitData) -{ - DesktopIntRect desktopRect = - RoundedToInt(aRect / GetDesktopToDeviceScale()); - return Create(aParent, aNativeParent, desktopRect, aInitData); -} - -static unsigned int WindowMaskForBorderStyle(nsBorderStyle aBorderStyle) -{ - bool allOrDefault = (aBorderStyle == eBorderStyle_all || - aBorderStyle == eBorderStyle_default); - - /* Apple's docs on NSWindow styles say that "a window's style mask should - * include NSTitledWindowMask if it includes any of the others [besides - * NSBorderlessWindowMask]". This implies that a borderless window - * shouldn't have any other styles than NSBorderlessWindowMask. - */ - if (!allOrDefault && !(aBorderStyle & eBorderStyle_title)) - return NSBorderlessWindowMask; - - unsigned int mask = NSTitledWindowMask; - if (allOrDefault || aBorderStyle & eBorderStyle_close) - mask |= NSClosableWindowMask; - if (allOrDefault || aBorderStyle & eBorderStyle_minimize) - mask |= NSMiniaturizableWindowMask; - if (allOrDefault || aBorderStyle & eBorderStyle_resizeh) - mask |= NSResizableWindowMask; - - return mask; -} - -// If aRectIsFrameRect, aRect specifies the frame rect of the new window. -// Otherwise, aRect.x/y specify the position of the window's frame relative to -// the bottom of the menubar and aRect.width/height specify the size of the -// content rect. -nsresult nsCocoaWindow::CreateNativeWindow(const NSRect &aRect, - nsBorderStyle aBorderStyle, - bool aRectIsFrameRect) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // We default to NSBorderlessWindowMask, add features if needed. - unsigned int features = NSBorderlessWindowMask; - - // Configure the window we will create based on the window type. - switch (mWindowType) - { - case eWindowType_invisible: - case eWindowType_child: - case eWindowType_plugin: - break; - case eWindowType_popup: - if (aBorderStyle != eBorderStyle_default && mBorderStyle & eBorderStyle_title) { - features |= NSTitledWindowMask; - if (aBorderStyle & eBorderStyle_close) { - features |= NSClosableWindowMask; - } - } - break; - case eWindowType_toplevel: - case eWindowType_dialog: - features = WindowMaskForBorderStyle(aBorderStyle); - break; - case eWindowType_sheet: - if (mParent->WindowType() != eWindowType_invisible && - aBorderStyle & eBorderStyle_resizeh) { - features = NSResizableWindowMask; - } - else { - features = NSMiniaturizableWindowMask; - } - features |= NSTitledWindowMask; - break; - default: - NS_ERROR("Unhandled window type!"); - return NS_ERROR_FAILURE; - } - - NSRect contentRect; - - if (aRectIsFrameRect) { - contentRect = [NSWindow contentRectForFrameRect:aRect styleMask:features]; - } else { - /* - * We pass a content area rect to initialize the native Cocoa window. The - * content rect we give is the same size as the size we're given by gecko. - * The origin we're given for non-popup windows is moved down by the height - * of the menu bar so that an origin of (0,100) from gecko puts the window - * 100 pixels below the top of the available desktop area. We also move the - * origin down by the height of a title bar if it exists. This is so the - * origin that gecko gives us for the top-left of the window turns out to - * be the top-left of the window we create. This is how it was done in - * Carbon. If it ought to be different we'll probably need to look at all - * the callers. - * - * Note: This means that if you put a secondary screen on top of your main - * screen and open a window in the top screen, it'll be incorrectly shifted - * down by the height of the menu bar. Same thing would happen in Carbon. - * - * Note: If you pass a rect with 0,0 for an origin, the window ends up in a - * weird place for some reason. This stops that without breaking popups. - */ - // Compensate for difference between frame and content area height (e.g. title bar). - NSRect newWindowFrame = [NSWindow frameRectForContentRect:aRect styleMask:features]; - - contentRect = aRect; - contentRect.origin.y -= (newWindowFrame.size.height - aRect.size.height); - - if (mWindowType != eWindowType_popup) - contentRect.origin.y -= [[NSApp mainMenu] menuBarHeight]; - } - - // NSLog(@"Top-level window being created at Cocoa rect: %f, %f, %f, %f\n", - // rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); - - Class windowClass = [BaseWindow class]; - // If we have a titlebar on a top-level window, we want to be able to control the - // titlebar color (for unified windows), so use the special ToolbarWindow class. - // Note that we need to check the window type because we mark sheets as - // having titlebars. - if ((mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) && - (features & NSTitledWindowMask)) - windowClass = [ToolbarWindow class]; - // If we're a popup window we need to use the PopupWindow class. - else if (mWindowType == eWindowType_popup) - windowClass = [PopupWindow class]; - // If we're a non-popup borderless window we need to use the - // BorderlessWindow class. - else if (features == NSBorderlessWindowMask) - windowClass = [BorderlessWindow class]; - - // Create the window - mWindow = [[windowClass alloc] initWithContentRect:contentRect styleMask:features - backing:NSBackingStoreBuffered defer:YES]; - - // Make sure that window titles don't leak to disk in private browsing mode - // due to macOS' resume feature. - [mWindow setRestorable:NO]; - [mWindow disableSnapshotRestoration]; - - // setup our notification delegate. Note that setDelegate: does NOT retain. - mDelegate = [[WindowDelegate alloc] initWithGeckoWindow:this]; - [mWindow setDelegate:mDelegate]; - - // Make sure that the content rect we gave has been honored. - NSRect wantedFrame = [mWindow frameRectForContentRect:contentRect]; - if (!NSEqualRects([mWindow frame], wantedFrame)) { - // This can happen when the window is not on the primary screen. - [mWindow setFrame:wantedFrame display:NO]; - } - UpdateBounds(); - - if (mWindowType == eWindowType_invisible) { - [mWindow setLevel:kCGDesktopWindowLevelKey]; - } - - if (mWindowType == eWindowType_popup) { - SetPopupWindowLevel(); - [mWindow setBackgroundColor:[NSColor clearColor]]; - [mWindow setOpaque:NO]; - } else { - // Make sure that regular windows are opaque from the start, so that - // nsChildView::WidgetTypeSupportsAcceleration returns true for them. - [mWindow setOpaque:YES]; - } - - NSWindowCollectionBehavior newBehavior = [mWindow collectionBehavior]; - if (mAlwaysOnTop) { - [mWindow setLevel:NSFloatingWindowLevel]; - newBehavior |= NSWindowCollectionBehaviorCanJoinAllSpaces; - } - [mWindow setCollectionBehavior:newBehavior]; - - [mWindow setContentMinSize:NSMakeSize(60, 60)]; - [mWindow disableCursorRects]; - - // Make sure the window starts out not draggable by the background. - // We will turn it on as necessary. - [mWindow setMovableByWindowBackground:NO]; - - [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:mWindow]; - mWindowMadeHere = true; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsCocoaWindow::CreatePopupContentView(const LayoutDeviceIntRect &aRect, - nsWidgetInitData* aInitData) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // We need to make our content view a ChildView. - mPopupContentView = new nsChildView(); - if (!mPopupContentView) - return NS_ERROR_FAILURE; - - NS_ADDREF(mPopupContentView); - - nsIWidget* thisAsWidget = static_cast<nsIWidget*>(this); - nsresult rv = mPopupContentView->Create(thisAsWidget, nullptr, aRect, - aInitData); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - NSView* contentView = [mWindow contentView]; - ChildView* childView = (ChildView*)mPopupContentView->GetNativeData(NS_NATIVE_WIDGET); - [childView setFrame:NSZeroRect]; - [childView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [contentView addSubview:childView]; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -void nsCocoaWindow::Destroy() -{ - if (mOnDestroyCalled) - return; - mOnDestroyCalled = true; - - // SetFakeModal(true) is called for non-modal window opened by modal window. - // On Cocoa, it needs corresponding SetFakeModal(false) on destroy to restore - // ancestor windows' state. - if (mFakeModal) { - SetFakeModal(false); - } - - // If we don't hide here we run into problems with panels, this is not ideal. - // (Bug 891424) - Show(false); - - if (mPopupContentView) - mPopupContentView->Destroy(); - - if (mFullscreenTransitionAnimation) { - [mFullscreenTransitionAnimation stopAnimation]; - ReleaseFullscreenTransitionAnimation(); - } - - nsBaseWidget::Destroy(); - // nsBaseWidget::Destroy() calls GetParent()->RemoveChild(this). But we - // don't implement GetParent(), so we need to do the equivalent here. - if (mParent) { - mParent->RemoveChild(this); - } - nsBaseWidget::OnDestroy(); - - if (mInFullScreenMode) { - // On Lion we don't have to mess with the OS chrome when in Full Screen - // mode. But we do have to destroy the native window here (and not wait - // for that to happen in our destructor). We don't switch away from the - // native window's space until the window is destroyed, and otherwise this - // might not happen for several seconds (because at least one object - // holding a reference to ourselves is usually waiting to be garbage- - // collected). See bug 757618. - if (mInNativeFullScreenMode) { - DestroyNativeWindow(); - } else if (mWindow) { - nsCocoaUtils::HideOSChromeOnScreen(false); - } - } -} - -nsIWidget* nsCocoaWindow::GetSheetWindowParent(void) -{ - if (mWindowType != eWindowType_sheet) - return nullptr; - nsCocoaWindow *parent = static_cast<nsCocoaWindow*>(mParent); - while (parent && (parent->mWindowType == eWindowType_sheet)) - parent = static_cast<nsCocoaWindow*>(parent->mParent); - return parent; -} - -void* nsCocoaWindow::GetNativeData(uint32_t aDataType) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL; - - void* retVal = nullptr; - - switch (aDataType) { - // to emulate how windows works, we always have to return a NSView - // for NS_NATIVE_WIDGET - case NS_NATIVE_WIDGET: - case NS_NATIVE_DISPLAY: - retVal = [mWindow contentView]; - break; - - case NS_NATIVE_WINDOW: - retVal = mWindow; - break; - - case NS_NATIVE_GRAPHIC: - // There isn't anything that makes sense to return here, - // and it doesn't matter so just return nullptr. - NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a top-level window!"); - break; - case NS_RAW_NATIVE_IME_CONTEXT: { - retVal = GetPseudoIMEContext(); - if (retVal) { - break; - } - NSView* view = mWindow ? [mWindow contentView] : nil; - if (view) { - retVal = [view inputContext]; - } - // If inputContext isn't available on this window, return this window's - // pointer instead of nullptr since if this returns nullptr, - // IMEStateManager cannot manage composition with TextComposition - // instance. Although, this case shouldn't occur. - if (NS_WARN_IF(!retVal)) { - retVal = this; - } - break; - } - } - - return retVal; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL; -} - -bool nsCocoaWindow::IsVisible() const -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - return (mWindow && ([mWindow isVisibleOrBeingShown] || mSheetNeedsShow)); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); -} - -void -nsCocoaWindow::SetModal(bool aState) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mWindow) - return; - - // This is used during startup (outside the event loop) when creating - // the add-ons compatibility checking dialog and the profile manager UI; - // therefore, it needs to provide an autorelease pool to avoid cocoa - // objects leaking. - nsAutoreleasePool localPool; - - mModal = aState; - nsCocoaWindow *ancestor = static_cast<nsCocoaWindow*>(mAncestorLink); - if (aState) { - ++gXULModalLevel; - // When a non-sheet window gets "set modal", make the window(s) that it - // appears over behave as they should. We can't rely on native methods to - // do this, for the following reason: The OS runs modal non-sheet windows - // in an event loop (using [NSApplication runModalForWindow:] or similar - // methods) that's incompatible with the modal event loop in nsXULWindow:: - // ShowModal() (each of these event loops is "exclusive", and can't run at - // the same time as other (similar) event loops). - if (mWindowType != eWindowType_sheet) { - while (ancestor) { - if (ancestor->mNumModalDescendents++ == 0) { - NSWindow *aWindow = ancestor->GetCocoaWindow(); - if (ancestor->mWindowType != eWindowType_invisible) { - [[aWindow standardWindowButton:NSWindowCloseButton] setEnabled:NO]; - [[aWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled:NO]; - [[aWindow standardWindowButton:NSWindowZoomButton] setEnabled:NO]; - } - } - ancestor = static_cast<nsCocoaWindow*>(ancestor->mParent); - } - [mWindow setLevel:NSModalPanelWindowLevel]; - nsCocoaWindowList *windowList = new nsCocoaWindowList; - if (windowList) { - windowList->window = this; // Don't ADDREF - windowList->prev = gGeckoAppModalWindowList; - gGeckoAppModalWindowList = windowList; - } - } - } - else { - --gXULModalLevel; - NS_ASSERTION(gXULModalLevel >= 0, "Mismatched call to nsCocoaWindow::SetModal(false)!"); - if (mWindowType != eWindowType_sheet) { - while (ancestor) { - if (--ancestor->mNumModalDescendents == 0) { - NSWindow *aWindow = ancestor->GetCocoaWindow(); - if (ancestor->mWindowType != eWindowType_invisible) { - [[aWindow standardWindowButton:NSWindowCloseButton] setEnabled:YES]; - [[aWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled:YES]; - [[aWindow standardWindowButton:NSWindowZoomButton] setEnabled:YES]; - } - } - NS_ASSERTION(ancestor->mNumModalDescendents >= 0, "Widget hierarchy changed while modal!"); - ancestor = static_cast<nsCocoaWindow*>(ancestor->mParent); - } - if (gGeckoAppModalWindowList) { - NS_ASSERTION(gGeckoAppModalWindowList->window == this, "Widget hierarchy changed while modal!"); - nsCocoaWindowList *saved = gGeckoAppModalWindowList; - gGeckoAppModalWindowList = gGeckoAppModalWindowList->prev; - delete saved; // "window" not ADDREFed - } - if (mWindowType == eWindowType_popup) - SetPopupWindowLevel(); - else - [mWindow setLevel:NSNormalWindowLevel]; - } - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -nsCocoaWindow::SetFakeModal(bool aState) -{ - mFakeModal = aState; - SetModal(aState); -} - -bool -nsCocoaWindow::IsRunningAppModal() -{ - return [NSApp _isRunningAppModal]; -} - -// Hide or show this window -NS_IMETHODIMP nsCocoaWindow::Show(bool bState) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (!mWindow) - return NS_OK; - - // We need to re-execute sometimes in order to bring already-visible - // windows forward. - if (!mSheetNeedsShow && !bState && ![mWindow isVisible]) - return NS_OK; - - // Protect against re-entering. - if (bState && [mWindow isBeingShown]) - return NS_OK; - - [mWindow setBeingShown:bState]; - - nsIWidget* parentWidget = mParent; - nsCOMPtr<nsPIWidgetCocoa> piParentWidget(do_QueryInterface(parentWidget)); - NSWindow* nativeParentWindow = (parentWidget) ? - (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW) : nil; - - if (bState && !mBounds.IsEmpty()) { - // Don't try to show a popup when the parent isn't visible or is minimized. - if (mWindowType == eWindowType_popup && nativeParentWindow) { - if (![nativeParentWindow isVisible] || [nativeParentWindow isMiniaturized]) { - return NS_ERROR_FAILURE; - } - } - - if (mPopupContentView) { - // Ensure our content view is visible. We never need to hide it. - mPopupContentView->Show(true); - } - - if (mWindowType == eWindowType_sheet) { - // bail if no parent window (its basically what we do in Carbon) - if (!nativeParentWindow || !piParentWidget) - return NS_ERROR_FAILURE; - - NSWindow* topNonSheetWindow = nativeParentWindow; - - // If this sheet is the child of another sheet, hide the parent so that - // this sheet can be displayed. Leave the parent mSheetNeedsShow alone, - // that is only used to handle sibling sheet contention. The parent will - // return once there are no more child sheets. - bool parentIsSheet = false; - if (NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) && - parentIsSheet) { - piParentWidget->GetSheetWindowParent(&topNonSheetWindow); - [NSApp endSheet:nativeParentWindow]; - } - - nsCOMPtr<nsIWidget> sheetShown; - if (NS_SUCCEEDED(piParentWidget->GetChildSheet( - true, getter_AddRefs(sheetShown))) && - (!sheetShown || sheetShown == this)) { - // If this sheet is already the sheet actually being shown, don't - // tell it to show again. Otherwise the number of calls to - // [NSApp beginSheet...] won't match up with [NSApp endSheet...]. - if (![mWindow isVisible]) { - mSheetNeedsShow = false; - mSheetWindowParent = topNonSheetWindow; - // Only set contextInfo if our parent isn't a sheet. - NSWindow* contextInfo = parentIsSheet ? nil : mSheetWindowParent; - [TopLevelWindowData deactivateInWindow:mSheetWindowParent]; - [NSApp beginSheet:mWindow - modalForWindow:mSheetWindowParent - modalDelegate:mDelegate - didEndSelector:@selector(didEndSheet:returnCode:contextInfo:) - contextInfo:contextInfo]; - [TopLevelWindowData activateInWindow:mWindow]; - SendSetZLevelEvent(); - } - } - else { - // A sibling of this sheet is active, don't show this sheet yet. - // When the active sheet hides, its brothers and sisters that have - // mSheetNeedsShow set will have their opportunities to display. - mSheetNeedsShow = true; - } - } - else if (mWindowType == eWindowType_popup) { - if (!nsCocoaFeatures::OnMojaveOrLater()) { - // If a popup window is shown after being hidden, it needs to be "reset" - // for it to receive any mouse events aside from mouse-moved events - // (because it was removed from the "window cache" when it was hidden - // -- see below). Setting the window number to -1 and then back to its - // original value seems to accomplish this. The idea was "borrowed" - // from the Java Embedding Plugin. This is fixed on macOS 10.14+. - NSInteger windowNumber = [mWindow windowNumber]; - [mWindow _setWindowNumber:-1]; - [mWindow _setWindowNumber:windowNumber]; - } - // For reasons that aren't yet clear, calls to [NSWindow orderFront:] or - // [NSWindow makeKeyAndOrderFront:] can sometimes trigger "Error (1000) - // creating CGSWindow", which in turn triggers an internal inconsistency - // NSException. These errors shouldn't be fatal. So we need to wrap - // calls to ...orderFront: in TRY blocks. See bmo bug 470864. - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - [[mWindow contentView] setNeedsDisplay:YES]; - [mWindow orderFront:nil]; - NS_OBJC_END_TRY_ABORT_BLOCK; - SendSetZLevelEvent(); - AdjustWindowShadow(); - SetWindowBackgroundBlur(); - // If our popup window is a non-native context menu, tell the OS (and - // other programs) that a menu has opened. This is how the OS knows to - // close other programs' context menus when ours open. - if ([mWindow isKindOfClass:[PopupWindow class]] && - [(PopupWindow*) mWindow isContextMenu]) { - [[NSDistributedNotificationCenter defaultCenter] - postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification" - object:@"org.mozilla.gecko.PopupWindow"]; - } - - // If a parent window was supplied and this is a popup at the parent - // level, set its child window. This will cause the child window to - // appear above the parent and move when the parent does. Setting this - // needs to happen after the _setWindowNumber calls above, otherwise the - // window doesn't focus properly. - if (nativeParentWindow && mPopupLevel == ePopupLevelParent) - [nativeParentWindow addChildWindow:mWindow - ordered:NSWindowAbove]; - } - else { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - if (mWindowType == eWindowType_toplevel && - [mWindow respondsToSelector:@selector(setAnimationBehavior:)]) { - NSWindowAnimationBehavior behavior; - if (mIsAnimationSuppressed) { - behavior = NSWindowAnimationBehaviorNone; - } else { - switch (mAnimationType) { - case nsIWidget::eDocumentWindowAnimation: - behavior = NSWindowAnimationBehaviorDocumentWindow; - break; - default: - NS_NOTREACHED("unexpected mAnimationType value"); - // fall through - case nsIWidget::eGenericWindowAnimation: - behavior = NSWindowAnimationBehaviorDefault; - break; - } - } - [mWindow setAnimationBehavior:behavior]; - } - [mWindow makeKeyAndOrderFront:nil]; - NS_OBJC_END_TRY_ABORT_BLOCK; - SendSetZLevelEvent(); - } - } - else { - // roll up any popups if a top-level window is going away - if (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog) - RollUpPopups(); - - // now get rid of the window/sheet - if (mWindowType == eWindowType_sheet) { - if (mSheetNeedsShow) { - // This is an attempt to hide a sheet that never had a chance to - // be shown. There's nothing to do other than make sure that it - // won't show. - mSheetNeedsShow = false; - } - else { - // get sheet's parent *before* hiding the sheet (which breaks the linkage) - NSWindow* sheetParent = mSheetWindowParent; - - // hide the sheet - [NSApp endSheet:mWindow]; - - [TopLevelWindowData deactivateInWindow:mWindow]; - - nsCOMPtr<nsIWidget> siblingSheetToShow; - bool parentIsSheet = false; - - if (nativeParentWindow && piParentWidget && - NS_SUCCEEDED(piParentWidget->GetChildSheet( - false, getter_AddRefs(siblingSheetToShow))) && - siblingSheetToShow) { - // First, give sibling sheets an opportunity to show. - siblingSheetToShow->Show(true); - } - else if (nativeParentWindow && piParentWidget && - NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) && - parentIsSheet) { - // Only set contextInfo if the parent of the parent sheet we're about - // to restore isn't itself a sheet. - NSWindow* contextInfo = sheetParent; - nsIWidget* grandparentWidget = nil; - if (NS_SUCCEEDED(piParentWidget->GetRealParent(&grandparentWidget)) && grandparentWidget) { - nsCOMPtr<nsPIWidgetCocoa> piGrandparentWidget(do_QueryInterface(grandparentWidget)); - bool grandparentIsSheet = false; - if (piGrandparentWidget && NS_SUCCEEDED(piGrandparentWidget->GetIsSheet(&grandparentIsSheet)) && - grandparentIsSheet) { - contextInfo = nil; - } - } - // If there are no sibling sheets, but the parent is a sheet, restore - // it. It wasn't sent any deactivate events when it was hidden, so - // don't call through Show, just let the OS put it back up. - [NSApp beginSheet:nativeParentWindow - modalForWindow:sheetParent - modalDelegate:[nativeParentWindow delegate] - didEndSelector:@selector(didEndSheet:returnCode:contextInfo:) - contextInfo:contextInfo]; - } - else { - // Sheet, that was hard. No more siblings or parents, going back - // to a real window. - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - [sheetParent makeKeyAndOrderFront:nil]; - NS_OBJC_END_TRY_ABORT_BLOCK; - } - SendSetZLevelEvent(); - } - } - else { - // If the window is a popup window with a parent window we need to - // unhook it here before ordering it out. When you order out the child - // of a window it hides the parent window. - if (mWindowType == eWindowType_popup && nativeParentWindow) - [nativeParentWindow removeChildWindow:mWindow]; - - [mWindow orderOut:nil]; - - if (!nsCocoaFeatures::OnMojaveOrLater()) { - // Unless it's explicitly removed from NSApp's "window cache", a popup - // window will keep receiving mouse-moved events even after it's been - // "ordered out" (instead of the browser window that was underneath it, - // until you click on that window). This is bmo bug 378645, but it's - // surely an Apple bug. The "window cache" is an undocumented - // subsystem, all of whose methods are included in the NSWindowCache - // category of the NSApplication class (in header files generated using - // class-dump). This workaround was "borrowed" from the Java Embedding - // Plugin (which uses it for a different purpose). This is fixed on - // macOS 10.14+. - if (mWindowType == eWindowType_popup) { - [NSApp _removeWindowFromCache:mWindow]; - } - } - - // If our popup window is a non-native context menu, tell the OS (and - // other programs) that a menu has closed. - if ([mWindow isKindOfClass:[PopupWindow class]] && - [(PopupWindow*) mWindow isContextMenu]) { - [[NSDistributedNotificationCenter defaultCenter] - postNotificationName:@"com.apple.HIToolbox.endMenuTrackingNotification" - object:@"org.mozilla.gecko.PopupWindow"]; - } - } - } - - [mWindow setBeingShown:NO]; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -struct ShadowParams { - float standardDeviation; - float density; - int offsetX; - int offsetY; - unsigned int flags; -}; - -// These numbers have been determined by looking at the results of -// CGSGetWindowShadowAndRimParameters for native window types. -static const ShadowParams kWindowShadowParametersPreYosemite[] = { - { 0.0f, 0.0f, 0, 0, 0 }, // none - { 8.0f, 0.5f, 0, 6, 1 }, // default - { 10.0f, 0.44f, 0, 10, 512 }, // menu - { 8.0f, 0.5f, 0, 6, 1 }, // tooltip - { 4.0f, 0.6f, 0, 4, 512 } // sheet -}; - -static const ShadowParams kWindowShadowParametersPostYosemite[] = { - { 0.0f, 0.0f, 0, 0, 0 }, // none - { 8.0f, 0.5f, 0, 6, 1 }, // default - { 9.882353f, 0.3f, 0, 4, 0 }, // menu - { 3.294118f, 0.2f, 0, 1, 0 }, // tooltip - { 9.882353f, 0.3f, 0, 4, 0 } // sheet -}; - -// This method will adjust the window shadow style for popup windows after -// they have been made visible. Before they're visible, their window number -// might be -1, which is not useful. -// We won't attempt to change the shadow for windows that can acquire key state -// since OS X will reset the shadow whenever that happens. -void -nsCocoaWindow::AdjustWindowShadow() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mWindow || ![mWindow isVisible] || ![mWindow hasShadow] || - [mWindow canBecomeKeyWindow] || [mWindow windowNumber] == -1) - return; - - const ShadowParams& params = nsCocoaFeatures::OnYosemiteOrLater() - ? kWindowShadowParametersPostYosemite[mShadowStyle] - : kWindowShadowParametersPreYosemite[mShadowStyle]; - CGSConnection cid = _CGSDefaultConnection(); - CGSSetWindowShadowAndRimParameters(cid, [mWindow windowNumber], - params.standardDeviation, params.density, - params.offsetX, params.offsetY, - params.flags); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -static const NSUInteger kWindowBackgroundBlurRadius = 4; - -void -nsCocoaWindow::SetWindowBackgroundBlur() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mWindow || ![mWindow isVisible] || [mWindow windowNumber] == -1) - return; - - // Only blur the background of menus and fake sheets. - if (mShadowStyle != NS_STYLE_WINDOW_SHADOW_MENU && - mShadowStyle != NS_STYLE_WINDOW_SHADOW_SHEET) - return; - - CGSConnection cid = _CGSDefaultConnection(); - CGSSetWindowBackgroundBlurRadius(cid, [mWindow windowNumber], kWindowBackgroundBlurRadius); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -nsresult -nsCocoaWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations) -{ - if (mPopupContentView) { - mPopupContentView->ConfigureChildren(aConfigurations); - } - return NS_OK; -} - -LayerManager* -nsCocoaWindow::GetLayerManager(PLayerTransactionChild* aShadowManager, - LayersBackend aBackendHint, - LayerManagerPersistence aPersistence) -{ - if (mPopupContentView) { - return mPopupContentView->GetLayerManager(aShadowManager, - aBackendHint, - aPersistence); - } - return nullptr; -} - -nsTransparencyMode nsCocoaWindow::GetTransparencyMode() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - return (!mWindow || [mWindow isOpaque]) ? eTransparencyOpaque : eTransparencyTransparent; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(eTransparencyOpaque); -} - -// This is called from nsMenuPopupFrame when making a popup transparent, or -// from nsChildView::SetTransparencyMode for other window types. -void nsCocoaWindow::SetTransparencyMode(nsTransparencyMode aMode) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mWindow) - return; - - // Transparent windows are only supported on popups. - BOOL isTransparent = aMode == eTransparencyTransparent && - mWindowType == eWindowType_popup; - BOOL currentTransparency = ![mWindow isOpaque]; - if (isTransparent != currentTransparency) { - [mWindow setOpaque:!isTransparent]; - [mWindow setBackgroundColor:(isTransparent ? [NSColor clearColor] : [NSColor whiteColor])]; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -NS_IMETHODIMP nsCocoaWindow::Enable(bool aState) -{ - return NS_OK; -} - -bool nsCocoaWindow::IsEnabled() const -{ - return true; -} - -#define kWindowPositionSlop 20 - -void -nsCocoaWindow::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mWindow || ![mWindow screen]) { - return; - } - - nsIntRect screenBounds; - - int32_t width, height; - - NSRect frame = [mWindow frame]; - - // zero size rects confuse the screen manager - width = std::max<int32_t>(frame.size.width, 1); - height = std::max<int32_t>(frame.size.height, 1); - - nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1"); - if (screenMgr) { - nsCOMPtr<nsIScreen> screen; - screenMgr->ScreenForRect(*aX, *aY, width, height, getter_AddRefs(screen)); - - if (screen) { - screen->GetRectDisplayPix(&(screenBounds.x), &(screenBounds.y), - &(screenBounds.width), &(screenBounds.height)); - } - } - - if (aAllowSlop) { - if (*aX < screenBounds.x - width + kWindowPositionSlop) { - *aX = screenBounds.x - width + kWindowPositionSlop; - } else if (*aX >= screenBounds.x + screenBounds.width - kWindowPositionSlop) { - *aX = screenBounds.x + screenBounds.width - kWindowPositionSlop; - } - - if (*aY < screenBounds.y - height + kWindowPositionSlop) { - *aY = screenBounds.y - height + kWindowPositionSlop; - } else if (*aY >= screenBounds.y + screenBounds.height - kWindowPositionSlop) { - *aY = screenBounds.y + screenBounds.height - kWindowPositionSlop; - } - } else { - if (*aX < screenBounds.x) { - *aX = screenBounds.x; - } else if (*aX >= screenBounds.x + screenBounds.width - width) { - *aX = screenBounds.x + screenBounds.width - width; - } - - if (*aY < screenBounds.y) { - *aY = screenBounds.y; - } else if (*aY >= screenBounds.y + screenBounds.height - height) { - *aY = screenBounds.y + screenBounds.height - height; - } - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void nsCocoaWindow::SetSizeConstraints(const SizeConstraints& aConstraints) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Popups can be smaller than (60, 60) - NSRect rect = - (mWindowType == eWindowType_popup) ? NSZeroRect : NSMakeRect(0.0, 0.0, 60, 60); - rect = [mWindow frameRectForContentRect:rect]; - - CGFloat scaleFactor = BackingScaleFactor(); - - SizeConstraints c = aConstraints; - c.mMinSize.width = - std::max(nsCocoaUtils::CocoaPointsToDevPixels(rect.size.width, scaleFactor), - c.mMinSize.width); - c.mMinSize.height = - std::max(nsCocoaUtils::CocoaPointsToDevPixels(rect.size.height, scaleFactor), - c.mMinSize.height); - - NSSize minSize = { - nsCocoaUtils::DevPixelsToCocoaPoints(c.mMinSize.width, scaleFactor), - nsCocoaUtils::DevPixelsToCocoaPoints(c.mMinSize.height, scaleFactor) - }; - [mWindow setMinSize:minSize]; - - NSSize maxSize = { - c.mMaxSize.width == NS_MAXSIZE ? - FLT_MAX : nsCocoaUtils::DevPixelsToCocoaPoints(c.mMaxSize.width, scaleFactor), - c.mMaxSize.height == NS_MAXSIZE ? - FLT_MAX : nsCocoaUtils::DevPixelsToCocoaPoints(c.mMaxSize.height, scaleFactor) - }; - [mWindow setMaxSize:maxSize]; - - nsBaseWidget::SetSizeConstraints(c); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// Coordinates are desktop pixels -NS_IMETHODIMP nsCocoaWindow::Move(double aX, double aY) -{ - if (!mWindow) { - return NS_OK; - } - - // The point we have is in Gecko coordinates (origin top-left). Convert - // it to Cocoa ones (origin bottom-left). - NSPoint coord = { - static_cast<float>(aX), - static_cast<float>(nsCocoaUtils::FlippedScreenY(NSToIntRound(aY))) - }; - - NSRect frame = [mWindow frame]; - if (frame.origin.x != coord.x || - frame.origin.y + frame.size.height != coord.y) { - [mWindow setFrameTopLeftPoint:coord]; - } - - return NS_OK; -} - -void -nsCocoaWindow::SetSizeMode(nsSizeMode aMode) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mWindow) - return; - - // mSizeMode will be updated in DispatchSizeModeEvent, which will be called - // from a delegate method that handles the state change during one of the - // calls below. - nsSizeMode previousMode = mSizeMode; - - if (aMode == nsSizeMode_Normal) { - if ([mWindow isMiniaturized]) - [mWindow deminiaturize:nil]; - else if (previousMode == nsSizeMode_Maximized && [mWindow isZoomed]) - [mWindow zoom:nil]; - } - else if (aMode == nsSizeMode_Minimized) { - if (![mWindow isMiniaturized]) - [mWindow miniaturize:nil]; - } - else if (aMode == nsSizeMode_Maximized) { - if ([mWindow isMiniaturized]) - [mWindow deminiaturize:nil]; - if (![mWindow isZoomed]) - [mWindow zoom:nil]; - } - else if (aMode == nsSizeMode_Fullscreen) { - if (!mInFullScreenMode) - MakeFullScreen(true); - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// This has to preserve the window's frame bounds. -// This method requires (as does the Windows impl.) that you call Resize shortly -// after calling HideWindowChrome. See bug 498835 for fixing this. -NS_IMETHODIMP nsCocoaWindow::HideWindowChrome(bool aShouldHide) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (!mWindow || !mWindowMadeHere || - (mWindowType != eWindowType_toplevel && mWindowType != eWindowType_dialog)) - return NS_ERROR_FAILURE; - - BOOL isVisible = [mWindow isVisible]; - - // Remove child windows. - NSArray* childWindows = [mWindow childWindows]; - NSEnumerator* enumerator = [childWindows objectEnumerator]; - NSWindow* child = nil; - while ((child = [enumerator nextObject])) { - [mWindow removeChildWindow:child]; - } - - // Remove the content view. - NSView* contentView = [mWindow contentView]; - [contentView retain]; - [contentView removeFromSuperviewWithoutNeedingDisplay]; - - // Save state (like window title). - NSMutableDictionary* state = [mWindow exportState]; - - // Recreate the window with the right border style. - NSRect frameRect = [mWindow frame]; - DestroyNativeWindow(); - nsresult rv = CreateNativeWindow(frameRect, aShouldHide ? eBorderStyle_none : mBorderStyle, true); - NS_ENSURE_SUCCESS(rv, rv); - - // Re-import state. - [mWindow importState:state]; - - // Reparent the content view. - [mWindow setContentView:contentView]; - [contentView release]; - - // Reparent child windows. - enumerator = [childWindows objectEnumerator]; - while ((child = [enumerator nextObject])) { - [mWindow addChildWindow:child ordered:NSWindowAbove]; - } - - // Show the new window. - if (isVisible) { - bool wasAnimationSuppressed = mIsAnimationSuppressed; - mIsAnimationSuppressed = true; - rv = Show(true); - mIsAnimationSuppressed = wasAnimationSuppressed; - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -class FullscreenTransitionData : public nsISupports -{ -public: - NS_DECL_ISUPPORTS - - explicit FullscreenTransitionData(NSWindow* aWindow) - : mTransitionWindow(aWindow) { } - - NSWindow* mTransitionWindow; - -private: - virtual ~FullscreenTransitionData() - { - [mTransitionWindow close]; - } -}; - -NS_IMPL_ISUPPORTS0(FullscreenTransitionData) - -@interface FullscreenTransitionDelegate : NSObject <NSAnimationDelegate> -{ -@public - nsCocoaWindow* mWindow; - nsIRunnable* mCallback; -} -@end - -@implementation FullscreenTransitionDelegate -- (void)cleanupAndDispatch:(NSAnimation* )animation -{ - [animation setDelegate:nil]; - [self autorelease]; - // The caller should have added ref for us. - NS_DispatchToMainThread(already_AddRefed<nsIRunnable>(mCallback)); -} - -- (void)animationDidEnd:(NSAnimation *)animation -{ - MOZ_ASSERT(animation == mWindow->FullscreenTransitionAnimation(), - "Should be handling the only animation on the window"); - mWindow->ReleaseFullscreenTransitionAnimation(); - [self cleanupAndDispatch:animation]; -} - -- (void)animationDidStop:(NSAnimation *)animation -{ - [self cleanupAndDispatch:animation]; -} -@end - -/* virtual */ bool -nsCocoaWindow::PrepareForFullscreenTransition(nsISupports** aData) -{ - nsCOMPtr<nsIScreen> widgetScreen = GetWidgetScreen(); - nsScreenCocoa* screen = static_cast<nsScreenCocoa*>(widgetScreen.get()); - NSScreen* cocoaScreen = screen->CocoaScreen(); - - NSWindow* win = - [[NSWindow alloc] initWithContentRect:[cocoaScreen frame] - styleMask:NSBorderlessWindowMask - backing:NSBackingStoreBuffered - defer:YES]; - [win setBackgroundColor:[NSColor blackColor]]; - [win setAlphaValue:0]; - [win setIgnoresMouseEvents:YES]; - [win setLevel:NSScreenSaverWindowLevel]; - [win makeKeyAndOrderFront:nil]; - - auto data = new FullscreenTransitionData(win); - *aData = data; - NS_ADDREF(data); - return true; -} - -/* virtual */ void -nsCocoaWindow::PerformFullscreenTransition(FullscreenTransitionStage aStage, - uint16_t aDuration, - nsISupports* aData, - nsIRunnable* aCallback) -{ - auto data = static_cast<FullscreenTransitionData*>(aData); - FullscreenTransitionDelegate* delegate = - [[FullscreenTransitionDelegate alloc] init]; - delegate->mWindow = this; - // Storing already_AddRefed directly could cause static checking fail. - delegate->mCallback = nsCOMPtr<nsIRunnable>(aCallback).forget().take(); - - if (mFullscreenTransitionAnimation) { - [mFullscreenTransitionAnimation stopAnimation]; - ReleaseFullscreenTransitionAnimation(); - } - - NSDictionary* dict = @{ - NSViewAnimationTargetKey: data->mTransitionWindow, - NSViewAnimationEffectKey: aStage == eBeforeFullscreenToggle ? - NSViewAnimationFadeInEffect : NSViewAnimationFadeOutEffect - }; - mFullscreenTransitionAnimation = - [[NSViewAnimation alloc] initWithViewAnimations:@[dict]]; - [mFullscreenTransitionAnimation setDelegate:delegate]; - [mFullscreenTransitionAnimation setDuration:aDuration / 1000.0]; - [mFullscreenTransitionAnimation startAnimation]; -} - -void nsCocoaWindow::EnteredFullScreen(bool aFullScreen, bool aNativeMode) -{ - mInFullScreenTransition = false; - bool wasInFullscreen = mInFullScreenMode; - mInFullScreenMode = aFullScreen; - if (aNativeMode || mInNativeFullScreenMode) { - mInNativeFullScreenMode = aFullScreen; - } - DispatchSizeModeEvent(); - if (mWidgetListener && wasInFullscreen != aFullScreen) { - mWidgetListener->FullscreenChanged(aFullScreen); - } -} - -inline bool -nsCocoaWindow::ShouldToggleNativeFullscreen(bool aFullScreen, - bool aUseSystemTransition) -{ - if (!mSupportsNativeFullScreen) { - // If we cannot use native fullscreen, don't touch it. - return false; - } - if (mInNativeFullScreenMode) { - // If we are using native fullscreen, go ahead to exit it. - return true; - } - if (!aUseSystemTransition) { - // If we do not want the system fullscreen transition, - // don't use the native fullscreen. - return false; - } - // If we are using native fullscreen, we should have returned earlier. - return aFullScreen; -} - -nsresult -nsCocoaWindow::MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen) -{ - return DoMakeFullScreen(aFullScreen, false); -} - -NS_IMETHODIMP -nsCocoaWindow::MakeFullScreenWithNativeTransition(bool aFullScreen, - nsIScreen* aTargetScreen) -{ - return DoMakeFullScreen(aFullScreen, true); -} - -nsresult -nsCocoaWindow::DoMakeFullScreen(bool aFullScreen, bool aUseSystemTransition) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (!mWindow) { - return NS_OK; - } - - // We will call into MakeFullScreen redundantly when entering/exiting - // fullscreen mode via OS X controls. When that happens we should just handle - // it gracefully - no need to ASSERT. - if (mInFullScreenMode == aFullScreen) { - return NS_OK; - } - - mInFullScreenTransition = true; - - if (ShouldToggleNativeFullscreen(aFullScreen, aUseSystemTransition)) { - // If we're using native fullscreen mode and our native window is invisible, - // our attempt to go into fullscreen mode will fail with an assertion in - // system code, without [WindowDelegate windowDidFailToEnterFullScreen:] - // ever getting called. To pre-empt this we bail here. See bug 752294. - if (aFullScreen && ![mWindow isVisible]) { - EnteredFullScreen(false); - return NS_OK; - } - MOZ_ASSERT(mInNativeFullScreenMode != aFullScreen, - "We shouldn't have been in native fullscreen."); - // Calling toggleFullScreen will result in windowDid(FailTo)?(Enter|Exit)FullScreen - // to be called from the OS. We will call EnteredFullScreen from those methods, - // where mInFullScreenMode will be set and a sizemode event will be dispatched. - [mWindow toggleFullScreen:nil]; - } else { - NSDisableScreenUpdates(); - // The order here matters. When we exit full screen mode, we need to show the - // Dock first, otherwise the newly-created window won't have its minimize - // button enabled. See bug 526282. - nsCocoaUtils::HideOSChromeOnScreen(aFullScreen); - nsBaseWidget::InfallibleMakeFullScreen(aFullScreen); - NSEnableScreenUpdates(); - EnteredFullScreen(aFullScreen, /* aNativeMode */ false); - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// Coordinates are desktop pixels -nsresult nsCocoaWindow::DoResize(double aX, double aY, - double aWidth, double aHeight, - bool aRepaint, - bool aConstrainToCurrentScreen) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (!mWindow || mInResize) { - return NS_OK; - } - - AutoRestore<bool> reentrantResizeGuard(mInResize); - mInResize = true; - - // ConstrainSize operates in device pixels, so we need to convert using - // the backing scale factor here - CGFloat scale = BackingScaleFactor(); - int32_t width = NSToIntRound(aWidth * scale); - int32_t height = NSToIntRound(aHeight * scale); - ConstrainSize(&width, &height); - - DesktopIntRect newBounds(NSToIntRound(aX), NSToIntRound(aY), - NSToIntRound(width / scale), - NSToIntRound(height / scale)); - - // constrain to the screen that contains the largest area of the new rect - FitRectToVisibleAreaForScreen(newBounds, aConstrainToCurrentScreen ? - [mWindow screen] : nullptr); - - // convert requested bounds into Cocoa coordinate system - NSRect newFrame = nsCocoaUtils::GeckoRectToCocoaRect(newBounds); - - NSRect frame = [mWindow frame]; - BOOL isMoving = newFrame.origin.x != frame.origin.x || - newFrame.origin.y != frame.origin.y; - BOOL isResizing = newFrame.size.width != frame.size.width || - newFrame.size.height != frame.size.height; - - if (!isMoving && !isResizing) { - return NS_OK; - } - - // We ignore aRepaint -- we have to call display:YES, otherwise the - // title bar doesn't immediately get repainted and is displayed in - // the wrong place, leading to a visual jump. - [mWindow setFrame:newFrame display:YES]; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// Coordinates are desktop pixels -NS_IMETHODIMP nsCocoaWindow::Resize(double aX, double aY, - double aWidth, double aHeight, - bool aRepaint) -{ - return DoResize(aX, aY, aWidth, aHeight, aRepaint, false); -} - -// Coordinates are desktop pixels -NS_IMETHODIMP nsCocoaWindow::Resize(double aWidth, double aHeight, bool aRepaint) -{ - double invScale = 1.0 / BackingScaleFactor(); - return DoResize(mBounds.x * invScale, mBounds.y * invScale, - aWidth, aHeight, aRepaint, true); -} - -LayoutDeviceIntRect -nsCocoaWindow::GetClientBounds() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - CGFloat scaleFactor = BackingScaleFactor(); - if (!mWindow) { - return nsCocoaUtils::CocoaRectToGeckoRectDevPix(NSZeroRect, scaleFactor); - } - - NSRect r; - if ([mWindow isKindOfClass:[ToolbarWindow class]] && - [(ToolbarWindow*)mWindow drawsContentsIntoWindowFrame]) { - r = [mWindow frame]; - } else { - r = [mWindow contentRectForFrameRect:[mWindow frame]]; - } - - return nsCocoaUtils::CocoaRectToGeckoRectDevPix(r, scaleFactor); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(LayoutDeviceIntRect(0, 0, 0, 0)); -} - -void -nsCocoaWindow::UpdateBounds() -{ - NSRect frame = NSZeroRect; - if (mWindow) { - frame = [mWindow frame]; - } - mBounds = - nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, BackingScaleFactor()); -} - -LayoutDeviceIntRect -nsCocoaWindow::GetScreenBounds() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - -#ifdef DEBUG - LayoutDeviceIntRect r = nsCocoaUtils::CocoaRectToGeckoRectDevPix([mWindow frame], BackingScaleFactor()); - NS_ASSERTION(mWindow && mBounds == r, "mBounds out of sync!"); -#endif - - return mBounds; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(LayoutDeviceIntRect(0, 0, 0, 0)); -} - -double -nsCocoaWindow::GetDefaultScaleInternal() -{ - return BackingScaleFactor(); -} - -static CGFloat -GetBackingScaleFactor(NSWindow* aWindow) -{ - NSRect frame = [aWindow frame]; - if (frame.size.width > 0 && frame.size.height > 0) { - return nsCocoaUtils::GetBackingScaleFactor(aWindow); - } - - // For windows with zero width or height, the backingScaleFactor method - // is broken - it will always return 2 on a retina macbook, even when - // the window position implies it's on a non-hidpi external display - // (to the extent that a zero-area window can be said to be "on" a - // display at all!) - // And to make matters worse, Cocoa even fires a - // windowDidChangeBackingProperties notification with the - // NSBackingPropertyOldScaleFactorKey key when a window on an - // external display is resized to/from zero height, even though it hasn't - // really changed screens. - - // This causes us to handle popup window sizing incorrectly when the - // popup is resized to zero height (bug 820327) - nsXULPopupManager - // becomes (incorrectly) convinced the popup has been explicitly forced - // to a non-default size and needs to have size attributes attached. - - // Workaround: instead of asking the window, we'll find the screen it is on - // and ask that for *its* backing scale factor. - - // (See bug 853252 and additional comments in windowDidChangeScreen: below - // for further complications this causes.) - - // First, expand the rect so that it actually has a measurable area, - // for FindTargetScreenForRect to use. - if (frame.size.width == 0) { - frame.size.width = 1; - } - if (frame.size.height == 0) { - frame.size.height = 1; - } - - // Then identify the screen it belongs to, and return its scale factor. - NSScreen *screen = - FindTargetScreenForRect(nsCocoaUtils::CocoaRectToGeckoRect(frame)); - return nsCocoaUtils::GetBackingScaleFactor(screen); -} - -CGFloat -nsCocoaWindow::BackingScaleFactor() -{ - if (mBackingScaleFactor > 0.0) { - return mBackingScaleFactor; - } - if (!mWindow) { - return 1.0; - } - mBackingScaleFactor = GetBackingScaleFactor(mWindow); - return mBackingScaleFactor; -} - -void -nsCocoaWindow::BackingScaleFactorChanged() -{ - CGFloat newScale = GetBackingScaleFactor(mWindow); - - // ignore notification if it hasn't really changed (or maybe we have - // disabled HiDPI mode via prefs) - if (mBackingScaleFactor == newScale) { - return; - } - - if (mBackingScaleFactor > 0.0) { - // convert size constraints to the new device pixel coordinate space - double scaleFactor = newScale / mBackingScaleFactor; - mSizeConstraints.mMinSize.width = - NSToIntRound(mSizeConstraints.mMinSize.width * scaleFactor); - mSizeConstraints.mMinSize.height = - NSToIntRound(mSizeConstraints.mMinSize.height * scaleFactor); - if (mSizeConstraints.mMaxSize.width < NS_MAXSIZE) { - mSizeConstraints.mMaxSize.width = - std::min(NS_MAXSIZE, - NSToIntRound(mSizeConstraints.mMaxSize.width * scaleFactor)); - } - if (mSizeConstraints.mMaxSize.height < NS_MAXSIZE) { - mSizeConstraints.mMaxSize.height = - std::min(NS_MAXSIZE, - NSToIntRound(mSizeConstraints.mMaxSize.height * scaleFactor)); - } - } - - mBackingScaleFactor = newScale; - - if (!mWidgetListener || mWidgetListener->GetXULWindow()) { - return; - } - - nsIPresShell* presShell = mWidgetListener->GetPresShell(); - if (presShell) { - presShell->BackingScaleFactorChanged(); - } -} - -int32_t -nsCocoaWindow::RoundsWidgetCoordinatesTo() -{ - if (BackingScaleFactor() == 2.0) { - return 2; - } - return 1; -} - -NS_IMETHODIMP nsCocoaWindow::SetCursor(nsCursor aCursor) -{ - if (mPopupContentView) - return mPopupContentView->SetCursor(aCursor); - - return NS_OK; -} - -NS_IMETHODIMP nsCocoaWindow::SetCursor(imgIContainer* aCursor, - uint32_t aHotspotX, uint32_t aHotspotY) -{ - if (mPopupContentView) - return mPopupContentView->SetCursor(aCursor, aHotspotX, aHotspotY); - - return NS_OK; -} - -NS_IMETHODIMP nsCocoaWindow::SetTitle(const nsAString& aTitle) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (!mWindow) - return NS_OK; - - const nsString& strTitle = PromiseFlatString(aTitle); - NSString* title = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(strTitle.get()) - length:strTitle.Length()]; - - if ([mWindow drawsContentsIntoWindowFrame] && ![mWindow wantsTitleDrawn]) { - // Don't cause invalidations. - [mWindow disableSetNeedsDisplay]; - [mWindow setTitle:title]; - [mWindow enableSetNeedsDisplay]; - } else { - [mWindow setTitle:title]; - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP nsCocoaWindow::Invalidate(const LayoutDeviceIntRect& aRect) -{ - if (mPopupContentView) { - return mPopupContentView->Invalidate(aRect); - } - - return NS_OK; -} - -// Pass notification of some drag event to Gecko -// -// The drag manager has let us know that something related to a drag has -// occurred in this window. It could be any number of things, ranging from -// a drop, to a drag enter/leave, or a drag over event. The actual event -// is passed in |aMessage| and is passed along to our event hanlder so Gecko -// knows about it. -bool nsCocoaWindow::DragEvent(unsigned int aMessage, mozilla::gfx::Point aMouseGlobal, UInt16 aKeyModifiers) -{ - return false; -} - -NS_IMETHODIMP nsCocoaWindow::SendSetZLevelEvent() -{ - nsWindowZ placement = nsWindowZTop; - nsCOMPtr<nsIWidget> actualBelow; - if (mWidgetListener) - mWidgetListener->ZLevelChanged(true, &placement, nullptr, getter_AddRefs(actualBelow)); - return NS_OK; -} - -NS_IMETHODIMP nsCocoaWindow::GetChildSheet(bool aShown, nsIWidget** _retval) -{ - nsIWidget* child = GetFirstChild(); - - while (child) { - if (child->WindowType() == eWindowType_sheet) { - // if it's a sheet, it must be an nsCocoaWindow - nsCocoaWindow* cocoaWindow = static_cast<nsCocoaWindow*>(child); - if (cocoaWindow->mWindow && - ((aShown && [cocoaWindow->mWindow isVisible]) || - (!aShown && cocoaWindow->mSheetNeedsShow))) { - nsCOMPtr<nsIWidget> widget = cocoaWindow; - widget.forget(_retval); - return NS_OK; - } - } - child = child->GetNextSibling(); - } - - *_retval = nullptr; - - return NS_OK; -} - -NS_IMETHODIMP nsCocoaWindow::GetRealParent(nsIWidget** parent) -{ - *parent = mParent; - return NS_OK; -} - -NS_IMETHODIMP nsCocoaWindow::GetIsSheet(bool* isSheet) -{ - mWindowType == eWindowType_sheet ? *isSheet = true : *isSheet = false; - return NS_OK; -} - -NS_IMETHODIMP nsCocoaWindow::GetSheetWindowParent(NSWindow** sheetWindowParent) -{ - *sheetWindowParent = mSheetWindowParent; - return NS_OK; -} - -// Invokes callback and ProcessEvent methods on Event Listener object -NS_IMETHODIMP -nsCocoaWindow::DispatchEvent(WidgetGUIEvent* event, nsEventStatus& aStatus) -{ - aStatus = nsEventStatus_eIgnore; - - nsCOMPtr<nsIWidget> kungFuDeathGrip(event->mWidget); - mozilla::Unused << kungFuDeathGrip; // Not used within this function - - if (mWidgetListener) - aStatus = mWidgetListener->HandleEvent(event, mUseAttachedEvents); - - return NS_OK; -} - -// aFullScreen should be the window's mInFullScreenMode. We don't have access to that -// from here, so we need to pass it in. mInFullScreenMode should be the canonical -// indicator that a window is currently full screen and it makes sense to keep -// all sizemode logic here. -static nsSizeMode -GetWindowSizeMode(NSWindow* aWindow, bool aFullScreen) { - if (aFullScreen) - return nsSizeMode_Fullscreen; - if ([aWindow isMiniaturized]) - return nsSizeMode_Minimized; - if (([aWindow styleMask] & NSResizableWindowMask) && [aWindow isZoomed]) - return nsSizeMode_Maximized; - return nsSizeMode_Normal; -} - -void -nsCocoaWindow::ReportMoveEvent() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Prevent recursion, which can become infinite (see bug 708278). This - // can happen when the call to [NSWindow setFrameTopLeftPoint:] in - // nsCocoaWindow::Move() triggers an immediate NSWindowDidMove notification - // (and a call to [WindowDelegate windowDidMove:]). - if (mInReportMoveEvent) { - return; - } - mInReportMoveEvent = true; - - UpdateBounds(); - - // Dispatch the move event to Gecko - NotifyWindowMoved(mBounds.x, mBounds.y); - - mInReportMoveEvent = false; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -nsCocoaWindow::DispatchSizeModeEvent() -{ - if (!mWindow) { - return; - } - - nsSizeMode newMode = GetWindowSizeMode(mWindow, mInFullScreenMode); - - // Don't dispatch a sizemode event if: - // 1. the window is transitioning to fullscreen - // 2. the new sizemode is the same as the current sizemode - if (mInFullScreenTransition || mSizeMode == newMode) { - return; - } - - mSizeMode = newMode; - if (mWidgetListener) { - mWidgetListener->SizeModeChanged(newMode); - } -} - -void -nsCocoaWindow::ReportSizeEvent() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - UpdateBounds(); - - if (mWidgetListener) { - LayoutDeviceIntRect innerBounds = GetClientBounds(); - mWidgetListener->WindowResized(this, innerBounds.width, innerBounds.height); - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void nsCocoaWindow::SetMenuBar(nsMenuBarX *aMenuBar) -{ - if (mMenuBar) - mMenuBar->SetParent(nullptr); - if (!mWindow) { - mMenuBar = nullptr; - return; - } - mMenuBar = aMenuBar; - - // Only paint for active windows, or paint the hidden window menu bar if no - // other menu bar has been painted yet so that some reasonable menu bar is - // displayed when the app starts up. - id windowDelegate = [mWindow delegate]; - if (mMenuBar && - ((!gSomeMenuBarPainted && nsMenuUtilsX::GetHiddenWindowMenuBar() == mMenuBar) || - (windowDelegate && [windowDelegate toplevelActiveState]))) - mMenuBar->Paint(); -} - -NS_IMETHODIMP nsCocoaWindow::SetFocus(bool aState) -{ - if (!mWindow) - return NS_OK; - - if (mPopupContentView) { - mPopupContentView->SetFocus(aState); - } - else if (aState && ([mWindow isVisible] || [mWindow isMiniaturized])) { - if ([mWindow isMiniaturized]) { - [mWindow deminiaturize:nil]; - } - - [mWindow makeKeyAndOrderFront:nil]; - SendSetZLevelEvent(); - } - - return NS_OK; -} - -LayoutDeviceIntPoint nsCocoaWindow::WidgetToScreenOffset() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - NSRect rect = NSZeroRect; - LayoutDeviceIntRect r; - if (mWindow) { - rect = [mWindow contentRectForFrameRect:[mWindow frame]]; - } - r = nsCocoaUtils::CocoaRectToGeckoRectDevPix(rect, BackingScaleFactor()); - - return r.TopLeft(); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(LayoutDeviceIntPoint(0,0)); -} - -LayoutDeviceIntPoint nsCocoaWindow::GetClientOffset() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - LayoutDeviceIntRect clientRect = GetClientBounds(); - - return clientRect.TopLeft() - mBounds.TopLeft(); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(LayoutDeviceIntPoint(0, 0)); -} - -LayoutDeviceIntSize -nsCocoaWindow::ClientToWindowSize(const LayoutDeviceIntSize& aClientSize) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - if (!mWindow) - return LayoutDeviceIntSize(0, 0); - - CGFloat backingScale = BackingScaleFactor(); - LayoutDeviceIntRect r(0, 0, aClientSize.width, aClientSize.height); - NSRect rect = nsCocoaUtils::DevPixelsToCocoaPoints(r, backingScale); - - NSRect inflatedRect = [mWindow frameRectForContentRect:rect]; - r = nsCocoaUtils::CocoaRectToGeckoRectDevPix(inflatedRect, backingScale); - return r.Size(); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(LayoutDeviceIntSize(0,0)); -} - -nsMenuBarX* nsCocoaWindow::GetMenuBar() -{ - return mMenuBar; -} - -void -nsCocoaWindow::CaptureRollupEvents(nsIRollupListener* aListener, - bool aDoCapture) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - gRollupListener = nullptr; - - if (aDoCapture) { - if (![NSApp isActive]) { - // We need to capture mouse event if we aren't - // the active application. We only set this up when needed - // because they cause spurious mouse event after crash - // and gdb sessions. See bug 699538. - nsToolkit::GetToolkit()->RegisterForAllProcessMouseEvents(); - } - gRollupListener = aListener; - - // Sometimes more than one popup window can be visible at the same time - // (e.g. nested non-native context menus, or the test case (attachment - // 276885) for bmo bug 392389, which displays a non-native combo-box in a - // non-native popup window). In these cases the "active" popup window should - // be the topmost -- the (nested) context menu the mouse is currently over, - // or the combo-box's drop-down list (when it's displayed). But (among - // windows that have the same "level") OS X makes topmost the window that - // last received a mouse-down event, which may be incorrect (in the combo- - // box case, it makes topmost the window containing the combo-box). So - // here we fiddle with a non-native popup window's level to make sure the - // "active" one is always above any other non-native popup windows that - // may be visible. - if (mWindow && (mWindowType == eWindowType_popup)) - SetPopupWindowLevel(); - } else { - nsToolkit::GetToolkit()->UnregisterAllProcessMouseEventHandlers(); - - // XXXndeakin this doesn't make sense. - // Why is the new window assumed to be a modal panel? - if (mWindow && (mWindowType == eWindowType_popup)) - [mWindow setLevel:NSModalPanelWindowLevel]; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -NS_IMETHODIMP nsCocoaWindow::GetAttention(int32_t aCycleCount) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - [NSApp requestUserAttention:NSInformationalRequest]; - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -bool -nsCocoaWindow::HasPendingInputEvent() -{ - return nsChildView::DoHasPendingInputEvent(); -} - -void -nsCocoaWindow::SetWindowShadowStyle(int32_t aStyle) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mWindow) - return; - - mShadowStyle = aStyle; - - // Shadowless windows are only supported on popups. - if (mWindowType == eWindowType_popup) - [mWindow setHasShadow:(aStyle != NS_STYLE_WINDOW_SHADOW_NONE)]; - - [mWindow setUseMenuStyle:(aStyle == NS_STYLE_WINDOW_SHADOW_MENU)]; - AdjustWindowShadow(); - SetWindowBackgroundBlur(); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void nsCocoaWindow::SetShowsToolbarButton(bool aShow) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (mWindow) - [mWindow setShowsToolbarButton:aShow]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void nsCocoaWindow::SetShowsFullScreenButton(bool aShow) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mWindow || ![mWindow respondsToSelector:@selector(toggleFullScreen:)] || - mSupportsNativeFullScreen == aShow) { - return; - } - - // If the window is currently in fullscreen mode, then we're going to - // transition out first, then set the collection behavior & toggle - // mSupportsNativeFullScreen, then transtion back into fullscreen mode. This - // prevents us from getting into a conflicting state with MakeFullScreen - // where mSupportsNativeFullScreen would lead us down the wrong path. - bool wasFullScreen = mInFullScreenMode; - - if (wasFullScreen) { - MakeFullScreen(false); - } - - NSWindowCollectionBehavior newBehavior = [mWindow collectionBehavior]; - if (aShow) { - newBehavior |= NSWindowCollectionBehaviorFullScreenPrimary; - } else { - newBehavior &= ~NSWindowCollectionBehaviorFullScreenPrimary; - } - [mWindow setCollectionBehavior:newBehavior]; - mSupportsNativeFullScreen = aShow; - - if (wasFullScreen) { - MakeFullScreen(true); - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void nsCocoaWindow::SetWindowAnimationType(nsIWidget::WindowAnimationType aType) -{ - mAnimationType = aType; -} - -void -nsCocoaWindow::SetDrawsTitle(bool aDrawTitle) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [mWindow setWantsTitleDrawn:aDrawTitle]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -nsCocoaWindow::SetUseBrightTitlebarForeground(bool aBrightForeground) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [mWindow setUseBrightTitlebarForeground:aBrightForeground]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -NS_IMETHODIMP nsCocoaWindow::SetNonClientMargins(LayoutDeviceIntMargin &margins) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - SetDrawsInTitlebar(margins.top == 0); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -void -nsCocoaWindow::SetWindowTitlebarColor(nscolor aColor, bool aActive) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mWindow) - return; - - // If they pass a color with a complete transparent alpha component, use the - // native titlebar appearance. - if (NS_GET_A(aColor) == 0) { - [mWindow setTitlebarColor:nil forActiveWindow:(BOOL)aActive]; - } else { - // Transform from sRGBA to monitor RGBA. This seems like it would make trying - // to match the system appearance lame, so probably we just shouldn't color - // correct chrome. - if (gfxPlatform::GetCMSMode() == eCMSMode_All) { - qcms_transform *transform = gfxPlatform::GetCMSRGBATransform(); - if (transform) { - uint8_t color[3]; - color[0] = NS_GET_R(aColor); - color[1] = NS_GET_G(aColor); - color[2] = NS_GET_B(aColor); - qcms_transform_data(transform, color, color, 1); - aColor = NS_RGB(color[0], color[1], color[2]); - } - } - - [mWindow setTitlebarColor:[NSColor colorWithDeviceRed:NS_GET_R(aColor)/255.0 - green:NS_GET_G(aColor)/255.0 - blue:NS_GET_B(aColor)/255.0 - alpha:NS_GET_A(aColor)/255.0] - forActiveWindow:(BOOL)aActive]; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void nsCocoaWindow::SetDrawsInTitlebar(bool aState) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (mWindow) - [mWindow setDrawsContentsIntoWindowFrame:aState]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -NS_IMETHODIMP nsCocoaWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint, - uint32_t aNativeMessage, - uint32_t aModifierFlags, - nsIObserver* aObserver) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - AutoObserverNotifier notifier(aObserver, "mouseevent"); - if (mPopupContentView) - return mPopupContentView->SynthesizeNativeMouseEvent(aPoint, aNativeMessage, - aModifierFlags, nullptr); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -void nsCocoaWindow::UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (mPopupContentView) { - return mPopupContentView->UpdateThemeGeometries(aThemeGeometries); - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void nsCocoaWindow::SetPopupWindowLevel() -{ - if (!mWindow) - return; - - // Floating popups are at the floating level and hide when the window is - // deactivated. - if (mPopupLevel == ePopupLevelFloating) { - [mWindow setLevel:NSFloatingWindowLevel]; - [mWindow setHidesOnDeactivate:YES]; - } - else { - // Otherwise, this is a top-level or parent popup. Parent popups always - // appear just above their parent and essentially ignore the level. - [mWindow setLevel:NSPopUpMenuWindowLevel]; - [mWindow setHidesOnDeactivate:NO]; - } -} - -NS_IMETHODIMP_(void) -nsCocoaWindow::SetInputContext(const InputContext& aContext, - const InputContextAction& aAction) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - mInputContext = aContext; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -NS_IMETHODIMP_(bool) -nsCocoaWindow::ExecuteNativeKeyBinding(NativeKeyBindingsType aType, - const WidgetKeyboardEvent& aEvent, - DoCommandCallback aCallback, - void* aCallbackData) -{ - NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType); - return keyBindings->Execute(aEvent, aCallback, aCallbackData); -} - -@implementation WindowDelegate - -// We try to find a gecko menu bar to paint. If one does not exist, just paint -// the application menu by itself so that a window doesn't have some other -// window's menu bar. -+ (void)paintMenubarForWindow:(NSWindow*)aWindow -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // make sure we only act on windows that have this kind of - // object as a delegate - id windowDelegate = [aWindow delegate]; - if ([windowDelegate class] != [self class]) - return; - - nsCocoaWindow* geckoWidget = [windowDelegate geckoWidget]; - NS_ASSERTION(geckoWidget, "Window delegate not returning a gecko widget!"); - - nsMenuBarX* geckoMenuBar = geckoWidget->GetMenuBar(); - if (geckoMenuBar) { - geckoMenuBar->Paint(); - } - else { - // sometimes we don't have a native application menu early in launching - if (!sApplicationMenu) - return; - - NSMenu* mainMenu = [NSApp mainMenu]; - NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!"); - - // Create a new menu bar. - // We create a GeckoNSMenu because all menu bar NSMenu objects should use that subclass for - // key handling reasons. - GeckoNSMenu* newMenuBar = [[GeckoNSMenu alloc] initWithTitle:@"MainMenuBar"]; - - // move the application menu from the existing menu bar to the new one - NSMenuItem* firstMenuItem = [[mainMenu itemAtIndex:0] retain]; - [mainMenu removeItemAtIndex:0]; - [newMenuBar insertItem:firstMenuItem atIndex:0]; - [firstMenuItem release]; - - // set our new menu bar as the main menu - [NSApp setMainMenu:newMenuBar]; - [newMenuBar release]; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (id)initWithGeckoWindow:(nsCocoaWindow*)geckoWind -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - [super init]; - mGeckoWindow = geckoWind; - mToplevelActiveState = false; - mHasEverBeenZoomed = false; - return self; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)proposedFrameSize -{ - RollUpPopups(); - - return proposedFrameSize; -} - -- (void)windowDidResize:(NSNotification *)aNotification -{ - BaseWindow* window = [aNotification object]; - [window updateTrackingArea]; - - if (!mGeckoWindow) - return; - - // Resizing might have changed our zoom state. - mGeckoWindow->DispatchSizeModeEvent(); - mGeckoWindow->ReportSizeEvent(); -} - -- (void)windowDidChangeScreen:(NSNotification *)aNotification -{ - if (!mGeckoWindow) - return; - - // Because of Cocoa's peculiar treatment of zero-size windows (see comments - // at GetBackingScaleFactor() above), we sometimes have a situation where - // our concept of backing scale (based on the screen where the zero-sized - // window is positioned) differs from Cocoa's idea (always based on the - // Retina screen, AFAICT, even when an external non-Retina screen is the - // primary display). - // - // As a result, if the window was created with zero size on an external - // display, but then made visible on the (secondary) Retina screen, we - // will *not* get a windowDidChangeBackingProperties notification for it. - // This leads to an incorrect GetDefaultScale(), and widget coordinate - // confusion, as per bug 853252. - // - // To work around this, we check for a backing scale mismatch when we - // receive a windowDidChangeScreen notification, as we will receive this - // even if Cocoa was already treating the zero-size window as having - // Retina backing scale. - NSWindow *window = (NSWindow *)[aNotification object]; - if ([window respondsToSelector:@selector(backingScaleFactor)]) { - if (GetBackingScaleFactor(window) != mGeckoWindow->BackingScaleFactor()) { - mGeckoWindow->BackingScaleFactorChanged(); - } - } - - mGeckoWindow->ReportMoveEvent(); -} - -// Lion's full screen mode will bypass our internal fullscreen tracking, so -// we need to catch it when we transition and call our own methods, which in -// turn will fire "fullscreen" events. -- (void)windowDidEnterFullScreen:(NSNotification *)notification -{ - if (!mGeckoWindow) { - return; - } - - mGeckoWindow->EnteredFullScreen(true); - - // On Yosemite, the NSThemeFrame class has two new properties -- - // titlebarView (an NSTitlebarView object) and titlebarContainerView (an - // NSTitlebarContainerView object). These are used to display the titlebar - // in fullscreen mode. In Safari they're not transparent. But in Firefox - // for some reason they are, which causes bug 1069658. The following code - // works around this Apple bug or design flaw. - NSWindow *window = (NSWindow *) [notification object]; - NSView *frameView = [[window contentView] superview]; - NSView *titlebarView = nil; - NSView *titlebarContainerView = nil; - if ([frameView respondsToSelector:@selector(titlebarView)]) { - titlebarView = [frameView titlebarView]; - } - if ([frameView respondsToSelector:@selector(titlebarContainerView)]) { - titlebarContainerView = [frameView titlebarContainerView]; - } - if ([titlebarView respondsToSelector:@selector(setTransparent:)]) { - [titlebarView setTransparent:NO]; - } - if ([titlebarContainerView respondsToSelector:@selector(setTransparent:)]) { - [titlebarContainerView setTransparent:NO]; - } -} - -- (void)windowDidExitFullScreen:(NSNotification *)notification -{ - if (!mGeckoWindow) { - return; - } - - mGeckoWindow->EnteredFullScreen(false); -} - -- (void)windowDidFailToEnterFullScreen:(NSWindow *)window -{ - if (!mGeckoWindow) { - return; - } - - mGeckoWindow->EnteredFullScreen(false); -} - -- (void)windowDidFailToExitFullScreen:(NSWindow *)window -{ - if (!mGeckoWindow) { - return; - } - - mGeckoWindow->EnteredFullScreen(true); -} - -- (void)windowDidBecomeMain:(NSNotification *)aNotification -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - RollUpPopups(); - ChildViewMouseTracker::ReEvaluateMouseEnterState(); - - // [NSApp _isRunningAppModal] will return true if we're running an OS dialog - // app modally. If one of those is up then we want it to retain its menu bar. - if ([NSApp _isRunningAppModal]) - return; - NSWindow* window = [aNotification object]; - if (window) - [WindowDelegate paintMenubarForWindow:window]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)windowDidResignMain:(NSNotification *)aNotification -{ - RollUpPopups(); - ChildViewMouseTracker::ReEvaluateMouseEnterState(); - - // [NSApp _isRunningAppModal] will return true if we're running an OS dialog - // app modally. If one of those is up then we want it to retain its menu bar. - if ([NSApp _isRunningAppModal]) - return; - RefPtr<nsMenuBarX> hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar(); - if (hiddenWindowMenuBar) { - // printf("painting hidden window menu bar due to window losing main status\n"); - hiddenWindowMenuBar->Paint(); - } -} - -- (void)windowDidBecomeKey:(NSNotification *)aNotification -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - RollUpPopups(); - ChildViewMouseTracker::ReEvaluateMouseEnterState(); - - NSWindow* window = [aNotification object]; - if ([window isSheet]) - [WindowDelegate paintMenubarForWindow:window]; - - nsChildView* mainChildView = - static_cast<nsChildView*>([[(BaseWindow*)window mainChildView] widget]); - if (mainChildView) { - if (mainChildView->GetInputContext().IsPasswordEditor()) { - TextInputHandler::EnableSecureEventInput(); - } else { - TextInputHandler::EnsureSecureEventInputDisabled(); - } - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)windowDidResignKey:(NSNotification *)aNotification -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - RollUpPopups(); - ChildViewMouseTracker::ReEvaluateMouseEnterState(); - - // If a sheet just resigned key then we should paint the menu bar - // for whatever window is now main. - NSWindow* window = [aNotification object]; - if ([window isSheet]) - [WindowDelegate paintMenubarForWindow:[NSApp mainWindow]]; - - TextInputHandler::EnsureSecureEventInputDisabled(); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)windowWillMove:(NSNotification *)aNotification -{ - RollUpPopups(); -} - -- (void)windowDidMove:(NSNotification *)aNotification -{ - if (mGeckoWindow) - mGeckoWindow->ReportMoveEvent(); -} - -- (BOOL)windowShouldClose:(id)sender -{ - nsIWidgetListener* listener = mGeckoWindow ? mGeckoWindow->GetWidgetListener() : nullptr; - if (listener) - listener->RequestWindowClose(mGeckoWindow); - return NO; // gecko will do it -} - -- (void)windowWillClose:(NSNotification *)aNotification -{ - RollUpPopups(); -} - -- (void)windowWillMiniaturize:(NSNotification *)aNotification -{ - RollUpPopups(); -} - -- (void)windowDidMiniaturize:(NSNotification *)aNotification -{ - if (mGeckoWindow) - mGeckoWindow->DispatchSizeModeEvent(); -} - -- (void)windowDidDeminiaturize:(NSNotification *)aNotification -{ - if (mGeckoWindow) - mGeckoWindow->DispatchSizeModeEvent(); -} - -- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)proposedFrame -{ - if (!mHasEverBeenZoomed && [window isZoomed]) - return NO; // See bug 429954. - - mHasEverBeenZoomed = YES; - return YES; -} - -- (void)didEndSheet:(NSWindow*)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Note: 'contextInfo' (if it is set) is the window that is the parent of - // the sheet. The value of contextInfo is determined in - // nsCocoaWindow::Show(). If it's set, 'contextInfo' is always the top- - // level window, not another sheet itself. But 'contextInfo' is nil if - // our parent window is also a sheet -- in that case we shouldn't send - // the top-level window any activate events (because it's our parent - // window that needs to get these events, not the top-level window). - [TopLevelWindowData deactivateInWindow:sheet]; - [sheet orderOut:self]; - if (contextInfo) - [TopLevelWindowData activateInWindow:(NSWindow*)contextInfo]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)windowDidChangeBackingProperties:(NSNotification *)aNotification -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - NSWindow *window = (NSWindow *)[aNotification object]; - - if ([window respondsToSelector:@selector(backingScaleFactor)]) { - CGFloat oldFactor = - [[[aNotification userInfo] - objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue]; - if ([window backingScaleFactor] != oldFactor) { - mGeckoWindow->BackingScaleFactorChanged(); - } - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (nsCocoaWindow*)geckoWidget -{ - return mGeckoWindow; -} - -- (bool)toplevelActiveState -{ - return mToplevelActiveState; -} - -- (void)sendToplevelActivateEvents -{ - if (!mToplevelActiveState && mGeckoWindow) { - nsIWidgetListener* listener = mGeckoWindow->GetWidgetListener(); - if (listener) { - listener->WindowActivated(); - } - mToplevelActiveState = true; - } -} - -- (void)sendToplevelDeactivateEvents -{ - if (mToplevelActiveState && mGeckoWindow) { - nsIWidgetListener* listener = mGeckoWindow->GetWidgetListener(); - if (listener) { - listener->WindowDeactivated(); - } - mToplevelActiveState = false; - } -} - -@end - -static float -GetDPI(NSWindow* aWindow) -{ - NSScreen* screen = [aWindow screen]; - if (!screen) - return 96.0f; - - CGDirectDisplayID displayID = - [[[screen deviceDescription] objectForKey:@"NSScreenNumber"] intValue]; - CGFloat heightMM = ::CGDisplayScreenSize(displayID).height; - size_t heightPx = ::CGDisplayPixelsHigh(displayID); - if (heightMM < 1 || heightPx < 1) { - // Something extremely bogus is going on - return 96.0f; - } - - float dpi = heightPx / (heightMM / MM_PER_INCH_FLOAT); - - // Account for HiDPI mode where Cocoa's "points" do not correspond to real - // device pixels - CGFloat backingScale = GetBackingScaleFactor(aWindow); - - return dpi * backingScale; -} - -@interface NSView(FrameViewMethodSwizzling) -- (NSPoint)FrameView__closeButtonOrigin; -- (NSPoint)FrameView__fullScreenButtonOrigin; -@end - -@implementation NSView(FrameViewMethodSwizzling) - -- (NSPoint)FrameView__closeButtonOrigin -{ - NSPoint defaultPosition = [self FrameView__closeButtonOrigin]; - if ([[self window] isKindOfClass:[ToolbarWindow class]]) { - return [(ToolbarWindow*)[self window] windowButtonsPositionWithDefaultPosition:defaultPosition]; - } - return defaultPosition; -} - -- (NSPoint)FrameView__fullScreenButtonOrigin -{ - NSPoint defaultPosition = [self FrameView__fullScreenButtonOrigin]; - if ([[self window] isKindOfClass:[ToolbarWindow class]]) { - return [(ToolbarWindow*)[self window] fullScreenButtonPositionWithDefaultPosition:defaultPosition]; - } - return defaultPosition; -} - -@end - -static NSMutableSet *gSwizzledFrameViewClasses = nil; - -@interface NSWindow(PrivateSetNeedsDisplayInRectMethod) - - (void)_setNeedsDisplayInRect:(NSRect)aRect; -@end - -// This method is on NSThemeFrame starting with 10.10, but since NSThemeFrame -// is not a public class, we declare the method on NSView instead. We only have -// this declaration in order to avoid compiler warnings. -@interface NSView(PrivateAddKnownSubviewMethod) - - (void)_addKnownSubview:(NSView*)aView positioned:(NSWindowOrderingMode)place relativeTo:(NSView*)otherView; -@end - -#if !defined(MAC_OS_X_VERSION_10_10) || \ - MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10 - -@interface NSImage(CapInsets) -- (void)setCapInsets:(NSEdgeInsets)capInsets; -@end - -#endif - -#if !defined(MAC_OS_X_VERSION_10_8) || \ - MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_8 - -@interface NSImage(ImageCreationWithDrawingHandler) -+ (NSImage *)imageWithSize:(NSSize)size - flipped:(BOOL)drawingHandlerShouldBeCalledWithFlippedContext - drawingHandler:(BOOL (^)(NSRect dstRect))drawingHandler; -@end - -#endif - -@interface NSView(NSVisualEffectViewSetMaskImage) -- (void)setMaskImage:(NSImage*)image; -@end - -@interface BaseWindow(Private) -- (void)removeTrackingArea; -- (void)cursorUpdated:(NSEvent*)aEvent; -- (void)updateContentViewSize; -- (void)reflowTitlebarElements; -@end - -@implementation BaseWindow - -// The frame of a window is implemented using undocumented NSView subclasses. -// We offset the window buttons by overriding the methods _closeButtonOrigin -// and _fullScreenButtonOrigin on these frame view classes. The class which is -// used for a window is determined in the window's frameViewClassForStyleMask: -// method, so this is where we make sure that we have swizzled the method on -// all encountered classes. -+ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask -{ - Class frameViewClass = [super frameViewClassForStyleMask:styleMask]; - - if (!gSwizzledFrameViewClasses) { - gSwizzledFrameViewClasses = [[NSMutableSet setWithCapacity:3] retain]; - if (!gSwizzledFrameViewClasses) { - return frameViewClass; - } - } - - static IMP our_closeButtonOrigin = - class_getMethodImplementation([NSView class], - @selector(FrameView__closeButtonOrigin)); - static IMP our_fullScreenButtonOrigin = - class_getMethodImplementation([NSView class], - @selector(FrameView__fullScreenButtonOrigin)); - - if (![gSwizzledFrameViewClasses containsObject:frameViewClass]) { - // Either of these methods might be implemented in both a subclass of - // NSFrameView and one of its own subclasses. Which means that if we - // aren't careful we might end up swizzling the same method twice. - // Since method swizzling involves swapping pointers, this would break - // things. - IMP _closeButtonOrigin = - class_getMethodImplementation(frameViewClass, - @selector(_closeButtonOrigin)); - if (_closeButtonOrigin && _closeButtonOrigin != our_closeButtonOrigin) { - nsToolkit::SwizzleMethods(frameViewClass, @selector(_closeButtonOrigin), - @selector(FrameView__closeButtonOrigin)); - } - IMP _fullScreenButtonOrigin = - class_getMethodImplementation(frameViewClass, - @selector(_fullScreenButtonOrigin)); - if (_fullScreenButtonOrigin && - _fullScreenButtonOrigin != our_fullScreenButtonOrigin) { - nsToolkit::SwizzleMethods(frameViewClass, @selector(_fullScreenButtonOrigin), - @selector(FrameView__fullScreenButtonOrigin)); - } - [gSwizzledFrameViewClasses addObject:frameViewClass]; - } - - return frameViewClass; -} - -- (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag -{ - mDrawsIntoWindowFrame = NO; - [super initWithContentRect:aContentRect styleMask:aStyle backing:aBufferingType defer:aFlag]; - mState = nil; - mActiveTitlebarColor = nil; - mInactiveTitlebarColor = nil; - mScheduledShadowInvalidation = NO; - mDisabledNeedsDisplay = NO; - mDPI = GetDPI(self); - mTrackingArea = nil; - mDirtyRect = NSZeroRect; - mBeingShown = NO; - mDrawTitle = NO; - mBrightTitlebarForeground = NO; - mUseMenuStyle = NO; - [self updateTrackingArea]; - - return self; -} - -// Returns an autoreleased NSImage. -static NSImage* -GetMenuMaskImage() -{ - CGFloat radius = 4.0f; - NSEdgeInsets insets = { 5, 5, 5, 5 }; - NSSize maskSize = { 12, 12 }; - NSImage* maskImage = [NSImage imageWithSize:maskSize flipped:YES drawingHandler:^BOOL(NSRect dstRect) { - NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:dstRect xRadius:radius yRadius:radius]; - [[NSColor colorWithDeviceWhite:1.0 alpha:1.0] set]; - [path fill]; - return YES; - }]; - [maskImage setCapInsets:insets]; - return maskImage; -} - -- (void)swapOutChildViewWrapper:(NSView*)aNewWrapper -{ - [aNewWrapper setFrame:[[self contentView] frame]]; - NSView* childView = [[self mainChildView] retain]; - [childView removeFromSuperview]; - [aNewWrapper addSubview:childView]; - [childView release]; - [super setContentView:aNewWrapper]; -} - -- (void)setUseMenuStyle:(BOOL)aValue -{ - if (!VibrancyManager::SystemSupportsVibrancy()) { - return; - } - - if (aValue && !mUseMenuStyle) { - // Turn on rounded corner masking. - NSView* effectView = VibrancyManager::CreateEffectView(VibrancyType::MENU, YES); - if ([effectView respondsToSelector:@selector(setMaskImage:)]) { - [effectView setMaskImage:GetMenuMaskImage()]; - } - [self swapOutChildViewWrapper:effectView]; - [effectView release]; - } else if (mUseMenuStyle && !aValue) { - // Turn off rounded corner masking. - NSView* wrapper = [[NSView alloc] initWithFrame:NSZeroRect]; - [self swapOutChildViewWrapper:wrapper]; - [wrapper release]; - } - mUseMenuStyle = aValue; -} - -- (void)setBeingShown:(BOOL)aValue -{ - mBeingShown = aValue; -} - -- (BOOL)isBeingShown -{ - return mBeingShown; -} - -- (BOOL)isVisibleOrBeingShown -{ - return [super isVisible] || mBeingShown; -} - -- (void)disableSetNeedsDisplay -{ - mDisabledNeedsDisplay = YES; -} - -- (void)enableSetNeedsDisplay -{ - mDisabledNeedsDisplay = NO; -} - -- (void)dealloc -{ - [mActiveTitlebarColor release]; - [mInactiveTitlebarColor release]; - [self removeTrackingArea]; - ChildViewMouseTracker::OnDestroyWindow(self); - [super dealloc]; -} - -static const NSString* kStateTitleKey = @"title"; -static const NSString* kStateDrawsContentsIntoWindowFrameKey = @"drawsContentsIntoWindowFrame"; -static const NSString* kStateActiveTitlebarColorKey = @"activeTitlebarColor"; -static const NSString* kStateInactiveTitlebarColorKey = @"inactiveTitlebarColor"; -static const NSString* kStateShowsToolbarButton = @"showsToolbarButton"; -static const NSString* kStateCollectionBehavior = @"collectionBehavior"; - -- (void)importState:(NSDictionary*)aState -{ - [self setTitle:[aState objectForKey:kStateTitleKey]]; - [self setDrawsContentsIntoWindowFrame:[[aState objectForKey:kStateDrawsContentsIntoWindowFrameKey] boolValue]]; - [self setTitlebarColor:[aState objectForKey:kStateActiveTitlebarColorKey] forActiveWindow:YES]; - [self setTitlebarColor:[aState objectForKey:kStateInactiveTitlebarColorKey] forActiveWindow:NO]; - [self setShowsToolbarButton:[[aState objectForKey:kStateShowsToolbarButton] boolValue]]; - [self setCollectionBehavior:[[aState objectForKey:kStateCollectionBehavior] unsignedIntValue]]; -} - -- (NSMutableDictionary*)exportState -{ - NSMutableDictionary* state = [NSMutableDictionary dictionaryWithCapacity:10]; - [state setObject:[self title] forKey:kStateTitleKey]; - [state setObject:[NSNumber numberWithBool:[self drawsContentsIntoWindowFrame]] - forKey:kStateDrawsContentsIntoWindowFrameKey]; - NSColor* activeTitlebarColor = [self titlebarColorForActiveWindow:YES]; - if (activeTitlebarColor) { - [state setObject:activeTitlebarColor forKey:kStateActiveTitlebarColorKey]; - } - NSColor* inactiveTitlebarColor = [self titlebarColorForActiveWindow:NO]; - if (inactiveTitlebarColor) { - [state setObject:inactiveTitlebarColor forKey:kStateInactiveTitlebarColorKey]; - } - [state setObject:[NSNumber numberWithBool:[self showsToolbarButton]] - forKey:kStateShowsToolbarButton]; - [state setObject:[NSNumber numberWithUnsignedInt: [self collectionBehavior]] - forKey:kStateCollectionBehavior]; - return state; -} - -- (void)setDrawsContentsIntoWindowFrame:(BOOL)aState -{ - bool changed = (aState != mDrawsIntoWindowFrame); - mDrawsIntoWindowFrame = aState; - if (changed) { - [self updateContentViewSize]; - [self reflowTitlebarElements]; - } -} - -- (BOOL)drawsContentsIntoWindowFrame -{ - return mDrawsIntoWindowFrame; -} - -- (void)setWantsTitleDrawn:(BOOL)aDrawTitle -{ - mDrawTitle = aDrawTitle; -} - -- (BOOL)wantsTitleDrawn -{ - return mDrawTitle; -} - -- (void)setUseBrightTitlebarForeground:(BOOL)aBrightForeground -{ - mBrightTitlebarForeground = aBrightForeground; - [[self standardWindowButton:NSWindowFullScreenButton] setNeedsDisplay:YES]; -} - -- (BOOL)useBrightTitlebarForeground -{ - return mBrightTitlebarForeground; -} - -// Pass nil here to get the default appearance. -- (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive -{ - [aColor retain]; - if (aActive) { - [mActiveTitlebarColor release]; - mActiveTitlebarColor = aColor; - } else { - [mInactiveTitlebarColor release]; - mInactiveTitlebarColor = aColor; - } -} - -- (NSColor*)titlebarColorForActiveWindow:(BOOL)aActive -{ - return aActive ? mActiveTitlebarColor : mInactiveTitlebarColor; -} - -- (void)deferredInvalidateShadow -{ - if (mScheduledShadowInvalidation || [self isOpaque] || ![self hasShadow]) - return; - - [self performSelector:@selector(invalidateShadow) withObject:nil afterDelay:0]; - mScheduledShadowInvalidation = YES; -} - -- (void)invalidateShadow -{ - [super invalidateShadow]; - mScheduledShadowInvalidation = NO; -} - -- (float)getDPI -{ - return mDPI; -} - -- (NSView*)trackingAreaView -{ - NSView* contentView = [self contentView]; - return [contentView superview] ? [contentView superview] : contentView; -} - -- (ChildView*)mainChildView -{ - NSView *contentView = [self contentView]; - NSView* lastView = [[contentView subviews] lastObject]; - if ([lastView isKindOfClass:[ChildView class]]) { - return (ChildView*)lastView; - } - return nil; -} - -- (void)removeTrackingArea -{ - if (mTrackingArea) { - [[self trackingAreaView] removeTrackingArea:mTrackingArea]; - [mTrackingArea release]; - mTrackingArea = nil; - } -} - -- (void)updateTrackingArea -{ - [self removeTrackingArea]; - - NSView* view = [self trackingAreaView]; - const NSTrackingAreaOptions options = - NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways; - mTrackingArea = [[NSTrackingArea alloc] initWithRect:[view bounds] - options:options - owner:self - userInfo:nil]; - [view addTrackingArea:mTrackingArea]; -} - -- (void)mouseEntered:(NSEvent*)aEvent -{ - ChildViewMouseTracker::MouseEnteredWindow(aEvent); -} - -- (void)mouseExited:(NSEvent*)aEvent -{ - ChildViewMouseTracker::MouseExitedWindow(aEvent); -} - -- (void)mouseMoved:(NSEvent*)aEvent -{ - ChildViewMouseTracker::MouseMoved(aEvent); -} - -- (void)cursorUpdated:(NSEvent*)aEvent -{ - // Nothing to do here, but NSTrackingArea wants us to implement this method. -} - -- (void)_setNeedsDisplayInRect:(NSRect)aRect -{ - // Prevent unnecessary invalidations due to moving NSViews (e.g. for plugins) - if (!mDisabledNeedsDisplay) { - // This method is only called by Cocoa, so when we're here, we know that - // it's available and don't need to check whether our superclass responds - // to the selector. - [super _setNeedsDisplayInRect:aRect]; - mDirtyRect = NSUnionRect(mDirtyRect, aRect); - } -} - -- (NSRect)getAndResetNativeDirtyRect -{ - NSRect dirtyRect = mDirtyRect; - mDirtyRect = NSZeroRect; - return dirtyRect; -} - -- (void)updateContentViewSize -{ - NSRect rect = [self contentRectForFrameRect:[self frame]]; - [[self contentView] setFrameSize:rect.size]; -} - -// Possibly move the titlebar buttons. -- (void)reflowTitlebarElements -{ - NSView *frameView = [[self contentView] superview]; - if ([frameView respondsToSelector:@selector(_tileTitlebarAndRedisplay:)]) { - [frameView _tileTitlebarAndRedisplay:NO]; - } -} - -// Override methods that translate between content rect and frame rect. -- (NSRect)contentRectForFrameRect:(NSRect)aRect -{ - if ([self drawsContentsIntoWindowFrame]) { - return aRect; - } - return [super contentRectForFrameRect:aRect]; -} - -- (NSRect)contentRectForFrameRect:(NSRect)aRect styleMask:(NSUInteger)aMask -{ - if ([self drawsContentsIntoWindowFrame]) { - return aRect; - } - if ([super respondsToSelector:@selector(contentRectForFrameRect:styleMask:)]) { - return [super contentRectForFrameRect:aRect styleMask:aMask]; - } else { - return [NSWindow contentRectForFrameRect:aRect styleMask:aMask]; - } -} - -- (NSRect)frameRectForContentRect:(NSRect)aRect -{ - if ([self drawsContentsIntoWindowFrame]) { - return aRect; - } - return [super frameRectForContentRect:aRect]; -} - -- (NSRect)frameRectForContentRect:(NSRect)aRect styleMask:(NSUInteger)aMask -{ - if ([self drawsContentsIntoWindowFrame]) { - return aRect; - } - if ([super respondsToSelector:@selector(frameRectForContentRect:styleMask:)]) { - return [super frameRectForContentRect:aRect styleMask:aMask]; - } else { - return [NSWindow frameRectForContentRect:aRect styleMask:aMask]; - } -} - -- (void)setContentView:(NSView*)aView -{ - [super setContentView:aView]; - - // Now move the contentView to the bottommost layer so that it's guaranteed - // to be under the window buttons. - NSView* frameView = [aView superview]; - [aView removeFromSuperview]; - if ([frameView respondsToSelector:@selector(_addKnownSubview:positioned:relativeTo:)]) { - // 10.10 prints a warning when we call addSubview on the frame view, so we - // silence the warning by calling a private method instead. - [frameView _addKnownSubview:aView positioned:NSWindowBelow relativeTo:nil]; - } else { - [frameView addSubview:aView positioned:NSWindowBelow relativeTo:nil]; - } -} - -- (NSArray*)titlebarControls -{ - // Return all subviews of the frameView which are not the content view. - NSView* frameView = [[self contentView] superview]; - NSMutableArray* array = [[[frameView subviews] mutableCopy] autorelease]; - [array removeObject:[self contentView]]; - return array; -} - -- (BOOL)respondsToSelector:(SEL)aSelector -{ - // Claim the window doesn't respond to this so that the system - // doesn't steal keyboard equivalents for it. Bug 613710. - if (aSelector == @selector(cancelOperation:)) { - return NO; - } - - return [super respondsToSelector:aSelector]; -} - -- (void) doCommandBySelector:(SEL)aSelector -{ - // We override this so that it won't beep if it can't act. - // We want to control the beeping for missing or disabled - // commands ourselves. - [self tryToPerform:aSelector with:nil]; -} - -- (id)accessibilityAttributeValue:(NSString *)attribute -{ - id retval = [super accessibilityAttributeValue:attribute]; - - // The following works around a problem with Text-to-Speech on OS X 10.7. - // See bug 674612 for more info. - // - // When accessibility is off, AXUIElementCopyAttributeValue(), when called - // on an AXApplication object to get its AXFocusedUIElement attribute, - // always returns an AXWindow object (the actual browser window -- never a - // mozAccessible object). This also happens with accessibility turned on, - // if no other object in the browser window has yet been focused. But if - // the browser window has a title bar (as it currently always does), the - // AXWindow object will always have four "accessible" children, one of which - // is an AXStaticText object (the title bar's "title"; the other three are - // the close, minimize and zoom buttons). This means that (for complicated - // reasons, for which see bug 674612) Text-to-Speech on OS X 10.7 will often - // "speak" the window title, no matter what text is selected, or even if no - // text at all is selected. (This always happens when accessibility is off. - // It doesn't happen in Firefox releases because Apple has (on OS X 10.7) - // special-cased the handling of apps whose CFBundleIdentifier is - // org.mozilla.firefox.) - // - // We work around this problem by only returning AXChildren that are - // mozAccessible object or are one of the titlebar's buttons (which - // instantiate subclasses of NSButtonCell). - if ([retval isKindOfClass:[NSArray class]] && - [attribute isEqualToString:@"AXChildren"]) { - NSMutableArray *holder = [NSMutableArray arrayWithCapacity:10]; - [holder addObjectsFromArray:(NSArray *)retval]; - NSUInteger count = [holder count]; - for (NSInteger i = count - 1; i >= 0; --i) { - id item = [holder objectAtIndex:i]; - // Remove anything from holder that isn't one of the titlebar's buttons - // (which instantiate subclasses of NSButtonCell) or a mozAccessible - // object (or one of its subclasses). - if (![item isKindOfClass:[NSButtonCell class]] && - ![item respondsToSelector:@selector(hasRepresentedView)]) { - [holder removeObjectAtIndex:i]; - } - } - retval = [NSArray arrayWithArray:holder]; - } - - return retval; -} - -@end - -// This class allows us to exercise control over the window's title bar. This -// allows for a "unified toolbar" look without having to extend the content -// area into the title bar. It works like this: -// 1) We set the window's style to textured. -// 2) Because of this, the background color applies to the entire window, including -// the titlebar area. For normal textured windows, the default pattern is a -// "brushed metal" image on Tiger and a unified gradient on Leopard. -// 3) We set the background color to a custom NSColor subclass that knows how tall the window is. -// When -set is called on it, it sets a pattern (with a draw callback) as the fill. In that callback, -// it paints the the titlebar and background colors in the correct areas of the context it's given, -// which will fill the entire window (CG will tile it horizontally for us). -// 4) Whenever the window's main state changes and when [window display] is called, -// Cocoa redraws the titlebar using the patternDraw callback function. -// -// This class also provides us with a pill button to show/hide the toolbar up to 10.6. -// -// Drawing the unified gradient in the titlebar and the toolbar works like this: -// 1) In the style sheet we set the toolbar's -moz-appearance to toolbar. -// 2) When the toolbar is visible and we paint the application chrome -// window, the array that Gecko passes nsChildView::UpdateThemeGeometries -// will contain an entry for the widget type NS_THEME_TOOLBAR. -// 3) nsChildView::UpdateThemeGeometries finds the toolbar frame's ToolbarWindow -// and passes the toolbar frame's height to setUnifiedToolbarHeight. -// 4) If the toolbar height has changed, a titlebar redraw is triggered and the -// upper part of the unified gradient is drawn in the titlebar. -// 5) The lower part of the unified gradient in the toolbar is drawn during -// normal window content painting in nsNativeThemeCocoa::DrawUnifiedToolbar. -// -// Whenever the unified gradient is drawn in the titlebar or the toolbar, both -// titlebar height and toolbar height must be known in order to construct the -// correct gradient. But you can only get from the toolbar frame -// to the containing window - the other direction doesn't work. That's why the -// toolbar height is cached in the ToolbarWindow but nsNativeThemeCocoa can simply -// query the window for its titlebar height when drawing the toolbar. -// -// Note that in drawsContentsIntoWindowFrame mode, titlebar drawing works in a -// completely different way: In that mode, the window's mainChildView will -// cover the titlebar completely and nothing that happens in the window -// background will reach the screen. -@implementation ToolbarWindow - -- (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - aStyle = aStyle | NSTexturedBackgroundWindowMask; - if ((self = [super initWithContentRect:aContentRect styleMask:aStyle backing:aBufferingType defer:aFlag])) { - mColor = [[TitlebarAndBackgroundColor alloc] initWithWindow:self]; - // Bypass our guard method below. - [super setBackgroundColor:mColor]; - mBackgroundColor = [[NSColor whiteColor] retain]; - - mUnifiedToolbarHeight = 22.0f; - mWindowButtonsRect = NSZeroRect; - mFullScreenButtonRect = NSZeroRect; - - // setBottomCornerRounded: is a private API call, so we check to make sure - // we respond to it just in case. - if ([self respondsToSelector:@selector(setBottomCornerRounded:)]) - [self setBottomCornerRounded:YES]; - - [self setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge]; - [self setContentBorderThickness:0.0f forEdge:NSMaxYEdge]; - } - return self; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (void)dealloc -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [super setBackgroundColor:[NSColor whiteColor]]; - [mColor release]; - [mBackgroundColor release]; - [mTitlebarView release]; - [super dealloc]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive -{ - [super setTitlebarColor:aColor forActiveWindow:aActive]; - [self setTitlebarNeedsDisplayInRect:[self titlebarRect]]; -} - -- (void)setBackgroundColor:(NSColor*)aColor -{ - [aColor retain]; - [mBackgroundColor release]; - mBackgroundColor = aColor; -} - -- (NSColor*)windowBackgroundColor -{ - return mBackgroundColor; -} - -- (void)setTemporaryBackgroundColor -{ - [super setBackgroundColor:[NSColor whiteColor]]; -} - -- (void)restoreBackgroundColor -{ - [super setBackgroundColor:mBackgroundColor]; -} - -- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect -{ - [self setTitlebarNeedsDisplayInRect:aRect sync:NO]; -} - -- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect sync:(BOOL)aSync -{ - NSRect titlebarRect = [self titlebarRect]; - NSRect rect = NSIntersectionRect(titlebarRect, aRect); - if (NSIsEmptyRect(rect)) - return; - - NSView* borderView = [[self contentView] superview]; - if (!borderView) - return; - - if (aSync) { - [borderView displayRect:rect]; - } else { - [borderView setNeedsDisplayInRect:rect]; - } -} - -- (NSRect)titlebarRect -{ - CGFloat titlebarHeight = [self titlebarHeight]; - return NSMakeRect(0, [self frame].size.height - titlebarHeight, - [self frame].size.width, titlebarHeight); -} - -// Returns the unified height of titlebar + toolbar. -- (CGFloat)unifiedToolbarHeight -{ - return mUnifiedToolbarHeight; -} - -- (CGFloat)titlebarHeight -{ - // We use the original content rect here, not what we return from - // [self contentRectForFrameRect:], because that would give us a - // titlebarHeight of zero in drawsContentsIntoWindowFrame mode. - NSRect frameRect = [self frame]; - NSRect originalContentRect = [NSWindow contentRectForFrameRect:frameRect styleMask:[self styleMask]]; - return NSMaxY(frameRect) - NSMaxY(originalContentRect); -} - -// Stores the complete height of titlebar + toolbar. -- (void)setUnifiedToolbarHeight:(CGFloat)aHeight -{ - if (aHeight == mUnifiedToolbarHeight) - return; - - mUnifiedToolbarHeight = aHeight; - - if (![self drawsContentsIntoWindowFrame]) { - // Redraw the title bar. If we're inside painting, we'll do it right now, - // otherwise we'll just invalidate it. - BOOL needSyncRedraw = ([NSView focusView] != nil); - [self setTitlebarNeedsDisplayInRect:[self titlebarRect] sync:needSyncRedraw]; - } -} - -// Extending the content area into the title bar works by resizing the -// mainChildView so that it covers the titlebar. -- (void)setDrawsContentsIntoWindowFrame:(BOOL)aState -{ - BOOL stateChanged = ([self drawsContentsIntoWindowFrame] != aState); - [super setDrawsContentsIntoWindowFrame:aState]; - if (stateChanged && [[self delegate] isKindOfClass:[WindowDelegate class]]) { - // Here we extend / shrink our mainChildView. We do that by firing a resize - // event which will cause the ChildView to be resized to the rect returned - // by nsCocoaWindow::GetClientBounds. GetClientBounds bases its return - // value on what we return from drawsContentsIntoWindowFrame. - WindowDelegate *windowDelegate = (WindowDelegate *)[self delegate]; - nsCocoaWindow *geckoWindow = [windowDelegate geckoWidget]; - if (geckoWindow) { - // Re-layout our contents. - geckoWindow->ReportSizeEvent(); - } - - // Resizing the content area causes a reflow which would send a synthesized - // mousemove event to the old mouse position relative to the top left - // corner of the content area. But the mouse has shifted relative to the - // content area, so that event would have wrong position information. So - // we'll send a mouse move event with the correct new position. - ChildViewMouseTracker::ResendLastMouseMoveEvent(); - } -} - -- (void)setWantsTitleDrawn:(BOOL)aDrawTitle -{ - [super setWantsTitleDrawn:aDrawTitle]; - [self setTitlebarNeedsDisplayInRect:[self titlebarRect]]; -} - -- (void)setSheetAttachmentPosition:(CGFloat)aY -{ - CGFloat topMargin = aY - [self titlebarHeight]; - [self setContentBorderThickness:topMargin forEdge:NSMaxYEdge]; -} - -- (void)placeWindowButtons:(NSRect)aRect -{ - if (!NSEqualRects(mWindowButtonsRect, aRect)) { - mWindowButtonsRect = aRect; - [self reflowTitlebarElements]; - } -} - -- (NSPoint)windowButtonsPositionWithDefaultPosition:(NSPoint)aDefaultPosition -{ - NSInteger styleMask = [self styleMask]; - if ([self drawsContentsIntoWindowFrame] && - !(styleMask & NSFullScreenWindowMask) && (styleMask & NSTitledWindowMask)) { - if (NSIsEmptyRect(mWindowButtonsRect)) { - // Empty rect. Let's hide the buttons. - // Position is in non-flipped window coordinates. Using frame's height - // for the vertical coordinate will move the buttons above the window, - // making them invisible. - return NSMakePoint(0, [self frame].size.height); - } - return NSMakePoint(mWindowButtonsRect.origin.x, mWindowButtonsRect.origin.y); - } - return aDefaultPosition; -} - -- (void)placeFullScreenButton:(NSRect)aRect -{ - if (!NSEqualRects(mFullScreenButtonRect, aRect)) { - mFullScreenButtonRect = aRect; - [self reflowTitlebarElements]; - } -} - -- (NSPoint)fullScreenButtonPositionWithDefaultPosition:(NSPoint)aDefaultPosition -{ - if ([self drawsContentsIntoWindowFrame] && !NSIsEmptyRect(mFullScreenButtonRect)) { - return NSMakePoint(std::min(mFullScreenButtonRect.origin.x, aDefaultPosition.x), - std::min(mFullScreenButtonRect.origin.y, aDefaultPosition.y)); - } - return aDefaultPosition; -} - -// Returning YES here makes the setShowsToolbarButton method work even though -// the window doesn't contain an NSToolbar. -- (BOOL)_hasToolbar -{ - return YES; -} - -// Dispatch a toolbar pill button clicked message to Gecko. -- (void)_toolbarPillButtonClicked:(id)sender -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - RollUpPopups(); - - if ([[self delegate] isKindOfClass:[WindowDelegate class]]) { - WindowDelegate *windowDelegate = (WindowDelegate *)[self delegate]; - nsCocoaWindow *geckoWindow = [windowDelegate geckoWidget]; - if (!geckoWindow) - return; - - nsIWidgetListener* listener = geckoWindow->GetWidgetListener(); - if (listener) - listener->OSToolbarButtonPressed(); - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// Retain and release "self" to avoid crashes when our widget (and its native -// window) is closed as a result of processing a key equivalent (e.g. -// Command+w or Command+q). This workaround is only needed for a window -// that can become key. -- (BOOL)performKeyEquivalent:(NSEvent*)theEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - NSWindow *nativeWindow = [self retain]; - BOOL retval = [super performKeyEquivalent:theEvent]; - [nativeWindow release]; - return retval; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); -} - -- (void)sendEvent:(NSEvent *)anEvent -{ - NSEventType type = [anEvent type]; - - switch (type) { - case NSScrollWheel: - case NSLeftMouseDown: - case NSLeftMouseUp: - case NSRightMouseDown: - case NSRightMouseUp: - case NSOtherMouseDown: - case NSOtherMouseUp: - case NSMouseMoved: - case NSLeftMouseDragged: - case NSRightMouseDragged: - case NSOtherMouseDragged: - { - // Drop all mouse events if a modal window has appeared above us. - // This helps make us behave as if the OS were running a "real" modal - // event loop. - id delegate = [self delegate]; - if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) { - nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget]; - if (widget) { - if (gGeckoAppModalWindowList && (widget != gGeckoAppModalWindowList->window)) - return; - if (widget->HasModalDescendents()) - return; - } - } - break; - } - default: - break; - } - - [super sendEvent:anEvent]; -} - -@end - -// Custom NSColor subclass where most of the work takes place for drawing in -// the titlebar area. Not used in drawsContentsIntoWindowFrame mode. -@implementation TitlebarAndBackgroundColor - -- (id)initWithWindow:(ToolbarWindow*)aWindow -{ - if ((self = [super init])) { - mWindow = aWindow; // weak ref to avoid a cycle - } - return self; -} - -static void -DrawNativeTitlebar(CGContextRef aContext, CGRect aTitlebarRect, - CGFloat aUnifiedToolbarHeight, BOOL aIsMain) -{ - nsNativeThemeCocoa::DrawNativeTitlebar(aContext, aTitlebarRect, aUnifiedToolbarHeight, aIsMain, NO); - - // The call to CUIDraw doesn't draw the top pixel strip at some window widths. - // We don't want to have a flickering transparent line, so we overdraw it. - CGContextSetRGBFillColor(aContext, 0.95, 0.95, 0.95, 1); - CGContextFillRect(aContext, CGRectMake(0, CGRectGetMaxY(aTitlebarRect) - 1, - aTitlebarRect.size.width, 1)); -} - -// Pattern draw callback for standard titlebar gradients and solid titlebar colors -static void -TitlebarDrawCallback(void* aInfo, CGContextRef aContext) -{ - ToolbarWindow *window = (ToolbarWindow*)aInfo; - if (![window drawsContentsIntoWindowFrame]) { - NSRect titlebarRect = [window titlebarRect]; - BOOL isMain = [window isMainWindow]; - NSColor *titlebarColor = [window titlebarColorForActiveWindow:isMain]; - if (!titlebarColor) { - // If the titlebar color is nil, draw the default titlebar shading. - DrawNativeTitlebar(aContext, NSRectToCGRect(titlebarRect), - [window unifiedToolbarHeight], isMain); - } else { - // If the titlebar color is not nil, just set and draw it normally. - [NSGraphicsContext saveGraphicsState]; - [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:aContext flipped:NO]]; - [titlebarColor set]; - NSRectFill(titlebarRect); - [NSGraphicsContext restoreGraphicsState]; - } - } -} - -- (void)setFill -{ - float patternWidth = [mWindow frame].size.width; - - CGPatternCallbacks callbacks = {0, &TitlebarDrawCallback, NULL}; - CGPatternRef pattern = CGPatternCreate(mWindow, CGRectMake(0.0f, 0.0f, patternWidth, [mWindow frame].size.height), - CGAffineTransformIdentity, patternWidth, [mWindow frame].size.height, - kCGPatternTilingConstantSpacing, true, &callbacks); - - // Set the pattern as the fill, which is what we were asked to do. All our - // drawing will take place in the patternDraw callback. - CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL); - CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; - CGContextSetFillColorSpace(context, patternSpace); - CGColorSpaceRelease(patternSpace); - CGFloat component = 1.0f; - CGContextSetFillPattern(context, pattern, &component); - CGPatternRelease(pattern); -} - -- (void)set -{ - [self setFill]; -} - -- (NSString*)colorSpaceName -{ - return NSDeviceRGBColorSpace; -} - -@end - -@implementation PopupWindow - -- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask - backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - mIsContextMenu = false; - return [super initWithContentRect:contentRect styleMask:styleMask - backing:bufferingType defer:deferCreation]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (BOOL)isContextMenu -{ - return mIsContextMenu; -} - -- (void)setIsContextMenu:(BOOL)flag -{ - mIsContextMenu = flag; -} - -- (BOOL)canBecomeMainWindow -{ - // This is overriden because the default is 'yes' when a titlebar is present. - return NO; -} - -@end - -// According to Apple's docs on [NSWindow canBecomeKeyWindow] and [NSWindow -// canBecomeMainWindow], windows without a title bar or resize bar can't (by -// default) become key or main. But if a window can't become key, it can't -// accept keyboard input (bmo bug 393250). And it should also be possible for -// an otherwise "ordinary" window to become main. We need to override these -// two methods to make this happen. -@implementation BorderlessWindow - -- (BOOL)canBecomeKeyWindow -{ - return YES; -} - -- (void)sendEvent:(NSEvent *)anEvent -{ - NSEventType type = [anEvent type]; - - switch (type) { - case NSScrollWheel: - case NSLeftMouseDown: - case NSLeftMouseUp: - case NSRightMouseDown: - case NSRightMouseUp: - case NSOtherMouseDown: - case NSOtherMouseUp: - case NSMouseMoved: - case NSLeftMouseDragged: - case NSRightMouseDragged: - case NSOtherMouseDragged: - { - // Drop all mouse events if a modal window has appeared above us. - // This helps make us behave as if the OS were running a "real" modal - // event loop. - id delegate = [self delegate]; - if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) { - nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget]; - if (widget) { - if (gGeckoAppModalWindowList && (widget != gGeckoAppModalWindowList->window)) - return; - if (widget->HasModalDescendents()) - return; - } - } - break; - } - default: - break; - } - - [super sendEvent:anEvent]; -} - -// Apple's doc on this method says that the NSWindow class's default is not to -// become main if the window isn't "visible" -- so we should replicate that -// behavior here. As best I can tell, the [NSWindow isVisible] method is an -// accurate test of what Apple means by "visibility". -- (BOOL)canBecomeMainWindow -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - if (![self isVisible]) - return NO; - return YES; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); -} - -// Retain and release "self" to avoid crashes when our widget (and its native -// window) is closed as a result of processing a key equivalent (e.g. -// Command+w or Command+q). This workaround is only needed for a window -// that can become key. -- (BOOL)performKeyEquivalent:(NSEvent*)theEvent -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - NSWindow *nativeWindow = [self retain]; - BOOL retval = [super performKeyEquivalent:theEvent]; - [nativeWindow release]; - return retval; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); -} - -@end diff --git a/widget/cocoa/nsColorPicker.h b/widget/cocoa/nsColorPicker.h deleted file mode 100644 index 4b3e262188..0000000000 --- a/widget/cocoa/nsColorPicker.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -*- 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 nsColorPicker_h_ -#define nsColorPicker_h_ - -#include "nsIColorPicker.h" -#include "nsString.h" -#include "nsCOMPtr.h" - -class nsIColorPickerShownCallback; -class mozIDOMWindowProxy; -@class NSColorPanelWrapper; -@class NSColor; - -class nsColorPicker final : public nsIColorPicker -{ -public: - NS_DECL_ISUPPORTS - - NS_IMETHOD Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle, - const nsAString& aInitialColor) override; - NS_IMETHOD Open(nsIColorPickerShownCallback* aCallback) override; - - // For NSColorPanelWrapper. - void Update(NSColor* aColor); - // Call this method if you are done with this input, but the color picker needs to - // stay open as it will be associated to another input - void DoneWithRetarget(); - // Same as DoneWithRetarget + clean the static instance of sColorPanelWrapper, - // as it is not needed anymore for now - void Done(); - -private: - ~nsColorPicker(); - - static NSColor* GetNSColorFromHexString(const nsAString& aColor); - static void GetHexStringFromNSColor(NSColor* aColor, nsAString& aResult); - - static NSColorPanelWrapper* sColorPanelWrapper; - - nsString mTitle; - nsString mColor; - nsCOMPtr<nsIColorPickerShownCallback> mCallback; -}; - -#endif // nsColorPicker_h_ diff --git a/widget/cocoa/nsColorPicker.mm b/widget/cocoa/nsColorPicker.mm deleted file mode 100644 index 263ea349b0..0000000000 --- a/widget/cocoa/nsColorPicker.mm +++ /dev/null @@ -1,188 +0,0 @@ -/* -*- 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/. */ - -#import <Cocoa/Cocoa.h> - -#include "nsColorPicker.h" -#include "nsCocoaUtils.h" -#include "nsThreadUtils.h" - -using namespace mozilla; - -static unsigned int -HexStrToInt(NSString* str) -{ - unsigned int result = 0; - - for (unsigned int i = 0; i < [str length]; ++i) { - char c = [str characterAtIndex:i]; - result *= 16; - if (c >= '0' && c <= '9') { - result += c - '0'; - } else if (c >= 'A' && c <= 'F') { - result += 10 + (c - 'A'); - } else { - result += 10 + (c - 'a'); - } - } - - return result; -} - -@interface NSColorPanelWrapper : NSObject <NSWindowDelegate> -{ - NSColorPanel* mColorPanel; - nsColorPicker* mColorPicker; -} -- (id)initWithPicker:(nsColorPicker*)aPicker; -- (void)open:(NSColor*)aInitialColor title:(NSString*)aTitle; -- (void)retarget:(nsColorPicker*)aPicker; -- (void)colorChanged:(NSColorPanel*)aPanel; -@end - -@implementation NSColorPanelWrapper -- (id)initWithPicker:(nsColorPicker*)aPicker -{ - mColorPicker = aPicker; - mColorPanel = [NSColorPanel sharedColorPanel]; - - self = [super init]; - return self; -} - -- (void)open:(NSColor*)aInitialColor title:(NSString*)aTitle -{ - [mColorPanel setTarget:self]; - [mColorPanel setAction:@selector(colorChanged:)]; - [mColorPanel setDelegate:self]; - [mColorPanel setTitle:aTitle]; - [mColorPanel setColor:aInitialColor]; - [mColorPanel makeKeyAndOrderFront:nil]; -} - -- (void)colorChanged:(NSColorPanel*)aPanel -{ - mColorPicker->Update([mColorPanel color]); -} - -- (void)windowWillClose:(NSNotification*)aNotification -{ - mColorPicker->Done(); -} - -- (void)retarget:(nsColorPicker*)aPicker -{ - mColorPicker->DoneWithRetarget(); - mColorPicker = aPicker; -} - -- (void)dealloc -{ - [mColorPanel setTarget:nil]; - [mColorPanel setAction:nil]; - [mColorPanel setDelegate:nil]; - - mColorPanel = nil; - mColorPicker = nullptr; - - [super dealloc]; -} -@end - -NS_IMPL_ISUPPORTS(nsColorPicker, nsIColorPicker) - -NSColorPanelWrapper* nsColorPicker::sColorPanelWrapper = nullptr; - -nsColorPicker::~nsColorPicker() -{ -} - -NS_IMETHODIMP -nsColorPicker::Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle, - const nsAString& aInitialColor) -{ - MOZ_ASSERT(NS_IsMainThread(), - "Color pickers can only be opened from main thread currently"); - mTitle = aTitle; - mColor = aInitialColor; - - if (sColorPanelWrapper) { - // Update current wrapper to target the new input instead - [sColorPanelWrapper retarget:this]; - } else { - // Create a brand new color panel wrapper - sColorPanelWrapper = [[NSColorPanelWrapper alloc] initWithPicker:this]; - } - return NS_OK; -} - -/* static */ NSColor* -nsColorPicker::GetNSColorFromHexString(const nsAString& aColor) -{ - NSString* str = nsCocoaUtils::ToNSString(aColor); - - double red = HexStrToInt([str substringWithRange:NSMakeRange(1, 2)]) / 255.0; - double green = HexStrToInt([str substringWithRange:NSMakeRange(3, 2)]) / 255.0; - double blue = HexStrToInt([str substringWithRange:NSMakeRange(5, 2)]) / 255.0; - - return [NSColor colorWithDeviceRed: red green: green blue: blue alpha: 1.0]; -} - -/* static */ void -nsColorPicker::GetHexStringFromNSColor(NSColor* aColor, nsAString& aResult) -{ - CGFloat redFloat, greenFloat, blueFloat; - - NSColor* color = aColor; - @try { - [color getRed:&redFloat green:&greenFloat blue:&blueFloat alpha: nil]; - } @catch (NSException* e) { - color = [color colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]]; - [color getRed:&redFloat green:&greenFloat blue:&blueFloat alpha: nil]; - } - - nsCocoaUtils::GetStringForNSString([NSString stringWithFormat:@"#%02x%02x%02x", - (int)(redFloat * 255), - (int)(greenFloat * 255), - (int)(blueFloat * 255)], - aResult); -} - -NS_IMETHODIMP -nsColorPicker::Open(nsIColorPickerShownCallback* aCallback) -{ - MOZ_ASSERT(aCallback); - mCallback = aCallback; - - [sColorPanelWrapper open:GetNSColorFromHexString(mColor) - title:nsCocoaUtils::ToNSString(mTitle)]; - - NS_ADDREF_THIS(); - - return NS_OK; -} - -void -nsColorPicker::Update(NSColor* aColor) -{ - GetHexStringFromNSColor(aColor, mColor); - mCallback->Update(mColor); -} - -void -nsColorPicker::DoneWithRetarget() -{ - mCallback->Done(EmptyString()); - mCallback = nullptr; - NS_RELEASE_THIS(); -} - -void -nsColorPicker::Done() -{ - [sColorPanelWrapper release]; - sColorPanelWrapper = nullptr; - DoneWithRetarget(); -} diff --git a/widget/cocoa/nsCursorManager.h b/widget/cocoa/nsCursorManager.h deleted file mode 100644 index 6dba8f9034..0000000000 --- a/widget/cocoa/nsCursorManager.h +++ /dev/null @@ -1,65 +0,0 @@ -/* 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 nsCursorManager_h_ -#define nsCursorManager_h_ - -#import <Foundation/Foundation.h> -#include "nsIWidget.h" -#include "nsMacCursor.h" - -/*! @class nsCursorManager - @abstract Singleton service provides access to all cursors available in the application. - @discussion Use <code>nsCusorManager</code> to set the current cursor using an XP <code>nsCusor</code> enum value. - <code>nsCursorManager</code> encapsulates the details of setting different types of cursors, animating - cursors and cleaning up cursors when they are no longer in use. - */ -@interface nsCursorManager : NSObject -{ - @private - NSMutableDictionary *mCursors; - nsMacCursor *mCurrentMacCursor; -} - -/*! @method setCursor: - @abstract Sets the current cursor. - @discussion Sets the current cursor to the cursor indicated by the XP cursor constant given as an argument. - Resources associated with the previous cursor are cleaned up. - @param aCursor the cursor to use -*/ -- (nsresult) setCursor: (nsCursor) aCursor; - -/*! @method setCursorWithImage:hotSpotX:hotSpotY: - @abstract Sets the current cursor to a custom image - @discussion Sets the current cursor to the cursor given by the aCursorImage argument. - Resources associated with the previous cursor are cleaned up. - @param aCursorImage the cursor image to use - @param aHotSpotX the x coordinate of the cursor's hotspot - @param aHotSpotY the y coordinate of the cursor's hotspot - @param scaleFactor the scale factor of the target display (2 for a retina display) - */ -- (nsresult) setCursorWithImage: (imgIContainer*) aCursorImage hotSpotX: (uint32_t) aHotspotX hotSpotY: (uint32_t) aHotspotY scaleFactor: (CGFloat) scaleFactor; - - -/*! @method sharedInstance - @abstract Get the Singleton instance of the cursor manager. - @discussion Use this method to obtain a reference to the cursor manager. - @result a reference to the cursor manager -*/ -+ (nsCursorManager *) sharedInstance; - -/*! @method dispose - @abstract Releases the shared instance of the cursor manager. - @discussion Use dispose to clean up the cursor manager and associated cursors. -*/ -+ (void) dispose; -@end - -@interface NSCursor (Undocumented) -// busyButClickableCursor is an undocumented NSCursor API, but has been in use since -// at least OS X 10.4 and through 10.9. -+ (NSCursor*)busyButClickableCursor; -@end - -#endif // nsCursorManager_h_ diff --git a/widget/cocoa/nsCursorManager.mm b/widget/cocoa/nsCursorManager.mm deleted file mode 100644 index c4281a438a..0000000000 --- a/widget/cocoa/nsCursorManager.mm +++ /dev/null @@ -1,308 +0,0 @@ -/* 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/. */ - -#include "imgIContainer.h" -#include "nsCocoaUtils.h" -#include "nsCursorManager.h" -#include "nsObjCExceptions.h" -#include <math.h> - -static nsCursorManager *gInstance; -static CGFloat sCursorScaleFactor = 0.0f; -static imgIContainer *sCursorImgContainer = nullptr; -static const nsCursor sCustomCursor = eCursorCount; - -/*! @category nsCursorManager(PrivateMethods) - Private methods for the cursor manager class. -*/ -@interface nsCursorManager(PrivateMethods) -/*! @method getCursor: - @abstract Get a reference to the native Mac representation of a cursor. - @discussion Gets a reference to the Mac native implementation of a cursor. - If the cursor has been requested before, it is retreived from the cursor cache, - otherwise it is created and cached. - @param aCursor the cursor to get - @result the Mac native implementation of the cursor -*/ -- (nsMacCursor *) getCursor: (nsCursor) aCursor; - -/*! @method setMacCursor: - @abstract Set the current Mac native cursor - @discussion Sets the current cursor - this routine is what actually causes the cursor to change. - The argument is retained and the old cursor is released. - @param aMacCursor the cursor to set - @result NS_OK - */ -- (nsresult) setMacCursor: (nsMacCursor*) aMacCursor; - -/*! @method createCursor: - @abstract Create a Mac native representation of a cursor. - @discussion Creates a version of the Mac native representation of this cursor - @param aCursor the cursor to create - @result the Mac native implementation of the cursor -*/ -+ (nsMacCursor *) createCursor: (enum nsCursor) aCursor; - -@end - -@implementation nsCursorManager - -+ (nsCursorManager *) sharedInstance -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - if (!gInstance) { - gInstance = [[nsCursorManager alloc] init]; - } - return gInstance; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -+ (void) dispose -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [gInstance release]; - gInstance = nil; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -+ (nsMacCursor *) createCursor: (enum nsCursor) aCursor -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - switch(aCursor) - { - SEL cursorSelector; - case eCursor_standard: - return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor]; - case eCursor_wait: - case eCursor_spinning: - { - return [nsMacCursor cursorWithCursor:[NSCursor busyButClickableCursor] type:aCursor]; - } - case eCursor_select: - return [nsMacCursor cursorWithCursor:[NSCursor IBeamCursor] type:aCursor]; - case eCursor_hyperlink: - return [nsMacCursor cursorWithCursor:[NSCursor pointingHandCursor] type:aCursor]; - case eCursor_crosshair: - return [nsMacCursor cursorWithCursor:[NSCursor crosshairCursor] type:aCursor]; - case eCursor_move: - return [nsMacCursor cursorWithImageNamed:@"move" hotSpot:NSMakePoint(12,12) type:aCursor]; - case eCursor_help: - return [nsMacCursor cursorWithImageNamed:@"help" hotSpot:NSMakePoint(12,12) type:aCursor]; - case eCursor_copy: - cursorSelector = @selector(dragCopyCursor); - return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ? - [NSCursor performSelector:cursorSelector] : - [NSCursor arrowCursor] type:aCursor]; - case eCursor_alias: - cursorSelector = @selector(dragLinkCursor); - return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ? - [NSCursor performSelector:cursorSelector] : - [NSCursor arrowCursor] type:aCursor]; - case eCursor_context_menu: - cursorSelector = @selector(contextualMenuCursor); - return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ? - [NSCursor performSelector:cursorSelector] : - [NSCursor arrowCursor] type:aCursor]; - case eCursor_cell: - return [nsMacCursor cursorWithImageNamed:@"cell" hotSpot:NSMakePoint(12,12) type:aCursor]; - case eCursor_grab: - return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor] type:aCursor]; - case eCursor_grabbing: - return [nsMacCursor cursorWithCursor:[NSCursor closedHandCursor] type:aCursor]; - case eCursor_zoom_in: - return [nsMacCursor cursorWithImageNamed:@"zoomIn" hotSpot:NSMakePoint(10,10) type:aCursor]; - case eCursor_zoom_out: - return [nsMacCursor cursorWithImageNamed:@"zoomOut" hotSpot:NSMakePoint(10,10) type:aCursor]; - case eCursor_vertical_text: - return [nsMacCursor cursorWithImageNamed:@"vtIBeam" hotSpot:NSMakePoint(12,11) type:aCursor]; - case eCursor_all_scroll: - return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor] type:aCursor]; - case eCursor_not_allowed: - case eCursor_no_drop: - cursorSelector = @selector(operationNotAllowedCursor); - return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ? - [NSCursor performSelector:cursorSelector] : - [NSCursor arrowCursor] type:aCursor]; - // Resize Cursors: - // North - case eCursor_n_resize: - return [nsMacCursor cursorWithCursor:[NSCursor resizeUpCursor] type:aCursor]; - // North East - case eCursor_ne_resize: - return [nsMacCursor cursorWithImageNamed:@"sizeNE" hotSpot:NSMakePoint(12,11) type:aCursor]; - // East - case eCursor_e_resize: - return [nsMacCursor cursorWithCursor:[NSCursor resizeRightCursor] type:aCursor]; - // South East - case eCursor_se_resize: - return [nsMacCursor cursorWithImageNamed:@"sizeSE" hotSpot:NSMakePoint(12,12) type:aCursor]; - // South - case eCursor_s_resize: - return [nsMacCursor cursorWithCursor:[NSCursor resizeDownCursor] type:aCursor]; - // South West - case eCursor_sw_resize: - return [nsMacCursor cursorWithImageNamed:@"sizeSW" hotSpot:NSMakePoint(10,12) type:aCursor]; - // West - case eCursor_w_resize: - return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftCursor] type:aCursor]; - // North West - case eCursor_nw_resize: - return [nsMacCursor cursorWithImageNamed:@"sizeNW" hotSpot:NSMakePoint(11,11) type:aCursor]; - // North & South - case eCursor_ns_resize: - return [nsMacCursor cursorWithCursor:[NSCursor resizeUpDownCursor] type:aCursor]; - // East & West - case eCursor_ew_resize: - return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftRightCursor] type:aCursor]; - // North East & South West - case eCursor_nesw_resize: - return [nsMacCursor cursorWithImageNamed:@"sizeNESW" hotSpot:NSMakePoint(12,12) type:aCursor]; - // North West & South East - case eCursor_nwse_resize: - return [nsMacCursor cursorWithImageNamed:@"sizeNWSE" hotSpot:NSMakePoint(12,12) type:aCursor]; - // Column Resize - case eCursor_col_resize: - return [nsMacCursor cursorWithImageNamed:@"colResize" hotSpot:NSMakePoint(12,12) type:aCursor]; - // Row Resize - case eCursor_row_resize: - return [nsMacCursor cursorWithImageNamed:@"rowResize" hotSpot:NSMakePoint(12,12) type:aCursor]; - default: - return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor]; - } - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (id) init -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - if ((self = [super init])) { - mCursors = [[NSMutableDictionary alloc] initWithCapacity:25]; - } - return self; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (nsresult) setCursor: (enum nsCursor) aCursor -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // Some plugins mess with our cursors and set a cursor that even - // [NSCursor currentCursor] doesn't know about. In case that happens, just - // reset the state. - [[NSCursor currentCursor] set]; - - nsCursor oldType = [mCurrentMacCursor type]; - if (oldType != aCursor) { - if (aCursor == eCursor_none) { - [NSCursor hide]; - } else if (oldType == eCursor_none) { - [NSCursor unhide]; - } - } - [self setMacCursor:[self getCursor:aCursor]]; - - // if a custom cursor was previously set, release sCursorImgContainer - if (oldType == sCustomCursor) { - NS_IF_RELEASE(sCursorImgContainer); - } - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -- (nsresult) setMacCursor: (nsMacCursor*) aMacCursor -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (mCurrentMacCursor != aMacCursor || ![mCurrentMacCursor isSet]) { - [aMacCursor retain]; - [mCurrentMacCursor unset]; - [aMacCursor set]; - [mCurrentMacCursor release]; - mCurrentMacCursor = aMacCursor; - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -- (nsresult) setCursorWithImage: (imgIContainer*) aCursorImage hotSpotX: (uint32_t) aHotspotX hotSpotY: (uint32_t) aHotspotY scaleFactor: (CGFloat) scaleFactor -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - // As the user moves the mouse, this gets called repeatedly with the same aCursorImage - if (sCursorImgContainer == aCursorImage && sCursorScaleFactor == scaleFactor && mCurrentMacCursor) { - [self setMacCursor:mCurrentMacCursor]; - return NS_OK; - } - - [[NSCursor currentCursor] set]; - int32_t width = 0, height = 0; - aCursorImage->GetWidth(&width); - aCursorImage->GetHeight(&height); - // prevent DoS attacks - if (width > 128 || height > 128) { - return NS_OK; - } - - NSImage *cursorImage; - nsresult rv = nsCocoaUtils::CreateNSImageFromImageContainer(aCursorImage, imgIContainer::FRAME_FIRST, &cursorImage, scaleFactor); - if (NS_FAILED(rv) || !cursorImage) { - return NS_ERROR_FAILURE; - } - - // if the hotspot is nonsensical, make it 0,0 - aHotspotX = (aHotspotX > (uint32_t)width - 1) ? 0 : aHotspotX; - aHotspotY = (aHotspotY > (uint32_t)height - 1) ? 0 : aHotspotY; - - NSPoint hotSpot = ::NSMakePoint(aHotspotX, aHotspotY); - [self setMacCursor:[nsMacCursor cursorWithCursor:[[NSCursor alloc] initWithImage:cursorImage hotSpot:hotSpot] type:sCustomCursor]]; - [cursorImage release]; - - NS_IF_RELEASE(sCursorImgContainer); - sCursorImgContainer = aCursorImage; - sCursorScaleFactor = scaleFactor; - NS_ADDREF(sCursorImgContainer); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -- (nsMacCursor *) getCursor: (enum nsCursor) aCursor -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - nsMacCursor * result = [mCursors objectForKey:[NSNumber numberWithInt:aCursor]]; - if (!result) { - result = [nsCursorManager createCursor:aCursor]; - [mCursors setObject:result forKey:[NSNumber numberWithInt:aCursor]]; - } - return result; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (void) dealloc -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [mCurrentMacCursor unset]; - [mCurrentMacCursor release]; - [mCursors release]; - NS_IF_RELEASE(sCursorImgContainer); - [super dealloc]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -@end diff --git a/widget/cocoa/nsDeviceContextSpecX.h b/widget/cocoa/nsDeviceContextSpecX.h deleted file mode 100644 index 2df52418a7..0000000000 --- a/widget/cocoa/nsDeviceContextSpecX.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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 nsDeviceContextSpecX_h_ -#define nsDeviceContextSpecX_h_ - -#include "nsIDeviceContextSpec.h" - -#include <ApplicationServices/ApplicationServices.h> - -class nsDeviceContextSpecX : public nsIDeviceContextSpec -{ -public: - NS_DECL_ISUPPORTS - - nsDeviceContextSpecX(); - - NS_IMETHOD Init(nsIWidget *aWidget, nsIPrintSettings* aPS, bool aIsPrintPreview) override; - virtual already_AddRefed<PrintTarget> MakePrintTarget() final; - NS_IMETHOD BeginDocument(const nsAString& aTitle, - const nsAString& aPrintToFileName, - int32_t aStartPage, - int32_t aEndPage) override; - NS_IMETHOD EndDocument() override; - NS_IMETHOD BeginPage() override; - NS_IMETHOD EndPage() override; - - void GetPaperRect(double* aTop, double* aLeft, double* aBottom, double* aRight); - -protected: - virtual ~nsDeviceContextSpecX(); - -protected: - PMPrintSession mPrintSession; // printing context. - PMPageFormat mPageFormat; // page format. - PMPrintSettings mPrintSettings; // print settings. -}; - -#endif //nsDeviceContextSpecX_h_ diff --git a/widget/cocoa/nsDeviceContextSpecX.mm b/widget/cocoa/nsDeviceContextSpecX.mm deleted file mode 100644 index d252adef69..0000000000 --- a/widget/cocoa/nsDeviceContextSpecX.mm +++ /dev/null @@ -1,165 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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/. */ - -#include "nsDeviceContextSpecX.h" - -#include "mozilla/gfx/PrintTargetCG.h" -#include "mozilla/RefPtr.h" -#include "nsCRT.h" -#include <unistd.h> - -#include "nsQueryObject.h" -#include "nsIServiceManager.h" -#include "nsPrintSettingsX.h" - -// This must be the last include: -#include "nsObjCExceptions.h" - -using namespace mozilla; -using namespace mozilla::gfx; - -nsDeviceContextSpecX::nsDeviceContextSpecX() -: mPrintSession(NULL) -, mPageFormat(kPMNoPageFormat) -, mPrintSettings(kPMNoPrintSettings) -{ -} - -nsDeviceContextSpecX::~nsDeviceContextSpecX() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (mPrintSession) - ::PMRelease(mPrintSession); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -NS_IMPL_ISUPPORTS(nsDeviceContextSpecX, nsIDeviceContextSpec) - -NS_IMETHODIMP nsDeviceContextSpecX::Init(nsIWidget *aWidget, - nsIPrintSettings* aPS, - bool aIsPrintPreview) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - RefPtr<nsPrintSettingsX> settings(do_QueryObject(aPS)); - if (!settings) - return NS_ERROR_NO_INTERFACE; - - mPrintSession = settings->GetPMPrintSession(); - ::PMRetain(mPrintSession); - mPageFormat = settings->GetPMPageFormat(); - mPrintSettings = settings->GetPMPrintSettings(); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP nsDeviceContextSpecX::BeginDocument(const nsAString& aTitle, - const nsAString& aPrintToFileName, - int32_t aStartPage, - int32_t aEndPage) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (!aTitle.IsEmpty()) { - CFStringRef cfString = - ::CFStringCreateWithCharacters(NULL, reinterpret_cast<const UniChar*>(aTitle.BeginReading()), - aTitle.Length()); - if (cfString) { - ::PMPrintSettingsSetJobName(mPrintSettings, cfString); - ::CFRelease(cfString); - } - } - - OSStatus status; - status = ::PMSetFirstPage(mPrintSettings, aStartPage, false); - NS_ASSERTION(status == noErr, "PMSetFirstPage failed"); - status = ::PMSetLastPage(mPrintSettings, aEndPage, false); - NS_ASSERTION(status == noErr, "PMSetLastPage failed"); - - status = ::PMSessionBeginCGDocumentNoDialog(mPrintSession, mPrintSettings, mPageFormat); - if (status != noErr) - return NS_ERROR_ABORT; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP nsDeviceContextSpecX::EndDocument() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - ::PMSessionEndDocumentNoDialog(mPrintSession); - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP nsDeviceContextSpecX::BeginPage() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - PMSessionError(mPrintSession); - OSStatus status = ::PMSessionBeginPageNoDialog(mPrintSession, mPageFormat, NULL); - if (status != noErr) return NS_ERROR_ABORT; - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP nsDeviceContextSpecX::EndPage() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - OSStatus status = ::PMSessionEndPageNoDialog(mPrintSession); - if (status != noErr) return NS_ERROR_ABORT; - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -void nsDeviceContextSpecX::GetPaperRect(double* aTop, double* aLeft, double* aBottom, double* aRight) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - PMRect paperRect; - ::PMGetAdjustedPaperRect(mPageFormat, &paperRect); - - *aTop = paperRect.top, *aLeft = paperRect.left; - *aBottom = paperRect.bottom, *aRight = paperRect.right; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -already_AddRefed<PrintTarget> nsDeviceContextSpecX::MakePrintTarget() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - double top, left, bottom, right; - GetPaperRect(&top, &left, &bottom, &right); - const double width = right - left; - const double height = bottom - top; - IntSize size = IntSize::Floor(width, height); - - CGContextRef context; - ::PMSessionGetCGGraphicsContext(mPrintSession, &context); - - if (context) { - // Initially, origin is at bottom-left corner of the paper. - // Here, we translate it to top-left corner of the paper. - CGContextTranslateCTM(context, 0, height); - CGContextScaleCTM(context, 1.0, -1.0); - return PrintTargetCG::CreateOrNull(context, size); - } - - // Apparently we do need this branch - bug 368933. - return PrintTargetCG::CreateOrNull(size, SurfaceFormat::A8R8G8B8_UINT32); - - NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL; -} diff --git a/widget/cocoa/nsDragService.h b/widget/cocoa/nsDragService.h deleted file mode 100644 index ea6702812a..0000000000 --- a/widget/cocoa/nsDragService.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -*- 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 nsDragService_h_ -#define nsDragService_h_ - -#include "nsBaseDragService.h" - -#include <Cocoa/Cocoa.h> - -extern NSString* const kWildcardPboardType; -extern NSString* const kCorePboardType_url; -extern NSString* const kCorePboardType_urld; -extern NSString* const kCorePboardType_urln; -extern NSString* const kCustomTypesPboardType; - -class nsDragService : public nsBaseDragService -{ -public: - nsDragService(); - - // nsBaseDragService - virtual nsresult InvokeDragSessionImpl(nsIArray* anArrayTransferables, - nsIScriptableRegion* aRegion, - uint32_t aActionType); - // nsIDragService - NS_IMETHOD EndDragSession(bool aDoneDrag); - - // nsIDragSession - NS_IMETHOD GetData(nsITransferable * aTransferable, uint32_t aItemIndex); - NS_IMETHOD IsDataFlavorSupported(const char *aDataFlavor, bool *_retval); - NS_IMETHOD GetNumDropItems(uint32_t * aNumItems); - -protected: - virtual ~nsDragService(); - -private: - - NSImage* ConstructDragImage(nsIDOMNode* aDOMNode, - mozilla::LayoutDeviceIntRect* aDragRect, - nsIScriptableRegion* aRegion); - bool IsValidType(NSString* availableType, bool allowFileURL); - NSString* GetStringForType(NSPasteboardItem* item, const NSString* type, - bool allowFileURL = false); - NSString* GetTitleForURL(NSPasteboardItem* item); - NSString* GetFilePath(NSPasteboardItem* item); - - nsCOMPtr<nsIArray> mDataItems; // only valid for a drag started within gecko - NSView* mNativeDragView; - NSEvent* mNativeDragEvent; -}; - -#endif // nsDragService_h_ diff --git a/widget/cocoa/nsDragService.mm b/widget/cocoa/nsDragService.mm deleted file mode 100644 index c4d8e6ff06..0000000000 --- a/widget/cocoa/nsDragService.mm +++ /dev/null @@ -1,669 +0,0 @@ -/* -*- 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/. */ - -#include "mozilla/Logging.h" - -#include "nsArrayUtils.h" -#include "nsDragService.h" -#include "nsArrayUtils.h" -#include "nsObjCExceptions.h" -#include "nsITransferable.h" -#include "nsString.h" -#include "nsClipboard.h" -#include "nsXPCOM.h" -#include "nsISupportsPrimitives.h" -#include "nsCOMPtr.h" -#include "nsPrimitiveHelpers.h" -#include "nsLinebreakConverter.h" -#include "nsIMacUtils.h" -#include "nsIDOMNode.h" -#include "nsRect.h" -#include "nsPoint.h" -#include "nsIIOService.h" -#include "nsIDocument.h" -#include "nsIContent.h" -#include "nsView.h" -#include "gfxContext.h" -#include "nsCocoaUtils.h" -#include "mozilla/gfx/2D.h" -#include "gfxPlatform.h" - -using namespace mozilla; -using namespace mozilla::gfx; - -extern PRLogModuleInfo* sCocoaLog; - -extern void EnsureLogInitialized(); - -extern NSPasteboard* globalDragPboard; -extern NSView* gLastDragView; -extern NSEvent* gLastDragMouseDownEvent; -extern bool gUserCancelledDrag; - -// This global makes the transferable array available to Cocoa's promised -// file destination callback. -nsIArray *gDraggedTransferables = nullptr; - -NSString* const kWildcardPboardType = @"MozillaWildcard"; -NSString* const kCorePboardType_url = @"CorePasteboardFlavorType 0x75726C20"; // 'url ' url -NSString* const kCorePboardType_urld = @"CorePasteboardFlavorType 0x75726C64"; // 'urld' desc -NSString* const kCorePboardType_urln = @"CorePasteboardFlavorType 0x75726C6E"; // 'urln' title -NSString* const kUTTypeURLName = @"public.url-name"; -NSString* const kCustomTypesPboardType = @"org.mozilla.custom-clipdata"; - -nsDragService::nsDragService() -{ - mNativeDragView = nil; - mNativeDragEvent = nil; - - EnsureLogInitialized(); -} - -nsDragService::~nsDragService() -{ -} - -static nsresult SetUpDragClipboard(nsIArray* aTransferableArray) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (!aTransferableArray) - return NS_ERROR_FAILURE; - - uint32_t count = 0; - aTransferableArray->GetLength(&count); - - NSPasteboard* dragPBoard = [NSPasteboard pasteboardWithName:NSDragPboard]; - - for (uint32_t j = 0; j < count; j++) { - nsCOMPtr<nsITransferable> currentTransferable = do_QueryElementAt(aTransferableArray, j); - if (!currentTransferable) - return NS_ERROR_FAILURE; - - // Transform the transferable to an NSDictionary - NSDictionary* pasteboardOutputDict = nsClipboard::PasteboardDictFromTransferable(currentTransferable); - if (!pasteboardOutputDict) - return NS_ERROR_FAILURE; - - // write everything out to the general pasteboard - unsigned int typeCount = [pasteboardOutputDict count]; - NSMutableArray* types = [NSMutableArray arrayWithCapacity:typeCount + 1]; - [types addObjectsFromArray:[pasteboardOutputDict allKeys]]; - // Gecko is initiating this drag so we always want its own views to consider - // it. Add our wildcard type to the pasteboard to accomplish this. - [types addObject:kWildcardPboardType]; // we don't increase the count for the loop below on purpose - [dragPBoard declareTypes:types owner:nil]; - for (unsigned int k = 0; k < typeCount; k++) { - NSString* currentKey = [types objectAtIndex:k]; - id currentValue = [pasteboardOutputDict valueForKey:currentKey]; - if (currentKey == NSStringPboardType || - currentKey == kCorePboardType_url || - currentKey == kCorePboardType_urld || - currentKey == kCorePboardType_urln) { - [dragPBoard setString:currentValue forType:currentKey]; - } - else if (currentKey == NSHTMLPboardType) { - [dragPBoard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue)) - forType:currentKey]; - } - else if (currentKey == NSTIFFPboardType || - currentKey == kCustomTypesPboardType) { - [dragPBoard setData:currentValue forType:currentKey]; - } - else if (currentKey == NSFilesPromisePboardType || - currentKey == NSFilenamesPboardType) { - [dragPBoard setPropertyList:currentValue forType:currentKey]; - } - } - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NSImage* -nsDragService::ConstructDragImage(nsIDOMNode* aDOMNode, - LayoutDeviceIntRect* aDragRect, - nsIScriptableRegion* aRegion) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(gLastDragView); - - RefPtr<SourceSurface> surface; - nsPresContext* pc; - nsresult rv = DrawDrag(aDOMNode, aRegion, mScreenPosition, - aDragRect, &surface, &pc); - if (pc && (!aDragRect->width || !aDragRect->height)) { - // just use some suitable defaults - int32_t size = nsCocoaUtils::CocoaPointsToDevPixels(20, scaleFactor); - aDragRect->SetRect(pc->CSSPixelsToDevPixels(mScreenPosition.x), - pc->CSSPixelsToDevPixels(mScreenPosition.y), size, size); - } - - if (NS_FAILED(rv) || !surface) - return nil; - - uint32_t width = aDragRect->width; - uint32_t height = aDragRect->height; - - RefPtr<DataSourceSurface> dataSurface = - Factory::CreateDataSourceSurface(IntSize(width, height), - SurfaceFormat::B8G8R8A8); - DataSourceSurface::MappedSurface map; - if (!dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) { - return nil; - } - - RefPtr<DrawTarget> dt = - Factory::CreateDrawTargetForData(BackendType::CAIRO, - map.mData, - dataSurface->GetSize(), - map.mStride, - dataSurface->GetFormat()); - if (!dt) { - dataSurface->Unmap(); - return nil; - } - - dt->FillRect(gfx::Rect(0, 0, width, height), - SurfacePattern(surface, ExtendMode::CLAMP), - DrawOptions(1.0f, CompositionOp::OP_SOURCE)); - - NSBitmapImageRep* imageRep = - [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL - pixelsWide:width - pixelsHigh:height - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:width * 4 - bitsPerPixel:32]; - - uint8_t* dest = [imageRep bitmapData]; - for (uint32_t i = 0; i < height; ++i) { - uint8_t* src = map.mData + i * map.mStride; - for (uint32_t j = 0; j < width; ++j) { - // Reduce transparency overall by multipying by a factor. Remember, Alpha - // is premultipled here. Also, Quartz likes RGBA, so do that translation as well. -#ifdef IS_BIG_ENDIAN - dest[0] = uint8_t(src[1] * DRAG_TRANSLUCENCY); - dest[1] = uint8_t(src[2] * DRAG_TRANSLUCENCY); - dest[2] = uint8_t(src[3] * DRAG_TRANSLUCENCY); - dest[3] = uint8_t(src[0] * DRAG_TRANSLUCENCY); -#else - dest[0] = uint8_t(src[2] * DRAG_TRANSLUCENCY); - dest[1] = uint8_t(src[1] * DRAG_TRANSLUCENCY); - dest[2] = uint8_t(src[0] * DRAG_TRANSLUCENCY); - dest[3] = uint8_t(src[3] * DRAG_TRANSLUCENCY); -#endif - src += 4; - dest += 4; - } - } - dataSurface->Unmap(); - - NSImage* image = - [[NSImage alloc] initWithSize:NSMakeSize(width / scaleFactor, - height / scaleFactor)]; - [image addRepresentation:imageRep]; - [imageRep release]; - - return [image autorelease]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -bool -nsDragService::IsValidType(NSString* availableType, bool allowFileURL) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - // Prevent exposing fileURL for non-fileURL type. - // We need URL provided by dropped webloc file, but don't need file's URL. - // kUTTypeFileURL is returned by [NSPasteboard availableTypeFromArray:] for - // kUTTypeURL, since it conforms to kUTTypeURL. - if (!allowFileURL && [availableType isEqualToString:(id)kUTTypeFileURL]) { - return false; - } - - return true; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); -} - -NSString* -nsDragService::GetStringForType(NSPasteboardItem* item, const NSString* type, - bool allowFileURL) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - NSString* availableType = [item availableTypeFromArray:[NSArray arrayWithObjects:(id)type, nil]]; - if (availableType && IsValidType(availableType, allowFileURL)) { - return [item stringForType:(id)availableType]; - } - - return nil; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -NSString* -nsDragService::GetTitleForURL(NSPasteboardItem* item) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - NSString* name = GetStringForType(item, (const NSString*)kUTTypeURLName); - if (name) { - return name; - } - - NSString* filePath = GetFilePath(item); - if (filePath) { - return [filePath lastPathComponent]; - } - - return nil; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -NSString* -nsDragService::GetFilePath(NSPasteboardItem* item) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - NSString* urlString = GetStringForType(item, (const NSString*)kUTTypeFileURL, true); - if (urlString) { - NSURL* url = [NSURL URLWithString:urlString]; - if (url) { - return [url path]; - } - } - - return nil; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -// We can only invoke NSView's 'dragImage:at:offset:event:pasteboard:source:slideBack:' from -// within NSView's 'mouseDown:' or 'mouseDragged:'. Luckily 'mouseDragged' is always on the -// stack when InvokeDragSession gets called. -nsresult -nsDragService::InvokeDragSessionImpl(nsIArray* aTransferableArray, - nsIScriptableRegion* aDragRgn, - uint32_t aActionType) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - mDataItems = aTransferableArray; - - // put data on the clipboard - if (NS_FAILED(SetUpDragClipboard(aTransferableArray))) - return NS_ERROR_FAILURE; - - CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(gLastDragView); - - LayoutDeviceIntRect dragRect(0, 0, 20, 20); - NSImage* image = ConstructDragImage(mSourceNode, &dragRect, aDragRgn); - if (!image) { - // if no image was returned, just draw a rectangle - NSSize size; - size.width = nsCocoaUtils::DevPixelsToCocoaPoints(dragRect.width, scaleFactor); - size.height = nsCocoaUtils::DevPixelsToCocoaPoints(dragRect.height, scaleFactor); - image = [[NSImage alloc] initWithSize:size]; - [image lockFocus]; - [[NSColor grayColor] set]; - NSBezierPath* path = [NSBezierPath bezierPath]; - [path setLineWidth:2.0]; - [path moveToPoint:NSMakePoint(0, 0)]; - [path lineToPoint:NSMakePoint(0, size.height)]; - [path lineToPoint:NSMakePoint(size.width, size.height)]; - [path lineToPoint:NSMakePoint(size.width, 0)]; - [path lineToPoint:NSMakePoint(0, 0)]; - [path stroke]; - [image unlockFocus]; - } - - LayoutDeviceIntPoint pt(dragRect.x, dragRect.YMost()); - NSPoint point = nsCocoaUtils::DevPixelsToCocoaPoints(pt, scaleFactor); - point.y = nsCocoaUtils::FlippedScreenY(point.y); - - point = nsCocoaUtils::ConvertPointFromScreen([gLastDragView window], point); - NSPoint localPoint = [gLastDragView convertPoint:point fromView:nil]; - - // Save the transferables away in case a promised file callback is invoked. - gDraggedTransferables = aTransferableArray; - - nsBaseDragService::StartDragSession(); - nsBaseDragService::OpenDragPopup(); - - // We need to retain the view and the event during the drag in case either gets destroyed. - mNativeDragView = [gLastDragView retain]; - mNativeDragEvent = [gLastDragMouseDownEvent retain]; - - gUserCancelledDrag = false; - [mNativeDragView dragImage:image - at:localPoint - offset:NSZeroSize - event:mNativeDragEvent - pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard] - source:mNativeDragView - slideBack:YES]; - gUserCancelledDrag = false; - - if (mDoingDrag) - nsBaseDragService::EndDragSession(false); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsDragService::GetData(nsITransferable* aTransferable, uint32_t aItemIndex) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (!aTransferable) - return NS_ERROR_FAILURE; - - // get flavor list that includes all acceptable flavors (including ones obtained through conversion) - nsCOMPtr<nsIArray> flavorList; - nsresult rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList)); - if (NS_FAILED(rv)) - return NS_ERROR_FAILURE; - - uint32_t acceptableFlavorCount; - flavorList->GetLength(&acceptableFlavorCount); - - // if this drag originated within Mozilla we should just use the cached data from - // when the drag started if possible - if (mDataItems) { - nsCOMPtr<nsITransferable> currentTransferable = do_QueryElementAt(mDataItems, aItemIndex); - if (currentTransferable) { - for (uint32_t i = 0; i < acceptableFlavorCount; i++) { - nsCOMPtr<nsISupportsCString> currentFlavor = do_QueryElementAt(flavorList, i); - if (!currentFlavor) - continue; - nsXPIDLCString flavorStr; - currentFlavor->ToString(getter_Copies(flavorStr)); - - nsCOMPtr<nsISupports> dataSupports; - uint32_t dataSize = 0; - rv = currentTransferable->GetTransferData(flavorStr, getter_AddRefs(dataSupports), &dataSize); - if (NS_SUCCEEDED(rv)) { - aTransferable->SetTransferData(flavorStr, dataSupports, dataSize); - return NS_OK; // maybe try to fill in more types? Is there a point? - } - } - } - } - - // now check the actual clipboard for data - for (uint32_t i = 0; i < acceptableFlavorCount; i++) { - nsCOMPtr<nsISupportsCString> currentFlavor = do_QueryElementAt(flavorList, i); - if (!currentFlavor) - continue; - - nsXPIDLCString flavorStr; - currentFlavor->ToString(getter_Copies(flavorStr)); - - MOZ_LOG(sCocoaLog, LogLevel::Info, ("nsDragService::GetData: looking for clipboard data of type %s\n", flavorStr.get())); - - NSArray* droppedItems = [globalDragPboard pasteboardItems]; - if (!droppedItems) { - continue; - } - - uint32_t itemCount = [droppedItems count]; - if (aItemIndex >= itemCount) { - continue; - } - - NSPasteboardItem* item = [droppedItems objectAtIndex:aItemIndex]; - if (!item) { - continue; - } - - if (flavorStr.EqualsLiteral(kFileMime)) { - NSString* filePath = GetFilePath(item); - if (!filePath) - continue; - - unsigned int stringLength = [filePath length]; - unsigned int dataLength = (stringLength + 1) * sizeof(char16_t); // in bytes - char16_t* clipboardDataPtr = (char16_t*)malloc(dataLength); - if (!clipboardDataPtr) - return NS_ERROR_OUT_OF_MEMORY; - [filePath getCharacters:reinterpret_cast<unichar*>(clipboardDataPtr)]; - clipboardDataPtr[stringLength] = 0; // null terminate - - nsCOMPtr<nsIFile> file; - rv = NS_NewLocalFile(nsDependentString(clipboardDataPtr), true, getter_AddRefs(file)); - free(clipboardDataPtr); - if (NS_FAILED(rv)) - continue; - - aTransferable->SetTransferData(flavorStr, file, dataLength); - - break; - } - else if (flavorStr.EqualsLiteral(kCustomTypesMime)) { - NSString* availableType = [item availableTypeFromArray:[NSArray arrayWithObject:kCustomTypesPboardType]]; - if (!availableType || !IsValidType(availableType, false)) { - continue; - } - NSData *pasteboardData = [item dataForType:availableType]; - if (!pasteboardData) { - continue; - } - - unsigned int dataLength = [pasteboardData length]; - void* clipboardDataPtr = malloc(dataLength); - if (!clipboardDataPtr) { - return NS_ERROR_OUT_OF_MEMORY; - } - [pasteboardData getBytes:clipboardDataPtr]; - - nsCOMPtr<nsISupports> genericDataWrapper; - nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtr, dataLength, - getter_AddRefs(genericDataWrapper)); - - aTransferable->SetTransferData(flavorStr, genericDataWrapper, sizeof(nsIInputStream*)); - free(clipboardDataPtr); - break; - } - - NSString* pString = nil; - if (flavorStr.EqualsLiteral(kUnicodeMime)) { - pString = GetStringForType(item, (const NSString*)kUTTypeUTF8PlainText); - } else if (flavorStr.EqualsLiteral(kHTMLMime)) { - pString = GetStringForType(item, (const NSString*)kUTTypeHTML); - } else if (flavorStr.EqualsLiteral(kURLMime)) { - pString = GetStringForType(item, (const NSString*)kUTTypeURL); - if (pString) { - NSString* title = GetTitleForURL(item); - if (!title) { - title = pString; - } - pString = [NSString stringWithFormat:@"%@\n%@", pString, title]; - } - } else if (flavorStr.EqualsLiteral(kURLDataMime)) { - pString = GetStringForType(item, (const NSString*)kUTTypeURL); - } else if (flavorStr.EqualsLiteral(kURLDescriptionMime)) { - pString = GetTitleForURL(item); - } else if (flavorStr.EqualsLiteral(kRTFMime)) { - pString = GetStringForType(item, (const NSString*)kUTTypeRTF); - } - if (pString) { - NSData* stringData; - if (flavorStr.EqualsLiteral(kRTFMime)) { - stringData = [pString dataUsingEncoding:NSASCIIStringEncoding]; - } else { - stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding]; - } - unsigned int dataLength = [stringData length]; - void* clipboardDataPtr = malloc(dataLength); - if (!clipboardDataPtr) - return NS_ERROR_OUT_OF_MEMORY; - [stringData getBytes:clipboardDataPtr]; - - // The DOM only wants LF, so convert from MacOS line endings to DOM line endings. - int32_t signedDataLength = dataLength; - nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr, &clipboardDataPtr, &signedDataLength); - dataLength = signedDataLength; - - // skip BOM (Byte Order Mark to distinguish little or big endian) - char16_t* clipboardDataPtrNoBOM = (char16_t*)clipboardDataPtr; - if ((dataLength > 2) && - ((clipboardDataPtrNoBOM[0] == 0xFEFF) || - (clipboardDataPtrNoBOM[0] == 0xFFFE))) { - dataLength -= sizeof(char16_t); - clipboardDataPtrNoBOM += 1; - } - - nsCOMPtr<nsISupports> genericDataWrapper; - nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtrNoBOM, dataLength, - getter_AddRefs(genericDataWrapper)); - aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLength); - free(clipboardDataPtr); - break; - } - - // We have never supported this on Mac OS X, we should someday. Normally dragging images - // in is accomplished with a file path drag instead of the image data itself. - /* - if (flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) || - flavorStr.EqualsLiteral(kJPGImageMime) || flavorStr.EqualsLiteral(kGIFImageMime)) { - - } - */ - } - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsDragService::IsDataFlavorSupported(const char *aDataFlavor, bool *_retval) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - *_retval = false; - - if (!globalDragPboard) - return NS_ERROR_FAILURE; - - nsDependentCString dataFlavor(aDataFlavor); - - // first see if we have data for this in our cached transferable - if (mDataItems) { - uint32_t dataItemsCount; - mDataItems->GetLength(&dataItemsCount); - for (unsigned int i = 0; i < dataItemsCount; i++) { - nsCOMPtr<nsITransferable> currentTransferable = do_QueryElementAt(mDataItems, i); - if (!currentTransferable) - continue; - - nsCOMPtr<nsIArray> flavorList; - nsresult rv = currentTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList)); - if (NS_FAILED(rv)) - continue; - - uint32_t flavorCount; - flavorList->GetLength(&flavorCount); - for (uint32_t j = 0; j < flavorCount; j++) { - nsCOMPtr<nsISupportsCString> currentFlavor = do_QueryElementAt(flavorList, j); - if (!currentFlavor) - continue; - nsXPIDLCString flavorStr; - currentFlavor->ToString(getter_Copies(flavorStr)); - if (dataFlavor.Equals(flavorStr)) { - *_retval = true; - return NS_OK; - } - } - } - } - - const NSString* type = nil; - bool allowFileURL = false; - if (dataFlavor.EqualsLiteral(kFileMime)) { - type = (const NSString*)kUTTypeFileURL; - allowFileURL = true; - } else if (dataFlavor.EqualsLiteral(kUnicodeMime)) { - type = (const NSString*)kUTTypeUTF8PlainText; - } else if (dataFlavor.EqualsLiteral(kHTMLMime)) { - type = (const NSString*)kUTTypeHTML; - } else if (dataFlavor.EqualsLiteral(kURLMime) || - dataFlavor.EqualsLiteral(kURLDataMime)) { - type = (const NSString*)kUTTypeURL; - } else if (dataFlavor.EqualsLiteral(kURLDescriptionMime)) { - type = (const NSString*)kUTTypeURLName; - } else if (dataFlavor.EqualsLiteral(kRTFMime)) { - type = (const NSString*)kUTTypeRTF; - } else if (dataFlavor.EqualsLiteral(kCustomTypesMime)) { - type = (const NSString*)kCustomTypesPboardType; - } - - NSString* availableType = [globalDragPboard availableTypeFromArray:[NSArray arrayWithObjects:(id)type, nil]]; - if (availableType && IsValidType(availableType, allowFileURL)) { - *_retval = true; - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsDragService::GetNumDropItems(uint32_t* aNumItems) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - *aNumItems = 0; - - // first check to see if we have a number of items cached - if (mDataItems) { - mDataItems->GetLength(aNumItems); - return NS_OK; - } - - NSArray* droppedItems = [globalDragPboard pasteboardItems]; - if (droppedItems) { - *aNumItems = [droppedItems count]; - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsDragService::EndDragSession(bool aDoneDrag) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (mNativeDragView) { - [mNativeDragView release]; - mNativeDragView = nil; - } - if (mNativeDragEvent) { - [mNativeDragEvent release]; - mNativeDragEvent = nil; - } - - mUserCancelled = gUserCancelledDrag; - - nsresult rv = nsBaseDragService::EndDragSession(aDoneDrag); - mDataItems = nullptr; - return rv; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} diff --git a/widget/cocoa/nsFilePicker.h b/widget/cocoa/nsFilePicker.h deleted file mode 100644 index 1aeb22cc18..0000000000 --- a/widget/cocoa/nsFilePicker.h +++ /dev/null @@ -1,74 +0,0 @@ -/* -*- 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 nsFilePicker_h_ -#define nsFilePicker_h_ - -#include "nsBaseFilePicker.h" -#include "nsString.h" -#include "nsIFileChannel.h" -#include "nsIFile.h" -#include "nsCOMArray.h" -#include "nsTArray.h" - -class nsILocalFileMac; -@class NSArray; - -class nsFilePicker : public nsBaseFilePicker -{ -public: - nsFilePicker(); - - NS_DECL_ISUPPORTS - - // nsIFilePicker (less what's in nsBaseFilePicker) - NS_IMETHOD GetDefaultString(nsAString& aDefaultString) override; - NS_IMETHOD SetDefaultString(const nsAString& aDefaultString) override; - NS_IMETHOD GetDefaultExtension(nsAString& aDefaultExtension) override; - NS_IMETHOD GetFilterIndex(int32_t *aFilterIndex) override; - NS_IMETHOD SetFilterIndex(int32_t aFilterIndex) override; - NS_IMETHOD SetDefaultExtension(const nsAString& aDefaultExtension) override; - NS_IMETHOD GetFile(nsIFile * *aFile) override; - NS_IMETHOD GetFileURL(nsIURI * *aFileURL) override; - NS_IMETHOD GetFiles(nsISimpleEnumerator **aFiles) override; - NS_IMETHOD Show(int16_t *_retval) override; - NS_IMETHOD AppendFilter(const nsAString& aTitle, const nsAString& aFilter) override; - - /** - * Returns the current filter list in the format used by Cocoa's NSSavePanel - * and NSOpenPanel. - * Returns nil if no filter currently apply. - */ - NSArray* GetFilterList(); - -protected: - virtual ~nsFilePicker(); - - virtual void InitNative(nsIWidget *aParent, const nsAString& aTitle) override; - - // actual implementations of get/put dialogs using NSOpenPanel & NSSavePanel - // aFile is an existing but unspecified file. These functions must specify it. - // - // will return |returnCancel| or |returnOK| as result. - int16_t GetLocalFiles(const nsString& inTitle, bool inAllowMultiple, nsCOMArray<nsIFile>& outFiles); - int16_t GetLocalFolder(const nsString& inTitle, nsIFile** outFile); - int16_t PutLocalFile(const nsString& inTitle, const nsString& inDefaultName, nsIFile** outFile); - - void SetDialogTitle(const nsString& inTitle, id aDialog); - NSString *PanelDefaultDirectory(); - NSView* GetAccessoryView(); - - nsString mTitle; - nsCOMArray<nsIFile> mFiles; - nsString mDefault; - - nsTArray<nsString> mFilters; - nsTArray<nsString> mTitles; - - int32_t mSelectedTypeIndex; -}; - -#endif // nsFilePicker_h_ diff --git a/widget/cocoa/nsFilePicker.mm b/widget/cocoa/nsFilePicker.mm deleted file mode 100644 index 5213dee241..0000000000 --- a/widget/cocoa/nsFilePicker.mm +++ /dev/null @@ -1,676 +0,0 @@ -/* -*- 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/. */ - -#import <Cocoa/Cocoa.h> - -#include "nsFilePicker.h" -#include "nsCOMPtr.h" -#include "nsReadableUtils.h" -#include "nsNetUtil.h" -#include "nsIComponentManager.h" -#include "nsIFile.h" -#include "nsILocalFileMac.h" -#include "nsIURL.h" -#include "nsArrayEnumerator.h" -#include "nsIStringBundle.h" -#include "nsCocoaUtils.h" -#include "mozilla/Preferences.h" - -// This must be included last: -#include "nsObjCExceptions.h" - -using namespace mozilla; - -const float kAccessoryViewPadding = 5; -const int kSaveTypeControlTag = 1; - -static bool gCallSecretHiddenFileAPI = false; -const char kShowHiddenFilesPref[] = "filepicker.showHiddenFiles"; - -/** - * This class is an observer of NSPopUpButton selection change. - */ -@interface NSPopUpButtonObserver : NSObject -{ - NSPopUpButton* mPopUpButton; - NSOpenPanel* mOpenPanel; - nsFilePicker* mFilePicker; -} -- (void) setPopUpButton:(NSPopUpButton*)aPopUpButton; -- (void) setOpenPanel:(NSOpenPanel*)aOpenPanel; -- (void) setFilePicker:(nsFilePicker*)aFilePicker; -- (void) menuChangedItem:(NSNotification*)aSender; -@end - -NS_IMPL_ISUPPORTS(nsFilePicker, nsIFilePicker) - -// We never want to call the secret show hidden files API unless the pref -// has been set. Once the pref has been set we always need to call it even -// if it disappears so that we stop showing hidden files if a user deletes -// the pref. If the secret API was used once and things worked out it should -// continue working for subsequent calls so the user is at no more risk. -static void SetShowHiddenFileState(NSSavePanel* panel) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - bool show = false; - if (NS_SUCCEEDED(Preferences::GetBool(kShowHiddenFilesPref, &show))) { - gCallSecretHiddenFileAPI = true; - } - - if (gCallSecretHiddenFileAPI) { - // invoke a method to get a Cocoa-internal nav view - SEL navViewSelector = @selector(_navView); - NSMethodSignature* navViewSignature = [panel methodSignatureForSelector:navViewSelector]; - if (!navViewSignature) - return; - NSInvocation* navViewInvocation = [NSInvocation invocationWithMethodSignature:navViewSignature]; - [navViewInvocation setSelector:navViewSelector]; - [navViewInvocation setTarget:panel]; - [navViewInvocation invoke]; - - // get the returned nav view - id navView = nil; - [navViewInvocation getReturnValue:&navView]; - - // invoke the secret show hidden file state method on the nav view - SEL showHiddenFilesSelector = @selector(setShowsHiddenFiles:); - NSMethodSignature* showHiddenFilesSignature = [navView methodSignatureForSelector:showHiddenFilesSelector]; - if (!showHiddenFilesSignature) - return; - NSInvocation* showHiddenFilesInvocation = [NSInvocation invocationWithMethodSignature:showHiddenFilesSignature]; - [showHiddenFilesInvocation setSelector:showHiddenFilesSelector]; - [showHiddenFilesInvocation setTarget:navView]; - [showHiddenFilesInvocation setArgument:&show atIndex:2]; - [showHiddenFilesInvocation invoke]; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -nsFilePicker::nsFilePicker() -: mSelectedTypeIndex(0) -{ -} - -nsFilePicker::~nsFilePicker() -{ -} - -void -nsFilePicker::InitNative(nsIWidget *aParent, const nsAString& aTitle) -{ - mTitle = aTitle; -} - -NSView* nsFilePicker::GetAccessoryView() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - NSView* accessoryView = [[[NSView alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)] autorelease]; - - // Set a label's default value. - NSString* label = @"Format:"; - - // Try to get the localized string. - nsCOMPtr<nsIStringBundleService> sbs = do_GetService(NS_STRINGBUNDLE_CONTRACTID); - nsCOMPtr<nsIStringBundle> bundle; - nsresult rv = sbs->CreateBundle("chrome://global/locale/filepicker.properties", getter_AddRefs(bundle)); - if (NS_SUCCEEDED(rv)) { - nsXPIDLString locaLabel; - bundle->GetStringFromName(u"formatLabel", getter_Copies(locaLabel)); - if (locaLabel) { - label = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(locaLabel.get()) - length:locaLabel.Length()]; - } - } - - // set up label text field - NSTextField* textField = [[[NSTextField alloc] init] autorelease]; - [textField setEditable:NO]; - [textField setSelectable:NO]; - [textField setDrawsBackground:NO]; - [textField setBezeled:NO]; - [textField setBordered:NO]; - [textField setFont:[NSFont labelFontOfSize:13.0]]; - [textField setStringValue:label]; - [textField setTag:0]; - [textField sizeToFit]; - - // set up popup button - NSPopUpButton* popupButton = [[[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 0, 0) pullsDown:NO] autorelease]; - uint32_t numMenuItems = mTitles.Length(); - for (uint32_t i = 0; i < numMenuItems; i++) { - const nsString& currentTitle = mTitles[i]; - NSString *titleString; - if (currentTitle.IsEmpty()) { - const nsString& currentFilter = mFilters[i]; - titleString = [[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(currentFilter.get()) - length:currentFilter.Length()]; - } - else { - titleString = [[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(currentTitle.get()) - length:currentTitle.Length()]; - } - [popupButton addItemWithTitle:titleString]; - [titleString release]; - } - if (mSelectedTypeIndex >= 0 && (uint32_t)mSelectedTypeIndex < numMenuItems) - [popupButton selectItemAtIndex:mSelectedTypeIndex]; - [popupButton setTag:kSaveTypeControlTag]; - [popupButton sizeToFit]; // we have to do sizeToFit to get the height calculated for us - // This is just a default width that works well, doesn't truncate the vast majority of - // things that might end up in the menu. - [popupButton setFrameSize:NSMakeSize(180, [popupButton frame].size.height)]; - - // position everything based on control sizes with kAccessoryViewPadding pix padding - // on each side kAccessoryViewPadding pix horizontal padding between controls - float greatestHeight = [textField frame].size.height; - if ([popupButton frame].size.height > greatestHeight) - greatestHeight = [popupButton frame].size.height; - float totalViewHeight = greatestHeight + kAccessoryViewPadding * 2; - float totalViewWidth = [textField frame].size.width + [popupButton frame].size.width + kAccessoryViewPadding * 3; - [accessoryView setFrameSize:NSMakeSize(totalViewWidth, totalViewHeight)]; - - float textFieldOriginY = ((greatestHeight - [textField frame].size.height) / 2 + 1) + kAccessoryViewPadding; - [textField setFrameOrigin:NSMakePoint(kAccessoryViewPadding, textFieldOriginY)]; - - float popupOriginX = [textField frame].size.width + kAccessoryViewPadding * 2; - float popupOriginY = ((greatestHeight - [popupButton frame].size.height) / 2) + kAccessoryViewPadding; - [popupButton setFrameOrigin:NSMakePoint(popupOriginX, popupOriginY)]; - - [accessoryView addSubview:textField]; - [accessoryView addSubview:popupButton]; - return accessoryView; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -// Display the file dialog -NS_IMETHODIMP nsFilePicker::Show(int16_t *retval) -{ - NS_ENSURE_ARG_POINTER(retval); - - *retval = returnCancel; - - int16_t userClicksOK = returnCancel; - -// Random questions from DHH: -// -// Why do we pass mTitle, mDefault to the functions? Can GetLocalFile. PutLocalFile, -// and GetLocalFolder get called someplace else? It generates a bunch of warnings -// as it is right now. -// -// I think we could easily combine GetLocalFile and GetLocalFolder together, just -// setting panel pick options based on mMode. I didn't do it here b/c I wanted to -// make this look as much like Carbon nsFilePicker as possible. - - mFiles.Clear(); - nsCOMPtr<nsIFile> theFile; - - switch (mMode) - { - case modeOpen: - userClicksOK = GetLocalFiles(mTitle, false, mFiles); - break; - - case modeOpenMultiple: - userClicksOK = GetLocalFiles(mTitle, true, mFiles); - break; - - case modeSave: - userClicksOK = PutLocalFile(mTitle, mDefault, getter_AddRefs(theFile)); - break; - - case modeGetFolder: - userClicksOK = GetLocalFolder(mTitle, getter_AddRefs(theFile)); - break; - - default: - NS_ERROR("Unknown file picker mode"); - break; - } - - if (theFile) - mFiles.AppendObject(theFile); - - *retval = userClicksOK; - return NS_OK; -} - -static -void UpdatePanelFileTypes(NSOpenPanel* aPanel, NSArray* aFilters) -{ - // If we show all file types, also "expose" bundles' contents. - [aPanel setTreatsFilePackagesAsDirectories:!aFilters]; - - [aPanel setAllowedFileTypes:aFilters]; -} - -@implementation NSPopUpButtonObserver -- (void) setPopUpButton:(NSPopUpButton*)aPopUpButton -{ - mPopUpButton = aPopUpButton; -} - -- (void) setOpenPanel:(NSOpenPanel*)aOpenPanel -{ - mOpenPanel = aOpenPanel; -} - -- (void) setFilePicker:(nsFilePicker*)aFilePicker -{ - mFilePicker = aFilePicker; -} - -- (void) menuChangedItem:(NSNotification *)aSender -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - int32_t selectedItem = [mPopUpButton indexOfSelectedItem]; - if (selectedItem < 0) { - return; - } - - mFilePicker->SetFilterIndex(selectedItem); - UpdatePanelFileTypes(mOpenPanel, mFilePicker->GetFilterList()); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(); -} -@end - -// Use OpenPanel to do a GetFile. Returns |returnOK| if the user presses OK in the dialog. -int16_t -nsFilePicker::GetLocalFiles(const nsString& inTitle, bool inAllowMultiple, nsCOMArray<nsIFile>& outFiles) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - int16_t retVal = (int16_t)returnCancel; - NSOpenPanel *thePanel = [NSOpenPanel openPanel]; - - SetShowHiddenFileState(thePanel); - - // Set the options for how the get file dialog will appear - SetDialogTitle(inTitle, thePanel); - [thePanel setAllowsMultipleSelection:inAllowMultiple]; - [thePanel setCanSelectHiddenExtension:YES]; - [thePanel setCanChooseDirectories:NO]; - [thePanel setCanChooseFiles:YES]; - [thePanel setResolvesAliases:YES]; //this is default - probably doesn't need to be set - - // Get filters - // filters may be null, if we should allow all file types. - NSArray *filters = GetFilterList(); - - // set up default directory - NSString *theDir = PanelDefaultDirectory(); - - // if this is the "Choose application..." dialog, and no other start - // dir has been set, then use the Applications folder. - if (!theDir) { - if (filters && [filters count] == 1 && - [(NSString *)[filters objectAtIndex:0] isEqualToString:@"app"]) - theDir = @"/Applications/"; - else - theDir = @""; - } - - if (theDir) { - [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]]; - } - - int result; - nsCocoaUtils::PrepareForNativeAppModalDialog(); - if (mFilters.Length() > 1) { - // [NSURL initWithString:] (below) throws an exception if URLString is nil. - - NSPopUpButtonObserver* observer = [[NSPopUpButtonObserver alloc] init]; - - NSView* accessoryView = GetAccessoryView(); - [thePanel setAccessoryView:accessoryView]; - - [observer setPopUpButton:[accessoryView viewWithTag:kSaveTypeControlTag]]; - [observer setOpenPanel:thePanel]; - [observer setFilePicker:this]; - - [[NSNotificationCenter defaultCenter] - addObserver:observer - selector:@selector(menuChangedItem:) - name:NSMenuWillSendActionNotification object:nil]; - - UpdatePanelFileTypes(thePanel, filters); - result = [thePanel runModal]; - - [[NSNotificationCenter defaultCenter] removeObserver:observer]; - [observer release]; - } else { - // If we show all file types, also "expose" bundles' contents. - if (!filters) { - [thePanel setTreatsFilePackagesAsDirectories:YES]; - } - [thePanel setAllowedFileTypes:filters]; - result = [thePanel runModal]; - } - nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); - - if (result == NSFileHandlingPanelCancelButton) - return retVal; - - // Converts data from a NSArray of NSURL to the returned format. - // We should be careful to not call [thePanel URLs] more than once given that - // it creates a new array each time. - // We are using Fast Enumeration, thus the NSURL array is created once then - // iterated. - for (NSURL* url in [thePanel URLs]) { - if (!url) { - continue; - } - - nsCOMPtr<nsIFile> localFile; - NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localFile)); - nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile); - if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)url))) { - outFiles.AppendObject(localFile); - } - } - - if (outFiles.Count() > 0) - retVal = returnOK; - - return retVal; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); -} - -// Use OpenPanel to do a GetFolder. Returns |returnOK| if the user presses OK in the dialog. -int16_t -nsFilePicker::GetLocalFolder(const nsString& inTitle, nsIFile** outFile) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - NS_ASSERTION(outFile, "this protected member function expects a null initialized out pointer"); - - int16_t retVal = (int16_t)returnCancel; - NSOpenPanel *thePanel = [NSOpenPanel openPanel]; - - SetShowHiddenFileState(thePanel); - - // Set the options for how the get file dialog will appear - SetDialogTitle(inTitle, thePanel); - [thePanel setAllowsMultipleSelection:NO]; //this is default -probably doesn't need to be set - [thePanel setCanSelectHiddenExtension:YES]; - [thePanel setCanChooseDirectories:YES]; - [thePanel setCanChooseFiles:NO]; - [thePanel setResolvesAliases:YES]; //this is default - probably doesn't need to be set - [thePanel setCanCreateDirectories:YES]; - - // packages != folders - [thePanel setTreatsFilePackagesAsDirectories:NO]; - - // set up default directory - NSString *theDir = PanelDefaultDirectory(); - if (theDir) { - [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]]; - } - nsCocoaUtils::PrepareForNativeAppModalDialog(); - int result = [thePanel runModal]; - nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); - - if (result == NSFileHandlingPanelCancelButton) - return retVal; - - // get the path for the folder (we allow just 1, so that's all we get) - NSURL *theURL = [[thePanel URLs] objectAtIndex:0]; - if (theURL) { - nsCOMPtr<nsIFile> localFile; - NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localFile)); - nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile); - if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)theURL))) { - *outFile = localFile; - NS_ADDREF(*outFile); - retVal = returnOK; - } - } - - return retVal; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); -} - -// Returns |returnOK| if the user presses OK in the dialog. -int16_t -nsFilePicker::PutLocalFile(const nsString& inTitle, const nsString& inDefaultName, nsIFile** outFile) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - NS_ASSERTION(outFile, "this protected member function expects a null initialized out pointer"); - - int16_t retVal = returnCancel; - NSSavePanel *thePanel = [NSSavePanel savePanel]; - - SetShowHiddenFileState(thePanel); - - SetDialogTitle(inTitle, thePanel); - - // set up accessory view for file format options - NSView* accessoryView = GetAccessoryView(); - [thePanel setAccessoryView:accessoryView]; - - // set up default file name - NSString* defaultFilename = [NSString stringWithCharacters:(const unichar*)inDefaultName.get() length:inDefaultName.Length()]; - - // set up allowed types; this prevents the extension from being selected - // use the UTI for the file type to allow alternate extensions (e.g., jpg vs. jpeg) - NSString* extension = defaultFilename.pathExtension; - if (extension.length != 0) { - CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)extension, NULL); - - if (type) { - thePanel.allowedFileTypes = @[(NSString*)type]; - CFRelease(type); - } else { - // if there's no UTI for the file extension, use the extension itself. - thePanel.allowedFileTypes = @[extension]; - } - } - // Allow users to change the extension. - thePanel.allowsOtherFileTypes = YES; - - // set up default directory - NSString *theDir = PanelDefaultDirectory(); - if (theDir) { - [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]]; - } - - // load the panel - nsCocoaUtils::PrepareForNativeAppModalDialog(); - [thePanel setNameFieldStringValue:defaultFilename]; - int result = [thePanel runModal]; - nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); - if (result == NSFileHandlingPanelCancelButton) - return retVal; - - // get the save type - NSPopUpButton* popupButton = [accessoryView viewWithTag:kSaveTypeControlTag]; - if (popupButton) { - mSelectedTypeIndex = [popupButton indexOfSelectedItem]; - } - - NSURL* fileURL = [thePanel URL]; - if (fileURL) { - nsCOMPtr<nsIFile> localFile; - NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localFile)); - nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile); - if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)fileURL))) { - *outFile = localFile; - NS_ADDREF(*outFile); - // We tell if we are replacing or not by just looking to see if the file exists. - // The user could not have hit OK and not meant to replace the file. - if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) - retVal = returnReplace; - else - retVal = returnOK; - } - } - - return retVal; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); -} - -NSArray * -nsFilePicker::GetFilterList() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - if (!mFilters.Length()) { - return nil; - } - - if (mFilters.Length() <= (uint32_t)mSelectedTypeIndex) { - NS_WARNING("An out of range index has been selected. Using the first index instead."); - mSelectedTypeIndex = 0; - } - - const nsString& filterWide = mFilters[mSelectedTypeIndex]; - if (!filterWide.Length()) { - return nil; - } - - if (filterWide.Equals(NS_LITERAL_STRING("*"))) { - return nil; - } - - // The extensions in filterWide are in the format "*.ext" but are expected - // in the format "ext" by NSOpenPanel. So we need to filter some characters. - NSMutableString* filterString = [[[NSMutableString alloc] initWithString: - [NSString stringWithCharacters:reinterpret_cast<const unichar*>(filterWide.get()) - length:filterWide.Length()]] autorelease]; - NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@". *"]; - NSRange range = [filterString rangeOfCharacterFromSet:set]; - while (range.length) { - [filterString replaceCharactersInRange:range withString:@""]; - range = [filterString rangeOfCharacterFromSet:set]; - } - - return [[[NSArray alloc] initWithArray: - [filterString componentsSeparatedByString:@";"]] autorelease]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -// Sets the dialog title to whatever it should be. If it fails, eh, -// the OS will provide a sensible default. -void -nsFilePicker::SetDialogTitle(const nsString& inTitle, id aPanel) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [aPanel setTitle:[NSString stringWithCharacters:(const unichar*)inTitle.get() length:inTitle.Length()]]; - - if (!mOkButtonLabel.IsEmpty()) { - [aPanel setPrompt:[NSString stringWithCharacters:(const unichar*)mOkButtonLabel.get() length:mOkButtonLabel.Length()]]; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// Converts path from an nsIFile into a NSString path -// If it fails, returns an empty string. -NSString * -nsFilePicker::PanelDefaultDirectory() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - NSString *directory = nil; - if (mDisplayDirectory) { - nsAutoString pathStr; - mDisplayDirectory->GetPath(pathStr); - directory = [[[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(pathStr.get()) - length:pathStr.Length()] autorelease]; - } - return directory; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -NS_IMETHODIMP nsFilePicker::GetFile(nsIFile **aFile) -{ - NS_ENSURE_ARG_POINTER(aFile); - *aFile = nullptr; - - // just return the first file - if (mFiles.Count() > 0) { - *aFile = mFiles.ObjectAt(0); - NS_IF_ADDREF(*aFile); - } - - return NS_OK; -} - -NS_IMETHODIMP nsFilePicker::GetFileURL(nsIURI **aFileURL) -{ - NS_ENSURE_ARG_POINTER(aFileURL); - *aFileURL = nullptr; - - if (mFiles.Count() == 0) - return NS_OK; - - return NS_NewFileURI(aFileURL, mFiles.ObjectAt(0)); -} - -NS_IMETHODIMP nsFilePicker::GetFiles(nsISimpleEnumerator **aFiles) -{ - return NS_NewArrayEnumerator(aFiles, mFiles); -} - -NS_IMETHODIMP nsFilePicker::SetDefaultString(const nsAString& aString) -{ - mDefault = aString; - return NS_OK; -} - -NS_IMETHODIMP nsFilePicker::GetDefaultString(nsAString& aString) -{ - return NS_ERROR_FAILURE; -} - -// The default extension to use for files -NS_IMETHODIMP nsFilePicker::GetDefaultExtension(nsAString& aExtension) -{ - aExtension.Truncate(); - return NS_OK; -} - -NS_IMETHODIMP nsFilePicker::SetDefaultExtension(const nsAString& aExtension) -{ - return NS_OK; -} - -// Append an entry to the filters array -NS_IMETHODIMP -nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter) -{ - // "..apps" has to be translated with native executable extensions. - if (aFilter.EqualsLiteral("..apps")) { - mFilters.AppendElement(NS_LITERAL_STRING("*.app")); - } else { - mFilters.AppendElement(aFilter); - } - mTitles.AppendElement(aTitle); - - return NS_OK; -} - -// Get the filter index - do we still need this? -NS_IMETHODIMP nsFilePicker::GetFilterIndex(int32_t *aFilterIndex) -{ - *aFilterIndex = mSelectedTypeIndex; - return NS_OK; -} - -// Set the filter index - do we still need this? -NS_IMETHODIMP nsFilePicker::SetFilterIndex(int32_t aFilterIndex) -{ - mSelectedTypeIndex = aFilterIndex; - return NS_OK; -} diff --git a/widget/cocoa/nsIdleServiceX.h b/widget/cocoa/nsIdleServiceX.h deleted file mode 100644 index f0b3d92ed5..0000000000 --- a/widget/cocoa/nsIdleServiceX.h +++ /dev/null @@ -1,33 +0,0 @@ -/* 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 nsIdleServiceX_h_ -#define nsIdleServiceX_h_ - -#include "nsIdleService.h" - -class nsIdleServiceX : public nsIdleService -{ -public: - NS_DECL_ISUPPORTS_INHERITED - - bool PollIdleTime(uint32_t* aIdleTime) override; - - static already_AddRefed<nsIdleServiceX> GetInstance() - { - RefPtr<nsIdleService> idleService = nsIdleService::GetInstance(); - if (!idleService) { - idleService = new nsIdleServiceX(); - } - - return idleService.forget().downcast<nsIdleServiceX>(); - } - -protected: - nsIdleServiceX() { } - virtual ~nsIdleServiceX() { } - bool UsePollMode() override; -}; - -#endif // nsIdleServiceX_h_ diff --git a/widget/cocoa/nsIdleServiceX.mm b/widget/cocoa/nsIdleServiceX.mm deleted file mode 100644 index 234a154146..0000000000 --- a/widget/cocoa/nsIdleServiceX.mm +++ /dev/null @@ -1,77 +0,0 @@ -/* 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/. */ - -#include "nsIdleServiceX.h" -#include "nsObjCExceptions.h" -#include "nsIServiceManager.h" -#import <Foundation/Foundation.h> - -NS_IMPL_ISUPPORTS_INHERITED0(nsIdleServiceX, nsIdleService) - -bool -nsIdleServiceX::PollIdleTime(uint32_t *aIdleTime) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - kern_return_t rval; - mach_port_t masterPort; - - rval = IOMasterPort(kIOMasterPortDefault, &masterPort); - if (rval != KERN_SUCCESS) - return false; - - io_iterator_t hidItr; - rval = IOServiceGetMatchingServices(masterPort, - IOServiceMatching("IOHIDSystem"), - &hidItr); - - if (rval != KERN_SUCCESS) - return false; - NS_ASSERTION(hidItr, "Our iterator is null, but it ought not to be!"); - - io_registry_entry_t entry = IOIteratorNext(hidItr); - NS_ASSERTION(entry, "Our IO Registry Entry is null, but it shouldn't be!"); - - IOObjectRelease(hidItr); - - NSMutableDictionary *hidProps; - rval = IORegistryEntryCreateCFProperties(entry, - (CFMutableDictionaryRef*)&hidProps, - kCFAllocatorDefault, 0); - if (rval != KERN_SUCCESS) - return false; - NS_ASSERTION(hidProps, "HIDProperties is null, but no error was returned."); - [hidProps autorelease]; - - id idleObj = [hidProps objectForKey:@"HIDIdleTime"]; - NS_ASSERTION([idleObj isKindOfClass: [NSData class]] || - [idleObj isKindOfClass: [NSNumber class]], - "What we got for the idle object is not what we expect!"); - - uint64_t time; - if ([idleObj isKindOfClass: [NSData class]]) - [idleObj getBytes: &time]; - else - time = [idleObj unsignedLongLongValue]; - - IOObjectRelease(entry); - - // convert to ms from ns - time /= 1000000; - if (time > UINT32_MAX) // Overflow will occur - return false; - - *aIdleTime = static_cast<uint32_t>(time); - - return true; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); -} - -bool -nsIdleServiceX::UsePollMode() -{ - return true; -} - diff --git a/widget/cocoa/nsLookAndFeel.h b/widget/cocoa/nsLookAndFeel.h deleted file mode 100644 index 2ad31a2aa1..0000000000 --- a/widget/cocoa/nsLookAndFeel.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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 nsLookAndFeel_h_ -#define nsLookAndFeel_h_ -#include "nsXPLookAndFeel.h" - -class nsLookAndFeel: public nsXPLookAndFeel { -public: - nsLookAndFeel(); - virtual ~nsLookAndFeel(); - - virtual nsresult NativeGetColor(ColorID aID, nscolor &aResult); - virtual nsresult GetIntImpl(IntID aID, int32_t &aResult); - virtual nsresult GetFloatImpl(FloatID aID, float &aResult); - virtual bool GetFontImpl(FontID aID, nsString& aFontName, - gfxFontStyle& aFontStyle, - float aDevPixPerCSSPixel); - virtual char16_t GetPasswordCharacterImpl() - { - // unicode value for the bullet character, used for password textfields. - return 0x2022; - } - - static bool UseOverlayScrollbars(); - - virtual nsTArray<LookAndFeelInt> GetIntCacheImpl(); - virtual void SetIntCacheImpl(const nsTArray<LookAndFeelInt>& aLookAndFeelIntCache); - - virtual void RefreshImpl(); - -protected: - static bool SystemWantsOverlayScrollbars(); - static bool AllowOverlayScrollbarsOverlap(); - -private: - int32_t mUseOverlayScrollbars; - bool mUseOverlayScrollbarsCached; - - int32_t mAllowOverlayScrollbarsOverlap; - bool mAllowOverlayScrollbarsOverlapCached; -}; - -#endif // nsLookAndFeel_h_ diff --git a/widget/cocoa/nsLookAndFeel.mm b/widget/cocoa/nsLookAndFeel.mm deleted file mode 100644 index 0b68cd0e4f..0000000000 --- a/widget/cocoa/nsLookAndFeel.mm +++ /dev/null @@ -1,584 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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/. */ - -#include "nsLookAndFeel.h" -#include "nsCocoaFeatures.h" -#include "nsIServiceManager.h" -#include "nsNativeThemeColors.h" -#include "nsStyleConsts.h" -#include "nsCocoaFeatures.h" -#include "nsIContent.h" -#include "gfxFont.h" -#include "gfxFontConstants.h" -#include "gfxPlatformMac.h" -#include "mozilla/gfx/2D.h" -#include "mozilla/widget/WidgetMessageUtils.h" - -#import <Cocoa/Cocoa.h> - -// This must be included last: -#include "nsObjCExceptions.h" - -enum { - mozNSScrollerStyleLegacy = 0, - mozNSScrollerStyleOverlay = 1 -}; -typedef NSInteger mozNSScrollerStyle; - -@interface NSScroller(AvailableSinceLion) -+ (mozNSScrollerStyle)preferredScrollerStyle; -@end - -nsLookAndFeel::nsLookAndFeel() - : nsXPLookAndFeel() - , mUseOverlayScrollbars(-1) - , mUseOverlayScrollbarsCached(false) - , mAllowOverlayScrollbarsOverlap(-1) - , mAllowOverlayScrollbarsOverlapCached(false) -{ -} - -nsLookAndFeel::~nsLookAndFeel() -{ -} - -static nscolor GetColorFromNSColor(NSColor* aColor) -{ - NSColor* deviceColor = [aColor colorUsingColorSpaceName:NSDeviceRGBColorSpace]; - return NS_RGB((unsigned int)([deviceColor redComponent] * 255.0), - (unsigned int)([deviceColor greenComponent] * 255.0), - (unsigned int)([deviceColor blueComponent] * 255.0)); -} - -static nscolor GetColorFromNSColorWithAlpha(NSColor* aColor, float alpha) -{ - NSColor* deviceColor = [aColor colorUsingColorSpaceName:NSDeviceRGBColorSpace]; - return NS_RGBA((unsigned int)([deviceColor redComponent] * 255.0), - (unsigned int)([deviceColor greenComponent] * 255.0), - (unsigned int)([deviceColor blueComponent] * 255.0), - (unsigned int)(alpha * 255.0)); -} - -nsresult -nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - nsresult res = NS_OK; - - switch (aID) { - case eColorID_WindowBackground: - aColor = NS_RGB(0xff,0xff,0xff); - break; - case eColorID_WindowForeground: - aColor = NS_RGB(0x00,0x00,0x00); - break; - case eColorID_WidgetBackground: - aColor = NS_RGB(0xdd,0xdd,0xdd); - break; - case eColorID_WidgetForeground: - aColor = NS_RGB(0x00,0x00,0x00); - break; - case eColorID_WidgetSelectBackground: - aColor = NS_RGB(0x80,0x80,0x80); - break; - case eColorID_WidgetSelectForeground: - aColor = NS_RGB(0x00,0x00,0x80); - break; - case eColorID_Widget3DHighlight: - aColor = NS_RGB(0xa0,0xa0,0xa0); - break; - case eColorID_Widget3DShadow: - aColor = NS_RGB(0x40,0x40,0x40); - break; - case eColorID_TextBackground: - aColor = NS_RGB(0xff,0xff,0xff); - break; - case eColorID_TextForeground: - aColor = NS_RGB(0x00,0x00,0x00); - break; - case eColorID_TextSelectBackground: - aColor = GetColorFromNSColor([NSColor selectedTextBackgroundColor]); - break; - case eColorID_highlight: // CSS2 color - aColor = GetColorFromNSColor([NSColor alternateSelectedControlColor]); - break; - case eColorID__moz_menuhover: - aColor = GetColorFromNSColor([NSColor alternateSelectedControlColor]); - break; - case eColorID_TextSelectForeground: - GetColor(eColorID_TextSelectBackground, aColor); - if (aColor == 0x000000) - aColor = NS_RGB(0xff,0xff,0xff); - else - aColor = NS_DONT_CHANGE_COLOR; - break; - case eColorID_highlighttext: // CSS2 color - case eColorID__moz_menuhovertext: - aColor = GetColorFromNSColor([NSColor alternateSelectedControlTextColor]); - break; - case eColorID_IMESelectedRawTextBackground: - case eColorID_IMESelectedConvertedTextBackground: - case eColorID_IMERawInputBackground: - case eColorID_IMEConvertedTextBackground: - aColor = NS_TRANSPARENT; - break; - case eColorID_IMESelectedRawTextForeground: - case eColorID_IMESelectedConvertedTextForeground: - case eColorID_IMERawInputForeground: - case eColorID_IMEConvertedTextForeground: - aColor = NS_SAME_AS_FOREGROUND_COLOR; - break; - case eColorID_IMERawInputUnderline: - case eColorID_IMEConvertedTextUnderline: - aColor = NS_40PERCENT_FOREGROUND_COLOR; - break; - case eColorID_IMESelectedRawTextUnderline: - case eColorID_IMESelectedConvertedTextUnderline: - aColor = NS_SAME_AS_FOREGROUND_COLOR; - break; - case eColorID_SpellCheckerUnderline: - aColor = NS_RGB(0xff, 0, 0); - break; - - // - // css2 system colors http://www.w3.org/TR/REC-CSS2/ui.html#system-colors - // - // It's really hard to effectively map these to the Appearance Manager properly, - // since they are modeled word for word after the win32 system colors and don't have any - // real counterparts in the Mac world. I'm sure we'll be tweaking these for - // years to come. - // - // Thanks to mpt26@student.canterbury.ac.nz for the hardcoded values that form the defaults - // if querying the Appearance Manager fails ;) - // - case eColorID__moz_mac_buttonactivetext: - case eColorID__moz_mac_defaultbuttontext: - if (nsCocoaFeatures::OnYosemiteOrLater()) { - aColor = NS_RGB(0xFF,0xFF,0xFF); - break; - } - // Otherwise fall through and return the regular button text: - - case eColorID_buttontext: - case eColorID__moz_buttonhovertext: - aColor = GetColorFromNSColor([NSColor controlTextColor]); - break; - case eColorID_captiontext: - case eColorID_menutext: - case eColorID_infotext: - case eColorID__moz_menubartext: - aColor = GetColorFromNSColor([NSColor textColor]); - break; - case eColorID_windowtext: - aColor = GetColorFromNSColor([NSColor windowFrameTextColor]); - break; - case eColorID_activecaption: - aColor = GetColorFromNSColor([NSColor gridColor]); - break; - case eColorID_activeborder: - aColor = GetColorFromNSColor([NSColor keyboardFocusIndicatorColor]); - break; - case eColorID_appworkspace: - aColor = NS_RGB(0xFF,0xFF,0xFF); - break; - case eColorID_background: - aColor = NS_RGB(0x63,0x63,0xCE); - break; - case eColorID_buttonface: - case eColorID__moz_buttonhoverface: - aColor = NS_RGB(0xF0,0xF0,0xF0); - break; - case eColorID_buttonhighlight: - aColor = NS_RGB(0xFF,0xFF,0xFF); - break; - case eColorID_buttonshadow: - aColor = NS_RGB(0xDC,0xDC,0xDC); - break; - case eColorID_graytext: - aColor = GetColorFromNSColor([NSColor disabledControlTextColor]); - break; - case eColorID_inactiveborder: - aColor = GetColorFromNSColor([NSColor controlBackgroundColor]); - break; - case eColorID_inactivecaption: - aColor = GetColorFromNSColor([NSColor controlBackgroundColor]); - break; - case eColorID_inactivecaptiontext: - aColor = NS_RGB(0x45,0x45,0x45); - break; - case eColorID_scrollbar: - aColor = GetColorFromNSColor([NSColor scrollBarColor]); - break; - case eColorID_threeddarkshadow: - aColor = NS_RGB(0xDC,0xDC,0xDC); - break; - case eColorID_threedshadow: - aColor = NS_RGB(0xE0,0xE0,0xE0); - break; - case eColorID_threedface: - aColor = NS_RGB(0xF0,0xF0,0xF0); - break; - case eColorID_threedhighlight: - aColor = GetColorFromNSColor([NSColor highlightColor]); - break; - case eColorID_threedlightshadow: - aColor = NS_RGB(0xDA,0xDA,0xDA); - break; - case eColorID_menu: - aColor = GetColorFromNSColor([NSColor alternateSelectedControlTextColor]); - break; - case eColorID_infobackground: - aColor = NS_RGB(0xFF,0xFF,0xC7); - break; - case eColorID_windowframe: - aColor = GetColorFromNSColor([NSColor gridColor]); - break; - case eColorID_window: - case eColorID__moz_field: - case eColorID__moz_combobox: - aColor = NS_RGB(0xff,0xff,0xff); - break; - case eColorID__moz_fieldtext: - case eColorID__moz_comboboxtext: - aColor = GetColorFromNSColor([NSColor controlTextColor]); - break; - case eColorID__moz_dialog: - aColor = GetColorFromNSColor([NSColor controlHighlightColor]); - break; - case eColorID__moz_dialogtext: - case eColorID__moz_cellhighlighttext: - case eColorID__moz_html_cellhighlighttext: - aColor = GetColorFromNSColor([NSColor controlTextColor]); - break; - case eColorID__moz_dragtargetzone: - aColor = GetColorFromNSColor([NSColor selectedControlColor]); - break; - case eColorID__moz_mac_chrome_active: - case eColorID__moz_mac_chrome_inactive: { - int grey = NativeGreyColorAsInt(toolbarFillGrey, (aID == eColorID__moz_mac_chrome_active)); - aColor = NS_RGB(grey, grey, grey); - } - break; - case eColorID__moz_mac_focusring: - aColor = GetColorFromNSColorWithAlpha([NSColor keyboardFocusIndicatorColor], 0.48); - break; - case eColorID__moz_mac_menushadow: - aColor = NS_RGB(0xA3,0xA3,0xA3); - break; - case eColorID__moz_mac_menutextdisable: - aColor = NS_RGB(0x98,0x98,0x98); - break; - case eColorID__moz_mac_menutextselect: - aColor = GetColorFromNSColor([NSColor selectedMenuItemTextColor]); - break; - case eColorID__moz_mac_disabledtoolbartext: - aColor = GetColorFromNSColor([NSColor disabledControlTextColor]); - break; - case eColorID__moz_mac_menuselect: - aColor = GetColorFromNSColor([NSColor alternateSelectedControlColor]); - break; - case eColorID__moz_buttondefault: - aColor = NS_RGB(0xDC,0xDC,0xDC); - break; - case eColorID__moz_cellhighlight: - case eColorID__moz_html_cellhighlight: - case eColorID__moz_mac_secondaryhighlight: - // For inactive list selection - aColor = GetColorFromNSColor([NSColor secondarySelectedControlColor]); - break; - case eColorID__moz_eventreerow: - // Background color of even list rows. - aColor = GetColorFromNSColor([[NSColor controlAlternatingRowBackgroundColors] objectAtIndex:0]); - break; - case eColorID__moz_oddtreerow: - // Background color of odd list rows. - aColor = GetColorFromNSColor([[NSColor controlAlternatingRowBackgroundColors] objectAtIndex:1]); - break; - case eColorID__moz_nativehyperlinktext: - // There appears to be no available system defined color. HARDCODING to the appropriate color. - aColor = NS_RGB(0x14,0x4F,0xAE); - break; - default: - NS_WARNING("Someone asked nsILookAndFeel for a color I don't know about"); - aColor = NS_RGB(0xff,0xff,0xff); - res = NS_ERROR_FAILURE; - break; - } - - return res; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -nsresult -nsLookAndFeel::GetIntImpl(IntID aID, int32_t &aResult) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - nsresult res = nsXPLookAndFeel::GetIntImpl(aID, aResult); - if (NS_SUCCEEDED(res)) - return res; - res = NS_OK; - - switch (aID) { - case eIntID_CaretBlinkTime: - aResult = 567; - break; - case eIntID_CaretWidth: - aResult = 1; - break; - case eIntID_ShowCaretDuringSelection: - aResult = 0; - break; - case eIntID_SelectTextfieldsOnKeyFocus: - // Select textfield content when focused by kbd - // used by EventStateManager::sTextfieldSelectModel - aResult = 1; - break; - case eIntID_SubmenuDelay: - aResult = 200; - break; - case eIntID_TooltipDelay: - aResult = 500; - break; - case eIntID_MenusCanOverlapOSBar: - // xul popups are not allowed to overlap the menubar. - aResult = 0; - break; - case eIntID_SkipNavigatingDisabledMenuItem: - aResult = 1; - break; - case eIntID_DragThresholdX: - case eIntID_DragThresholdY: - aResult = 4; - break; - case eIntID_ScrollArrowStyle: - aResult = eScrollArrow_None; - break; - case eIntID_ScrollSliderStyle: - aResult = eScrollThumbStyle_Proportional; - break; - case eIntID_UseOverlayScrollbars: - if (!mUseOverlayScrollbarsCached) { - mUseOverlayScrollbars = SystemWantsOverlayScrollbars() ? 1 : 0; - mUseOverlayScrollbarsCached = true; - } - aResult = mUseOverlayScrollbars; - break; - case eIntID_AllowOverlayScrollbarsOverlap: - if (!mAllowOverlayScrollbarsOverlapCached) { - mAllowOverlayScrollbarsOverlap = AllowOverlayScrollbarsOverlap() ? 1 : 0; - mAllowOverlayScrollbarsOverlapCached = true; - } - aResult = mAllowOverlayScrollbarsOverlap; - break; - case eIntID_ScrollbarDisplayOnMouseMove: - aResult = 0; - break; - case eIntID_ScrollbarFadeBeginDelay: - aResult = 450; - break; - case eIntID_ScrollbarFadeDuration: - aResult = 200; - break; - case eIntID_TreeOpenDelay: - aResult = 1000; - break; - case eIntID_TreeCloseDelay: - aResult = 1000; - break; - case eIntID_TreeLazyScrollDelay: - aResult = 150; - break; - case eIntID_TreeScrollDelay: - aResult = 100; - break; - case eIntID_TreeScrollLinesMax: - aResult = 3; - break; - case eIntID_DWMCompositor: - case eIntID_WindowsClassic: - case eIntID_WindowsDefaultTheme: - case eIntID_TouchEnabled: - case eIntID_WindowsThemeIdentifier: - case eIntID_OperatingSystemVersionIdentifier: - aResult = 0; - res = NS_ERROR_NOT_IMPLEMENTED; - break; - case eIntID_MacGraphiteTheme: - aResult = [NSColor currentControlTint] == NSGraphiteControlTint; - break; - case eIntID_MacLionTheme: - aResult = 1; - break; - case eIntID_MacYosemiteTheme: - aResult = nsCocoaFeatures::OnYosemiteOrLater(); - break; - case eIntID_AlertNotificationOrigin: - aResult = NS_ALERT_TOP; - break; - case eIntID_TabFocusModel: - aResult = [NSApp isFullKeyboardAccessEnabled] ? - nsIContent::eTabFocus_any : nsIContent::eTabFocus_textControlsMask; - break; - case eIntID_ScrollToClick: - { - aResult = [[NSUserDefaults standardUserDefaults] boolForKey:@"AppleScrollerPagingBehavior"]; - } - break; - case eIntID_ChosenMenuItemsShouldBlink: - aResult = 1; - break; - case eIntID_IMERawInputUnderlineStyle: - case eIntID_IMEConvertedTextUnderlineStyle: - case eIntID_IMESelectedRawTextUnderlineStyle: - case eIntID_IMESelectedConvertedTextUnderline: - aResult = NS_STYLE_TEXT_DECORATION_STYLE_SOLID; - break; - case eIntID_SpellCheckerUnderlineStyle: - aResult = NS_STYLE_TEXT_DECORATION_STYLE_DOTTED; - break; - case eIntID_ScrollbarButtonAutoRepeatBehavior: - aResult = 0; - break; - case eIntID_SwipeAnimationEnabled: - aResult = 0; - if ([NSEvent respondsToSelector:@selector( - isSwipeTrackingFromScrollEventsEnabled)]) { - aResult = [NSEvent isSwipeTrackingFromScrollEventsEnabled] ? 1 : 0; - } - break; - case eIntID_ColorPickerAvailable: - aResult = 1; - break; - case eIntID_ContextMenuOffsetVertical: - aResult = -6; - break; - case eIntID_ContextMenuOffsetHorizontal: - aResult = 1; - break; - default: - aResult = 0; - res = NS_ERROR_FAILURE; - } - return res; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -nsresult -nsLookAndFeel::GetFloatImpl(FloatID aID, float &aResult) -{ - nsresult res = nsXPLookAndFeel::GetFloatImpl(aID, aResult); - if (NS_SUCCEEDED(res)) - return res; - res = NS_OK; - - switch (aID) { - case eFloatID_IMEUnderlineRelativeSize: - aResult = 2.0f; - break; - case eFloatID_SpellCheckerUnderlineRelativeSize: - aResult = 2.0f; - break; - default: - aResult = -1.0; - res = NS_ERROR_FAILURE; - } - - return res; -} - -bool nsLookAndFeel::UseOverlayScrollbars() -{ - return GetInt(eIntID_UseOverlayScrollbars) != 0; -} - -bool nsLookAndFeel::SystemWantsOverlayScrollbars() -{ - return ([NSScroller respondsToSelector:@selector(preferredScrollerStyle)] && - [NSScroller preferredScrollerStyle] == mozNSScrollerStyleOverlay); -} - -bool nsLookAndFeel::AllowOverlayScrollbarsOverlap() -{ - return (UseOverlayScrollbars()); -} - -bool -nsLookAndFeel::GetFontImpl(FontID aID, nsString &aFontName, - gfxFontStyle &aFontStyle, - float aDevPixPerCSSPixel) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - // hack for now - if (aID == eFont_Window || aID == eFont_Document) { - aFontStyle.style = NS_FONT_STYLE_NORMAL; - aFontStyle.weight = NS_FONT_WEIGHT_NORMAL; - aFontStyle.stretch = NS_FONT_STRETCH_NORMAL; - aFontStyle.size = 14 * aDevPixPerCSSPixel; - aFontStyle.systemFont = true; - - aFontName.AssignLiteral("sans-serif"); - return true; - } - - gfxPlatformMac::LookupSystemFont(aID, aFontName, aFontStyle, - aDevPixPerCSSPixel); - - return true; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); -} - -nsTArray<LookAndFeelInt> -nsLookAndFeel::GetIntCacheImpl() -{ - nsTArray<LookAndFeelInt> lookAndFeelIntCache = - nsXPLookAndFeel::GetIntCacheImpl(); - - LookAndFeelInt useOverlayScrollbars; - useOverlayScrollbars.id = eIntID_UseOverlayScrollbars; - useOverlayScrollbars.value = GetInt(eIntID_UseOverlayScrollbars); - lookAndFeelIntCache.AppendElement(useOverlayScrollbars); - - LookAndFeelInt allowOverlayScrollbarsOverlap; - allowOverlayScrollbarsOverlap.id = eIntID_AllowOverlayScrollbarsOverlap; - allowOverlayScrollbarsOverlap.value = GetInt(eIntID_AllowOverlayScrollbarsOverlap); - lookAndFeelIntCache.AppendElement(allowOverlayScrollbarsOverlap); - - return lookAndFeelIntCache; -} - -void -nsLookAndFeel::SetIntCacheImpl(const nsTArray<LookAndFeelInt>& aLookAndFeelIntCache) -{ - for (auto entry : aLookAndFeelIntCache) { - switch(entry.id) { - case eIntID_UseOverlayScrollbars: - mUseOverlayScrollbars = entry.value; - mUseOverlayScrollbarsCached = true; - break; - case eIntID_AllowOverlayScrollbarsOverlap: - mAllowOverlayScrollbarsOverlap = entry.value; - mAllowOverlayScrollbarsOverlapCached = true; - break; - } - } -} - -void -nsLookAndFeel::RefreshImpl() -{ - // We should only clear the cache if we're in the main browser process. - // Otherwise, we should wait for the parent to inform us of new values - // to cache via LookAndFeel::SetIntCache. - if (XRE_IsParentProcess()) { - mUseOverlayScrollbarsCached = false; - mAllowOverlayScrollbarsOverlapCached = false; - } -} diff --git a/widget/cocoa/nsMacCursor.h b/widget/cocoa/nsMacCursor.h deleted file mode 100644 index cf9c84c7ea..0000000000 --- a/widget/cocoa/nsMacCursor.h +++ /dev/null @@ -1,105 +0,0 @@ -/* 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 nsMacCursor_h_ -#define nsMacCursor_h_ - -#import <Cocoa/Cocoa.h> -#import "nsIWidget.h" - -/*! @class nsMacCursor - @abstract Represents a native Mac cursor. - @discussion <code>nsMacCursor</code> provides a simple API for creating and working with native Macintosh cursors. - Cursors can be created used without needing to be aware of the way different cursors are implemented, - in particular the details of managing an animated cursor are hidden. -*/ -@interface nsMacCursor : NSObject -{ - @private - NSTimer *mTimer; - @protected - nsCursor mType; - int mFrameCounter; -} - -/*! @method cursorWithCursor: - @abstract Create a cursor by specifying a Cocoa <code>NSCursor</code>. - @discussion Creates a cursor representing the given Cocoa built-in cursor. - @param aCursor the <code>NSCursor</code> to use - @param aType the corresponding <code>nsCursor</code> constant - @result an autoreleased instance of <code>nsMacCursor</code> representing the given <code>NSCursor</code> - */ -+ (nsMacCursor *) cursorWithCursor: (NSCursor *) aCursor type: (nsCursor) aType; - -/*! @method cursorWithImageNamed:hotSpot:type: - @abstract Create a cursor by specifying the name of an image resource to use for the cursor and a hotspot. - @discussion Creates a cursor by loading the named image using the <code>+[NSImage imageNamed:]</code> method. - <p>The image must be compatible with any restrictions laid down by <code>NSCursor</code>. These vary - by operating system version.</p> - <p>The hotspot precisely determines the point where the user clicks when using the cursor.</p> - @param aCursor the name of the image to use for the cursor - @param aPoint the point within the cursor to use as the hotspot - @param aType the corresponding <code>nsCursor</code> constant - @result an autoreleased instance of <code>nsMacCursor</code> that uses the given image and hotspot - */ -+ (nsMacCursor *) cursorWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint type: (nsCursor) aType; - -/*! @method cursorWithFrames:type: - @abstract Create an animated cursor by specifying the frames to use for the animation. - @discussion Creates a cursor that will animate by cycling through the given frames. Each element of the array - must be an instance of <code>NSCursor</code> - @param aCursorFrames an array of <code>NSCursor</code>, representing the frames of an animated cursor, in the - order they should be played. - @param aType the corresponding <code>nsCursor</code> constant - @result an autoreleased instance of <code>nsMacCursor</code> that will animate the given cursor frames - */ -+ (nsMacCursor *) cursorWithFrames: (NSArray *) aCursorFrames type: (nsCursor) aType; - -/*! @method cocoaCursorWithImageNamed:hotSpot: - @abstract Create a Cocoa NSCursor object with a Gecko image resource name and a hotspot point. - @discussion Create a Cocoa NSCursor object with a Gecko image resource name and a hotspot point. - @param imageName the name of the gecko image resource, "tiff" extension is assumed, do not append. - @param aPoint the point within the cursor to use as the hotspot - @result an autoreleased instance of <code>nsMacCursor</code> that will animate the given cursor frames - */ -+ (NSCursor *) cocoaCursorWithImageNamed: (NSString *) imageName hotSpot: (NSPoint) aPoint; - -/*! @method isSet - @abstract Determines whether this cursor is currently active. - @discussion This can be helpful when the Cocoa NSCursor state can be influenced without going - through nsCursorManager. - @result whether the cursor is currently set - */ -- (BOOL) isSet; - -/*! @method set - @abstract Set the cursor. - @discussion Makes this cursor the current cursor. If the cursor is animated, the animation is started. - */ -- (void) set; - -/*! @method unset - @abstract Unset the cursor. The cursor will return to the default (usually the arrow cursor). - @discussion Unsets the cursor. If the cursor is animated, the animation is stopped. - */ -- (void) unset; - -/*! @method isAnimated - @abstract Tests whether this cursor is animated. - @discussion Use this method to determine whether a cursor is animated - @result YES if the cursor is animated (has more than one frame), NO if it is a simple static cursor. - */ -- (BOOL) isAnimated; - -/** @method cursorType - @abstract Get the cursor type for this cursor - @discussion This method returns the <code>nsCursor</code> constant that corresponds to this cursor, which is - equivalent to the CSS name for the cursor. - @result The nsCursor constant corresponding to this cursor, or nsCursor's 'eCursorCount' if the cursor - is a custom cursor loaded from a URI - */ -- (nsCursor) type; -@end - -#endif // nsMacCursor_h_ diff --git a/widget/cocoa/nsMacCursor.mm b/widget/cocoa/nsMacCursor.mm deleted file mode 100644 index 4fcdfd3e5d..0000000000 --- a/widget/cocoa/nsMacCursor.mm +++ /dev/null @@ -1,382 +0,0 @@ -/* 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/. */ - -#include "nsMacCursor.h" -#include "nsObjCExceptions.h" -#include "nsDebug.h" -#include "nsDirectoryServiceDefs.h" -#include "nsCOMPtr.h" -#include "nsIFile.h" -#include "nsString.h" - -/*! @category nsMacCursor (PrivateMethods) - @abstract Private methods internal to the nsMacCursor class. - @discussion <code>nsMacCursor</code> is effectively an abstract class. It does not define complete - behaviour in and of itself, the subclasses defined in this file provide the useful implementations. -*/ -@interface nsMacCursor (PrivateMethods) - -/*! @method getNextCursorFrame - @abstract get the index of the next cursor frame to display. - @discussion Increments and returns the frame counter of an animated cursor. - @result The index of the next frame to display in the cursor animation -*/ -- (int) getNextCursorFrame; - -/*! @method numFrames - @abstract Query the number of frames in this cursor's animation. - @discussion Returns the number of frames in this cursor's animation. Static cursors return 1. -*/ -- (int) numFrames; - -/*! @method createTimer - @abstract Create a Timer to use to animate the cursor. - @discussion Creates an instance of <code>NSTimer</code> which is used to drive the cursor animation. - This method should only be called for cursors that are animated. -*/ -- (void) createTimer; - -/*! @method destroyTimer - @abstract Destroy any timer instance associated with this cursor. - @discussion Invalidates and releases any <code>NSTimer</code> instance associated with this cursor. - */ -- (void) destroyTimer; -/*! @method destroyTimer - @abstract Destroy any timer instance associated with this cursor. - @discussion Invalidates and releases any <code>NSTimer</code> instance associated with this cursor. -*/ - -/*! @method advanceAnimatedCursor: - @abstract Method called by animation timer to perform animation. - @discussion Called by an animated cursor's associated timer to advance the animation to the next frame. - Determines which frame should occur next and sets the cursor to that frame. - @param aTimer the timer causing the animation -*/ -- (void) advanceAnimatedCursor: (NSTimer *) aTimer; - -/*! @method setFrame: - @abstract Sets the current cursor, using an index to determine which frame in the animation to display. - @discussion Sets the current cursor. The frame index determines which frame is shown if the cursor is animated. - Frames and numbered from <code>0</code> to <code>-[nsMacCursor numFrames] - 1</code>. A static cursor - has a single frame, numbered 0. - @param aFrameIndex the index indicating which frame from the animation to display -*/ -- (void) setFrame: (int) aFrameIndex; - -@end - -/*! @class nsCocoaCursor - @abstract Implementation of <code>nsMacCursor</code> that uses Cocoa <code>NSCursor</code> instances. - @discussion Displays a static or animated cursor, using Cocoa <code>NSCursor</code> instances. These can be either - built-in <code>NSCursor</code> instances, or custom <code>NSCursor</code>s created from images. - When more than one <code>NSCursor</code> is provided, the cursor will use these as animation frames. -*/ -@interface nsCocoaCursor : nsMacCursor -{ - @private - NSArray *mFrames; - NSCursor *mLastSetCocoaCursor; -} - -/*! @method initWithFrames: - @abstract Create an animated cursor by specifying the frames to use for the animation. - @discussion Creates a cursor that will animate by cycling through the given frames. Each element of the array - must be an instance of <code>NSCursor</code> - @param aCursorFrames an array of <code>NSCursor</code>, representing the frames of an animated cursor, in the - order they should be played. - @param aType the corresponding <code>nsCursor</code> constant - @result an instance of <code>nsCocoaCursor</code> that will animate the given cursor frames - */ -- (id) initWithFrames: (NSArray *) aCursorFrames type: (nsCursor) aType; - -/*! @method initWithCursor: - @abstract Create a cursor by specifying a Cocoa <code>NSCursor</code>. - @discussion Creates a cursor representing the given Cocoa built-in cursor. - @param aCursor the <code>NSCursor</code> to use - @param aType the corresponding <code>nsCursor</code> constant - @result an instance of <code>nsCocoaCursor</code> representing the given <code>NSCursor</code> -*/ -- (id) initWithCursor: (NSCursor *) aCursor type: (nsCursor) aType; - -/*! @method initWithImageNamed:hotSpot: - @abstract Create a cursor by specifying the name of an image resource to use for the cursor and a hotspot. - @discussion Creates a cursor by loading the named image using the <code>+[NSImage imageNamed:]</code> method. - <p>The image must be compatible with any restrictions laid down by <code>NSCursor</code>. These vary - by operating system version.</p> - <p>The hotspot precisely determines the point where the user clicks when using the cursor.</p> - @param aCursor the name of the image to use for the cursor - @param aPoint the point within the cursor to use as the hotspot - @param aType the corresponding <code>nsCursor</code> constant - @result an instance of <code>nsCocoaCursor</code> that uses the given image and hotspot -*/ -- (id) initWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint type: (nsCursor) aType; - -@end - -@implementation nsMacCursor - -+ (nsMacCursor *) cursorWithCursor: (NSCursor *) aCursor type: (nsCursor) aType -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - return [[[nsCocoaCursor alloc] initWithCursor:aCursor type:aType] autorelease]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -+ (nsMacCursor *) cursorWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint type: (nsCursor) aType -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - return [[[nsCocoaCursor alloc] initWithImageNamed:aCursorImage hotSpot:aPoint type:aType] autorelease]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -+ (nsMacCursor *) cursorWithFrames: (NSArray *) aCursorFrames type: (nsCursor) aType -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - return [[[nsCocoaCursor alloc] initWithFrames:aCursorFrames type:aType] autorelease]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -+ (NSCursor *) cocoaCursorWithImageNamed: (NSString *) imageName hotSpot: (NSPoint) aPoint -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - nsCOMPtr<nsIFile> resDir; - nsAutoCString resPath; - NSString* pathToImage, *pathToHiDpiImage; - NSImage* cursorImage, *hiDpiCursorImage; - - nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(resDir)); - if (NS_FAILED(rv)) - goto INIT_FAILURE; - resDir->AppendNative(NS_LITERAL_CSTRING("res")); - resDir->AppendNative(NS_LITERAL_CSTRING("cursors")); - - rv = resDir->GetNativePath(resPath); - if (NS_FAILED(rv)) - goto INIT_FAILURE; - - pathToImage = [NSString stringWithUTF8String:(const char*)resPath.get()]; - if (!pathToImage) - goto INIT_FAILURE; - pathToImage = [pathToImage stringByAppendingPathComponent:imageName]; - pathToHiDpiImage = [pathToImage stringByAppendingString:@"@2x"]; - // Add same extension to both image paths. - pathToImage = [pathToImage stringByAppendingPathExtension:@"png"]; - pathToHiDpiImage = [pathToHiDpiImage stringByAppendingPathExtension:@"png"]; - - cursorImage = [[[NSImage alloc] initWithContentsOfFile:pathToImage] autorelease]; - if (!cursorImage) - goto INIT_FAILURE; - - // Note 1: There are a few different ways to get a hidpi image via - // initWithContentsOfFile. We let the OS handle this here: when the - // file basename ends in "@2x", it will be displayed at native resolution - // instead of being pixel-doubled. See bug 784909 comment 7 for alternates ways. - // - // Note 2: The OS is picky, and will ignore the hidpi representation - // unless it is exactly twice the size of the lowdpi image. - hiDpiCursorImage = [[[NSImage alloc] initWithContentsOfFile:pathToHiDpiImage] autorelease]; - if (hiDpiCursorImage) { - NSImageRep *imageRep = [[hiDpiCursorImage representations] objectAtIndex:0]; - [cursorImage addRepresentation: imageRep]; - } - return [[[NSCursor alloc] initWithImage:cursorImage hotSpot:aPoint] autorelease]; - -INIT_FAILURE: - NS_WARNING("Problem getting path to cursor image file!"); - [self release]; - return nil; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (BOOL) isSet -{ - // implemented by subclasses - return NO; -} - -- (void) set -{ - if ([self isAnimated]) { - [self createTimer]; - } - // if the cursor isn't animated or the timer creation fails for any reason... - if (!mTimer) { - [self setFrame:0]; - } -} - -- (void) unset -{ - [self destroyTimer]; -} - -- (BOOL) isAnimated -{ - return [self numFrames] > 1; -} - -- (int) numFrames -{ - // subclasses need to override this to support animation - return 1; -} - -- (int) getNextCursorFrame -{ - mFrameCounter = (mFrameCounter + 1) % [self numFrames]; - return mFrameCounter; -} - -- (void) createTimer -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mTimer) { - mTimer = [[NSTimer scheduledTimerWithTimeInterval:0.25 - target:self - selector:@selector(advanceAnimatedCursor:) - userInfo:nil - repeats:YES] retain]; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void) destroyTimer -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (mTimer) { - [mTimer invalidate]; - [mTimer release]; - mTimer = nil; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void) advanceAnimatedCursor: (NSTimer *) aTimer -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if ([aTimer isValid]) { - [self setFrame:[self getNextCursorFrame]]; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void) setFrame: (int) aFrameIndex -{ - // subclasses need to do something useful here -} - -- (nsCursor) type { - return mType; -} - -- (void) dealloc -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [self destroyTimer]; - [super dealloc]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -@end - -@implementation nsCocoaCursor - -- (id) initWithFrames: (NSArray *) aCursorFrames type: (nsCursor) aType -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - self = [super init]; - NSEnumerator *it = [aCursorFrames objectEnumerator]; - NSObject *frame = nil; - while ((frame = [it nextObject])) { - NS_ASSERTION([frame isKindOfClass:[NSCursor class]], "Invalid argument: All frames must be of type NSCursor"); - } - mFrames = [aCursorFrames retain]; - mFrameCounter = 0; - mType = aType; - return self; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (id) initWithCursor: (NSCursor *) aCursor type: (nsCursor) aType -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - NSArray *frame = [NSArray arrayWithObjects:aCursor, nil]; - return [self initWithFrames:frame type:aType]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (id) initWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint type: (nsCursor) aType -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - return [self initWithCursor:[nsMacCursor cocoaCursorWithImageNamed:aCursorImage hotSpot:aPoint] type:aType]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (BOOL) isSet -{ - return [NSCursor currentCursor] == mLastSetCocoaCursor; -} - -- (void) setFrame: (int) aFrameIndex -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - NSCursor* newCursor = [mFrames objectAtIndex:aFrameIndex]; - [newCursor set]; - mLastSetCocoaCursor = newCursor; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (int) numFrames -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - return [mFrames count]; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); -} - -- (NSString *) description -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - return [mFrames description]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (void) dealloc -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [mFrames release]; - [super dealloc]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -@end diff --git a/widget/cocoa/nsMacDockSupport.h b/widget/cocoa/nsMacDockSupport.h deleted file mode 100644 index a638b89e03..0000000000 --- a/widget/cocoa/nsMacDockSupport.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ -/* 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/. */ - -#include "nsIMacDockSupport.h" -#include "nsIStandaloneNativeMenu.h" -#include "nsITaskbarProgress.h" -#include "nsITimer.h" -#include "nsCOMPtr.h" -#include "nsString.h" -#include "nsNativeThemeCocoa.h" - -class nsMacDockSupport : public nsIMacDockSupport, public nsITaskbarProgress -{ -public: - nsMacDockSupport(); - - NS_DECL_ISUPPORTS - NS_DECL_NSIMACDOCKSUPPORT - NS_DECL_NSITASKBARPROGRESS - -protected: - virtual ~nsMacDockSupport(); - - nsCOMPtr<nsIStandaloneNativeMenu> mDockMenu; - nsString mBadgeText; - - NSImage *mAppIcon, *mProgressBackground; - - HIRect mProgressBounds; - nsTaskbarProgressState mProgressState; - double mProgressFraction; - nsCOMPtr<nsITimer> mProgressTimer; - RefPtr<nsNativeThemeCocoa> mTheme; - - static void RedrawIconCallback(nsITimer* aTimer, void* aClosure); - - bool InitProgress(); - nsresult RedrawIcon(); -}; diff --git a/widget/cocoa/nsMacDockSupport.mm b/widget/cocoa/nsMacDockSupport.mm deleted file mode 100644 index 56b37822bd..0000000000 --- a/widget/cocoa/nsMacDockSupport.mm +++ /dev/null @@ -1,174 +0,0 @@ -/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ -/* 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/. */ - -#import <Cocoa/Cocoa.h> - -#include "nsComponentManagerUtils.h" -#include "nsMacDockSupport.h" -#include "nsObjCExceptions.h" - -NS_IMPL_ISUPPORTS(nsMacDockSupport, nsIMacDockSupport, nsITaskbarProgress) - -nsMacDockSupport::nsMacDockSupport() -: mAppIcon(nil) -, mProgressBackground(nil) -, mProgressState(STATE_NO_PROGRESS) -, mProgressFraction(0.0) -{ - mProgressTimer = do_CreateInstance(NS_TIMER_CONTRACTID); -} - -nsMacDockSupport::~nsMacDockSupport() -{ - if (mAppIcon) { - [mAppIcon release]; - mAppIcon = nil; - } - if (mProgressBackground) { - [mProgressBackground release]; - mProgressBackground = nil; - } - if (mProgressTimer) { - mProgressTimer->Cancel(); - mProgressTimer = nullptr; - } -} - -NS_IMETHODIMP -nsMacDockSupport::GetDockMenu(nsIStandaloneNativeMenu ** aDockMenu) -{ - nsCOMPtr<nsIStandaloneNativeMenu> dockMenu(mDockMenu); - dockMenu.forget(aDockMenu); - return NS_OK; -} - -NS_IMETHODIMP -nsMacDockSupport::SetDockMenu(nsIStandaloneNativeMenu * aDockMenu) -{ - mDockMenu = aDockMenu; - return NS_OK; -} - -NS_IMETHODIMP -nsMacDockSupport::ActivateApplication(bool aIgnoreOtherApplications) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - [[NSApplication sharedApplication] activateIgnoringOtherApps:aIgnoreOtherApplications]; - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsMacDockSupport::SetBadgeText(const nsAString& aBadgeText) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NSDockTile *tile = [[NSApplication sharedApplication] dockTile]; - mBadgeText = aBadgeText; - if (aBadgeText.IsEmpty()) - [tile setBadgeLabel: nil]; - else - [tile setBadgeLabel:[NSString stringWithCharacters:reinterpret_cast<const unichar*>(mBadgeText.get()) - length:mBadgeText.Length()]]; - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsMacDockSupport::GetBadgeText(nsAString& aBadgeText) -{ - aBadgeText = mBadgeText; - return NS_OK; -} - -NS_IMETHODIMP -nsMacDockSupport::SetProgressState(nsTaskbarProgressState aState, - uint64_t aCurrentValue, - uint64_t aMaxValue) -{ - NS_ENSURE_ARG_RANGE(aState, 0, STATE_PAUSED); - if (aState == STATE_NO_PROGRESS || aState == STATE_INDETERMINATE) { - NS_ENSURE_TRUE(aCurrentValue == 0, NS_ERROR_INVALID_ARG); - NS_ENSURE_TRUE(aMaxValue == 0, NS_ERROR_INVALID_ARG); - } - if (aCurrentValue > aMaxValue) { - return NS_ERROR_ILLEGAL_VALUE; - } - - mProgressState = aState; - if (aMaxValue == 0) { - mProgressFraction = 0; - } else { - mProgressFraction = (double)aCurrentValue / aMaxValue; - } - - if (mProgressState == STATE_NORMAL || mProgressState == STATE_INDETERMINATE) { - int perSecond = 8; // Empirically determined, see bug 848792 - mProgressTimer->InitWithFuncCallback(RedrawIconCallback, this, 1000 / perSecond, - nsITimer::TYPE_REPEATING_SLACK); - return NS_OK; - } else { - mProgressTimer->Cancel(); - return RedrawIcon(); - } -} - -// static -void nsMacDockSupport::RedrawIconCallback(nsITimer* aTimer, void* aClosure) -{ - static_cast<nsMacDockSupport*>(aClosure)->RedrawIcon(); -} - -// Return whether to draw progress -bool nsMacDockSupport::InitProgress() -{ - if (mProgressState != STATE_NORMAL && mProgressState != STATE_INDETERMINATE) { - return false; - } - - if (!mAppIcon) { - mProgressTimer = do_CreateInstance(NS_TIMER_CONTRACTID); - mAppIcon = [[NSImage imageNamed:@"NSApplicationIcon"] retain]; - mProgressBackground = [mAppIcon copyWithZone:nil]; - mTheme = new nsNativeThemeCocoa(); - - NSSize sz = [mProgressBackground size]; - mProgressBounds = CGRectMake(sz.width * 1/32, sz.height * 3/32, - sz.width * 30/32, sz.height * 4/32); - [mProgressBackground lockFocus]; - [[NSColor whiteColor] set]; - NSRectFill(NSRectFromCGRect(mProgressBounds)); - [mProgressBackground unlockFocus]; - } - return true; -} - -nsresult -nsMacDockSupport::RedrawIcon() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (InitProgress()) { - // TODO: - Implement ERROR and PAUSED states? - NSImage *icon = [mProgressBackground copyWithZone:nil]; - bool isIndeterminate = (mProgressState != STATE_NORMAL); - - [icon lockFocus]; - CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; - mTheme->DrawProgress(ctx, mProgressBounds, isIndeterminate, - true, mProgressFraction, 1.0, NULL); - [icon unlockFocus]; - [NSApp setApplicationIconImage:icon]; - [icon release]; - } else { - [NSApp setApplicationIconImage:mAppIcon]; - } - - return NS_OK; - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} diff --git a/widget/cocoa/nsMacWebAppUtils.h b/widget/cocoa/nsMacWebAppUtils.h deleted file mode 100644 index 98ef235615..0000000000 --- a/widget/cocoa/nsMacWebAppUtils.h +++ /dev/null @@ -1,22 +0,0 @@ -/* 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 _MAC_WEB_APP_UTILS_H_ -#define _MAC_WEB_APP_UTILS_H_ - -#include "nsIMacWebAppUtils.h" - -#define NS_MACWEBAPPUTILS_CONTRACTID "@mozilla.org/widget/mac-web-app-utils;1" - -class nsMacWebAppUtils : public nsIMacWebAppUtils { -public: - nsMacWebAppUtils() {} - - NS_DECL_ISUPPORTS - NS_DECL_NSIMACWEBAPPUTILS - -protected: - virtual ~nsMacWebAppUtils() {} -}; - -#endif //_MAC_WEB_APP_UTILS_H_ diff --git a/widget/cocoa/nsMacWebAppUtils.mm b/widget/cocoa/nsMacWebAppUtils.mm deleted file mode 100644 index 1b98cef7cc..0000000000 --- a/widget/cocoa/nsMacWebAppUtils.mm +++ /dev/null @@ -1,82 +0,0 @@ -/* 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/. */ - -#import <Cocoa/Cocoa.h> - -#include "nsMacWebAppUtils.h" -#include "nsCOMPtr.h" -#include "nsCocoaUtils.h" -#include "nsString.h" - -// This must be included last: -#include "nsObjCExceptions.h" - -// Find the path to the app with the given bundleIdentifier, if any. -// Note that the OS will return the path to the newest binary, if there is more than one. -// The determination of 'newest' is complex and beyond the scope of this comment. - -NS_IMPL_ISUPPORTS(nsMacWebAppUtils, nsIMacWebAppUtils) - -NS_IMETHODIMP nsMacWebAppUtils::PathForAppWithIdentifier(const nsAString& bundleIdentifier, nsAString& outPath) { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - outPath.Truncate(); - - nsAutoreleasePool localPool; - - //note that the result of this expression might be nil, meaning no matching app was found. - NSString* temp = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier: - [NSString stringWithCharacters:reinterpret_cast<const unichar*>(((nsString)bundleIdentifier).get()) - length:((nsString)bundleIdentifier).Length()]]; - - if (temp) { - // Copy out the resultant absolute path into outPath if non-nil. - nsCocoaUtils::GetStringForNSString(temp, outPath); - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP nsMacWebAppUtils::LaunchAppWithIdentifier(const nsAString& bundleIdentifier) { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - nsAutoreleasePool localPool; - - // Note this might return false, meaning the app wasnt launched for some reason. - BOOL success = [[NSWorkspace sharedWorkspace] launchAppWithBundleIdentifier: - [NSString stringWithCharacters:reinterpret_cast<const unichar*>(((nsString)bundleIdentifier).get()) - length:((nsString)bundleIdentifier).Length()] - options: (NSWorkspaceLaunchOptions)0 - additionalEventParamDescriptor: nil - launchIdentifier: NULL]; - - return success ? NS_OK : NS_ERROR_FAILURE; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP nsMacWebAppUtils::TrashApp(const nsAString& path, nsITrashAppCallback* aCallback) { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (NS_WARN_IF(!aCallback)) { - return NS_ERROR_INVALID_ARG; - } - - nsCOMPtr<nsITrashAppCallback> callback = aCallback; - - NSString* tempString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(((nsString)path).get()) - length:path.Length()]; - - [[NSWorkspace sharedWorkspace] recycleURLs: [NSArray arrayWithObject:[NSURL fileURLWithPath:tempString]] - completionHandler: ^(NSDictionary *newURLs, NSError *error) { - nsresult rv = (error == nil) ? NS_OK : NS_ERROR_FAILURE; - callback->TrashAppFinished(rv); - }]; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} diff --git a/widget/cocoa/nsMenuBarX.h b/widget/cocoa/nsMenuBarX.h deleted file mode 100644 index 7cbb8ce62a..0000000000 --- a/widget/cocoa/nsMenuBarX.h +++ /dev/null @@ -1,128 +0,0 @@ -/* -*- 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 nsMenuBarX_h_ -#define nsMenuBarX_h_ - -#import <Cocoa/Cocoa.h> - -#include "mozilla/UniquePtr.h" -#include "nsMenuBaseX.h" -#include "nsMenuGroupOwnerX.h" -#include "nsChangeObserver.h" -#include "nsINativeMenuService.h" -#include "nsString.h" - -class nsMenuX; -class nsIWidget; -class nsIContent; - -// The native menu service for creating native menu bars. -class nsNativeMenuServiceX : public nsINativeMenuService -{ -public: - NS_DECL_ISUPPORTS - - nsNativeMenuServiceX() {} - - NS_IMETHOD CreateNativeMenuBar(nsIWidget* aParent, nsIContent* aMenuBarNode) override; - -protected: - virtual ~nsNativeMenuServiceX() {} -}; - -// Objective-C class used to allow us to intervene with keyboard event handling. -// We allow mouse actions to work normally. -@interface GeckoNSMenu : NSMenu -{ -} -@end - -// Objective-C class used as action target for menu items -@interface NativeMenuItemTarget : NSObject -{ -} --(IBAction)menuItemHit:(id)sender; -@end - -// Objective-C class used for menu items on the Services menu to allow Gecko -// to override their standard behavior in order to stop key equivalents from -// firing in certain instances. -@interface GeckoServicesNSMenuItem : NSMenuItem -{ -} -- (id) target; -- (SEL) action; -- (void) _doNothing:(id)sender; -@end - -// Objective-C class used as the Services menu so that Gecko can override the -// standard behavior of the Services menu in order to stop key equivalents -// from firing in certain instances. -@interface GeckoServicesNSMenu : NSMenu -{ -} -- (void)addItem:(NSMenuItem *)newItem; -- (NSMenuItem *)addItemWithTitle:(NSString *)aString action:(SEL)aSelector keyEquivalent:(NSString *)keyEquiv; -- (void)insertItem:(NSMenuItem *)newItem atIndex:(NSInteger)index; -- (NSMenuItem *)insertItemWithTitle:(NSString *)aString action:(SEL)aSelector keyEquivalent:(NSString *)keyEquiv atIndex:(NSInteger)index; -- (void) _overrideClassOfMenuItem:(NSMenuItem *)menuItem; -@end - -// Once instantiated, this object lives until its DOM node or its parent window is destroyed. -// Do not hold references to this, they can become invalid any time the DOM node can be destroyed. -class nsMenuBarX : public nsMenuGroupOwnerX, public nsChangeObserver -{ -public: - nsMenuBarX(); - virtual ~nsMenuBarX(); - - static NativeMenuItemTarget* sNativeEventTarget; - static nsMenuBarX* sLastGeckoMenuBarPainted; - - // The following content nodes have been removed from the menu system. - // We save them here for use in command handling. - nsCOMPtr<nsIContent> mAboutItemContent; - nsCOMPtr<nsIContent> mPrefItemContent; - nsCOMPtr<nsIContent> mQuitItemContent; - - // nsChangeObserver - NS_DECL_CHANGEOBSERVER - - // nsMenuObjectX - void* NativeData() override {return (void*)mNativeMenu;} - nsMenuObjectTypeX MenuObjectType() override {return eMenuBarObjectType;} - - // nsMenuBarX - nsresult Create(nsIWidget* aParent, nsIContent* aContent); - void SetParent(nsIWidget* aParent); - uint32_t GetMenuCount(); - bool MenuContainsAppMenu(); - nsMenuX* GetMenuAt(uint32_t aIndex); - nsMenuX* GetXULHelpMenu(); - void SetSystemHelpMenu(); - nsresult Paint(); - void ForceUpdateNativeMenuAt(const nsAString& indexString); - void ForceNativeMenuReload(); // used for testing - static char GetLocalizedAccelKey(const char *shortcutID); - static void ResetNativeApplicationMenu(); - -protected: - void ConstructNativeMenus(); - void ConstructFallbackNativeMenus(); - nsresult InsertMenuAtIndex(nsMenuX* aMenu, uint32_t aIndex); - void RemoveMenuAtIndex(uint32_t aIndex); - void HideItem(nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode); - void AquifyMenuBar(); - NSMenuItem* CreateNativeAppMenuItem(nsMenuX* inMenu, const nsAString& nodeID, SEL action, - int tag, NativeMenuItemTarget* target); - nsresult CreateApplicationMenu(nsMenuX* inMenu); - - nsTArray<mozilla::UniquePtr<nsMenuX>> mMenuArray; - nsIWidget* mParentWindow; // [weak] - GeckoNSMenu* mNativeMenu; // root menu, representing entire menu bar -}; - -#endif // nsMenuBarX_h_ diff --git a/widget/cocoa/nsMenuBarX.mm b/widget/cocoa/nsMenuBarX.mm deleted file mode 100644 index ff25eb81fc..0000000000 --- a/widget/cocoa/nsMenuBarX.mm +++ /dev/null @@ -1,979 +0,0 @@ -/* -*- 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/. */ - -#include <objc/objc-runtime.h> - -#include "nsMenuBarX.h" -#include "nsMenuX.h" -#include "nsMenuItemX.h" -#include "nsMenuUtilsX.h" -#include "nsCocoaUtils.h" -#include "nsCocoaWindow.h" -#include "nsChildView.h" - -#include "nsCOMPtr.h" -#include "nsString.h" -#include "nsGkAtoms.h" -#include "nsObjCExceptions.h" -#include "nsThreadUtils.h" - -#include "nsIContent.h" -#include "nsIWidget.h" -#include "nsIDocument.h" -#include "nsIDOMDocument.h" -#include "nsIDOMElement.h" -#include "nsIAppStartup.h" -#include "nsIStringBundle.h" -#include "nsToolkitCompsCID.h" - -NativeMenuItemTarget* nsMenuBarX::sNativeEventTarget = nil; -nsMenuBarX* nsMenuBarX::sLastGeckoMenuBarPainted = nullptr; -NSMenu* sApplicationMenu = nil; -BOOL sApplicationMenuIsFallback = NO; -BOOL gSomeMenuBarPainted = NO; - -// We keep references to the first quit and pref item content nodes we find, which -// will be from the hidden window. We use these when the document for the current -// window does not have a quit or pref item. We don't need strong refs here because -// these items are always strong ref'd by their owning menu bar (instance variable). -static nsIContent* sAboutItemContent = nullptr; -static nsIContent* sPrefItemContent = nullptr; -static nsIContent* sQuitItemContent = nullptr; - -NS_IMPL_ISUPPORTS(nsNativeMenuServiceX, nsINativeMenuService) - -NS_IMETHODIMP nsNativeMenuServiceX::CreateNativeMenuBar(nsIWidget* aParent, nsIContent* aMenuBarNode) -{ - NS_ASSERTION(NS_IsMainThread(), "Attempting to create native menu bar on wrong thread!"); - - RefPtr<nsMenuBarX> mb = new nsMenuBarX(); - if (!mb) - return NS_ERROR_OUT_OF_MEMORY; - - return mb->Create(aParent, aMenuBarNode); -} - -nsMenuBarX::nsMenuBarX() -: nsMenuGroupOwnerX(), mParentWindow(nullptr) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - mNativeMenu = [[GeckoNSMenu alloc] initWithTitle:@"MainMenuBar"]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -nsMenuBarX::~nsMenuBarX() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (nsMenuBarX::sLastGeckoMenuBarPainted == this) - nsMenuBarX::sLastGeckoMenuBarPainted = nullptr; - - // the quit/pref items of a random window might have been used if there was no - // hidden window, thus we need to invalidate the weak references. - if (sAboutItemContent == mAboutItemContent) - sAboutItemContent = nullptr; - if (sQuitItemContent == mQuitItemContent) - sQuitItemContent = nullptr; - if (sPrefItemContent == mPrefItemContent) - sPrefItemContent = nullptr; - - // make sure we unregister ourselves as a content observer - if (mContent) { - UnregisterForContentChanges(mContent); - } - - // We have to manually clear the array here because clearing causes menu items - // to call back into the menu bar to unregister themselves. We don't want to - // depend on member variable ordering to ensure that the array gets cleared - // before the registration hash table is destroyed. - mMenuArray.Clear(); - - [mNativeMenu release]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -nsresult nsMenuBarX::Create(nsIWidget* aParent, nsIContent* aContent) -{ - if (!aParent) - return NS_ERROR_INVALID_ARG; - - mParentWindow = aParent; - mContent = aContent; - - if (mContent) { - AquifyMenuBar(); - - nsresult rv = nsMenuGroupOwnerX::Create(mContent); - if (NS_FAILED(rv)) - return rv; - - RegisterForContentChanges(mContent, this); - ConstructNativeMenus(); - } else { - ConstructFallbackNativeMenus(); - } - - // Give this to the parent window. The parent takes ownership. - static_cast<nsCocoaWindow*>(mParentWindow)->SetMenuBar(this); - - return NS_OK; -} - -void nsMenuBarX::ConstructNativeMenus() -{ - uint32_t count = mContent->GetChildCount(); - for (uint32_t i = 0; i < count; i++) { - nsIContent *menuContent = mContent->GetChildAt(i); - if (menuContent && - menuContent->IsXULElement(nsGkAtoms::menu)) { - nsMenuX* newMenu = new nsMenuX(); - if (newMenu) { - nsresult rv = newMenu->Create(this, this, menuContent); - if (NS_SUCCEEDED(rv)) - InsertMenuAtIndex(newMenu, GetMenuCount()); - else - delete newMenu; - } - } - } -} - -void nsMenuBarX::ConstructFallbackNativeMenus() -{ - if (sApplicationMenu) { - // Menu has already been built. - return; - } - - nsCOMPtr<nsIStringBundle> stringBundle; - - nsCOMPtr<nsIStringBundleService> bundleSvc = do_GetService(NS_STRINGBUNDLE_CONTRACTID); - bundleSvc->CreateBundle("chrome://global/locale/fallbackMenubar.properties", getter_AddRefs(stringBundle)); - - if (!stringBundle) { - return; - } - - nsXPIDLString labelUTF16; - nsXPIDLString keyUTF16; - - const char16_t* labelProp = u"quitMenuitem.label"; - const char16_t* keyProp = u"quitMenuitem.key"; - - stringBundle->GetStringFromName(labelProp, getter_Copies(labelUTF16)); - stringBundle->GetStringFromName(keyProp, getter_Copies(keyUTF16)); - - NSString* labelStr = [NSString stringWithUTF8String: - NS_ConvertUTF16toUTF8(labelUTF16).get()]; - NSString* keyStr= [NSString stringWithUTF8String: - NS_ConvertUTF16toUTF8(keyUTF16).get()]; - - if (!nsMenuBarX::sNativeEventTarget) { - nsMenuBarX::sNativeEventTarget = [[NativeMenuItemTarget alloc] init]; - } - - sApplicationMenu = [[[[NSApp mainMenu] itemAtIndex:0] submenu] retain]; - NSMenuItem* quitMenuItem = [[[NSMenuItem alloc] initWithTitle:labelStr - action:@selector(menuItemHit:) - keyEquivalent:keyStr] autorelease]; - [quitMenuItem setTarget:nsMenuBarX::sNativeEventTarget]; - [quitMenuItem setTag:eCommand_ID_Quit]; - [sApplicationMenu addItem:quitMenuItem]; - sApplicationMenuIsFallback = YES; -} - -uint32_t nsMenuBarX::GetMenuCount() -{ - return mMenuArray.Length(); -} - -bool nsMenuBarX::MenuContainsAppMenu() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - return ([mNativeMenu numberOfItems] > 0 && - [[mNativeMenu itemAtIndex:0] submenu] == sApplicationMenu); - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); -} - -nsresult nsMenuBarX::InsertMenuAtIndex(nsMenuX* aMenu, uint32_t aIndex) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // If we've only yet created a fallback global Application menu (using - // ContructFallbackNativeMenus()), destroy it before recreating it properly. - if (sApplicationMenu && sApplicationMenuIsFallback) { - ResetNativeApplicationMenu(); - } - // If we haven't created a global Application menu yet, do it. - if (!sApplicationMenu) { - nsresult rv = NS_OK; // avoid warning about rv being unused - rv = CreateApplicationMenu(aMenu); - NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create Application menu"); - - // Hook the new Application menu up to the menu bar. - NSMenu* mainMenu = [NSApp mainMenu]; - NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!"); - [[mainMenu itemAtIndex:0] setSubmenu:sApplicationMenu]; - } - - // add menu to array that owns our menus - mMenuArray.InsertElementAt(aIndex, aMenu); - - // hook up submenus - nsIContent* menuContent = aMenu->Content(); - if (menuContent->GetChildCount() > 0 && - !nsMenuUtilsX::NodeIsHiddenOrCollapsed(menuContent)) { - int insertionIndex = nsMenuUtilsX::CalculateNativeInsertionPoint(this, aMenu); - if (MenuContainsAppMenu()) - insertionIndex++; - [mNativeMenu insertItem:aMenu->NativeMenuItem() atIndex:insertionIndex]; - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -void nsMenuBarX::RemoveMenuAtIndex(uint32_t aIndex) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (mMenuArray.Length() <= aIndex) { - NS_ERROR("Attempting submenu removal with bad index!"); - return; - } - - // Our native menu and our internal menu object array might be out of sync. - // This happens, for example, when a submenu is hidden. Because of this we - // should not assume that a native submenu is hooked up. - NSMenuItem* nativeMenuItem = mMenuArray[aIndex]->NativeMenuItem(); - int nativeMenuItemIndex = [mNativeMenu indexOfItem:nativeMenuItem]; - if (nativeMenuItemIndex != -1) - [mNativeMenu removeItemAtIndex:nativeMenuItemIndex]; - - mMenuArray.RemoveElementAt(aIndex); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void nsMenuBarX::ObserveAttributeChanged(nsIDocument* aDocument, - nsIContent* aContent, - nsIAtom* aAttribute) -{ -} - -void nsMenuBarX::ObserveContentRemoved(nsIDocument* aDocument, - nsIContent* aChild, - int32_t aIndexInContainer) -{ - RemoveMenuAtIndex(aIndexInContainer); -} - -void nsMenuBarX::ObserveContentInserted(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aChild) -{ - nsMenuX* newMenu = new nsMenuX(); - if (newMenu) { - nsresult rv = newMenu->Create(this, this, aChild); - if (NS_SUCCEEDED(rv)) - InsertMenuAtIndex(newMenu, aContainer->IndexOf(aChild)); - else - delete newMenu; - } -} - -void nsMenuBarX::ForceUpdateNativeMenuAt(const nsAString& indexString) -{ - NSString* locationString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading()) - length:indexString.Length()]; - NSArray* indexes = [locationString componentsSeparatedByString:@"|"]; - unsigned int indexCount = [indexes count]; - if (indexCount == 0) - return; - - nsMenuX* currentMenu = NULL; - int targetIndex = [[indexes objectAtIndex:0] intValue]; - int visible = 0; - uint32_t length = mMenuArray.Length(); - // first find a menu in the menu bar - for (unsigned int i = 0; i < length; i++) { - nsMenuX* menu = mMenuArray[i].get(); - if (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(menu->Content())) { - visible++; - if (visible == (targetIndex + 1)) { - currentMenu = menu; - break; - } - } - } - - if (!currentMenu) - return; - - // fake open/close to cause lazy update to happen so submenus populate - currentMenu->MenuOpened(); - currentMenu->MenuClosed(); - - // now find the correct submenu - for (unsigned int i = 1; currentMenu && i < indexCount; i++) { - targetIndex = [[indexes objectAtIndex:i] intValue]; - visible = 0; - length = currentMenu->GetItemCount(); - for (unsigned int j = 0; j < length; j++) { - nsMenuObjectX* targetMenu = currentMenu->GetItemAt(j); - if (!targetMenu) - return; - if (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(targetMenu->Content())) { - visible++; - if (targetMenu->MenuObjectType() == eSubmenuObjectType && visible == (targetIndex + 1)) { - currentMenu = static_cast<nsMenuX*>(targetMenu); - // fake open/close to cause lazy update to happen - currentMenu->MenuOpened(); - currentMenu->MenuClosed(); - break; - } - } - } - } -} - -// Calling this forces a full reload of the menu system, reloading all native -// menus and their items. -// Without this testing is hard because changes to the DOM affect the native -// menu system lazily. -void nsMenuBarX::ForceNativeMenuReload() -{ - // tear down everything - while (GetMenuCount() > 0) - RemoveMenuAtIndex(0); - - // construct everything - ConstructNativeMenus(); -} - -nsMenuX* nsMenuBarX::GetMenuAt(uint32_t aIndex) -{ - if (mMenuArray.Length() <= aIndex) { - NS_ERROR("Requesting menu at invalid index!"); - return NULL; - } - return mMenuArray[aIndex].get(); -} - -nsMenuX* nsMenuBarX::GetXULHelpMenu() -{ - // The Help menu is usually (always?) the last one, so we start there and - // count back. - for (int32_t i = GetMenuCount() - 1; i >= 0; --i) { - nsMenuX* aMenu = GetMenuAt(i); - if (aMenu && nsMenuX::IsXULHelpMenu(aMenu->Content())) - return aMenu; - } - return nil; -} - -// On SnowLeopard and later we must tell the OS which is our Help menu. -// Otherwise it will only add Spotlight for Help (the Search item) to our -// Help menu if its label/title is "Help" -- i.e. if the menu is in English. -// This resolves bugs 489196 and 539317. -void nsMenuBarX::SetSystemHelpMenu() -{ - nsMenuX* xulHelpMenu = GetXULHelpMenu(); - if (xulHelpMenu) { - NSMenu* helpMenu = (NSMenu*)xulHelpMenu->NativeData(); - if (helpMenu) - [NSApp setHelpMenu:helpMenu]; - } -} - -nsresult nsMenuBarX::Paint() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // Don't try to optimize anything in this painting by checking - // sLastGeckoMenuBarPainted because the menubar can be manipulated by - // native dialogs and sheet code and other things besides this paint method. - - // We have to keep the same menu item for the Application menu so we keep - // passing it along. - NSMenu* outgoingMenu = [NSApp mainMenu]; - NS_ASSERTION([outgoingMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!"); - - NSMenuItem* appMenuItem = [[outgoingMenu itemAtIndex:0] retain]; - [outgoingMenu removeItemAtIndex:0]; - [mNativeMenu insertItem:appMenuItem atIndex:0]; - [appMenuItem release]; - - // Set menu bar and event target. - [NSApp setMainMenu:mNativeMenu]; - SetSystemHelpMenu(); - nsMenuBarX::sLastGeckoMenuBarPainted = this; - - gSomeMenuBarPainted = YES; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// Returns the 'key' attribute of the 'shortcutID' object (if any) in the -// currently active menubar's DOM document. 'shortcutID' should be the id -// (i.e. the name) of a component that defines a commonly used (and -// localized) cmd+key shortcut, and belongs to a keyset containing similar -// objects. For example "key_selectAll". Returns a value that can be -// compared to the first character of [NSEvent charactersIgnoringModifiers] -// when [NSEvent modifierFlags] == NSCommandKeyMask. -char nsMenuBarX::GetLocalizedAccelKey(const char *shortcutID) -{ - if (!sLastGeckoMenuBarPainted) - return 0; - - nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(sLastGeckoMenuBarPainted->mContent->OwnerDoc())); - if (!domDoc) - return 0; - - NS_ConvertASCIItoUTF16 shortcutIDStr((const char *)shortcutID); - nsCOMPtr<nsIDOMElement> shortcutElement; - domDoc->GetElementById(shortcutIDStr, getter_AddRefs(shortcutElement)); - nsCOMPtr<nsIContent> shortcutContent = do_QueryInterface(shortcutElement); - if (!shortcutContent) - return 0; - - nsAutoString key; - shortcutContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, key); - NS_LossyConvertUTF16toASCII keyASC(key.get()); - const char *keyASCPtr = keyASC.get(); - if (!keyASCPtr) - return 0; - // If keyID's 'key' attribute isn't exactly one character long, it's not - // what we're looking for. - if (strlen(keyASCPtr) != sizeof(char)) - return 0; - // Make sure retval is lower case. - char retval = tolower(keyASCPtr[0]); - - return retval; -} - -/* static */ -void nsMenuBarX::ResetNativeApplicationMenu() -{ - [sApplicationMenu removeAllItems]; - [sApplicationMenu release]; - sApplicationMenu = nil; - sApplicationMenuIsFallback = NO; -} - -// Hide the item in the menu by setting the 'hidden' attribute. Returns it in |outHiddenNode| so -// the caller can hang onto it if they so choose. It is acceptable to pass nsull -// for |outHiddenNode| if the caller doesn't care about the hidden node. -void nsMenuBarX::HideItem(nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode) -{ - nsCOMPtr<nsIDOMElement> menuItem; - inDoc->GetElementById(inID, getter_AddRefs(menuItem)); - nsCOMPtr<nsIContent> menuContent(do_QueryInterface(menuItem)); - if (menuContent) { - menuContent->SetAttr(kNameSpaceID_None, nsGkAtoms::hidden, NS_LITERAL_STRING("true"), false); - if (outHiddenNode) { - *outHiddenNode = menuContent.get(); - NS_IF_ADDREF(*outHiddenNode); - } - } -} - -// Do what is necessary to conform to the Aqua guidelines for menus. -void nsMenuBarX::AquifyMenuBar() -{ - nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mContent->GetComposedDoc())); - if (domDoc) { - // remove the "About..." item and its separator - HideItem(domDoc, NS_LITERAL_STRING("aboutSeparator"), nullptr); - HideItem(domDoc, NS_LITERAL_STRING("aboutName"), getter_AddRefs(mAboutItemContent)); - if (!sAboutItemContent) - sAboutItemContent = mAboutItemContent; - - // remove quit item and its separator - HideItem(domDoc, NS_LITERAL_STRING("menu_FileQuitSeparator"), nullptr); - HideItem(domDoc, NS_LITERAL_STRING("menu_FileQuitItem"), getter_AddRefs(mQuitItemContent)); - if (!sQuitItemContent) - sQuitItemContent = mQuitItemContent; - - // remove prefs item and its separator, but save off the pref content node - // so we can invoke its command later. - HideItem(domDoc, NS_LITERAL_STRING("menu_PrefsSeparator"), nullptr); - HideItem(domDoc, NS_LITERAL_STRING("menu_preferences"), getter_AddRefs(mPrefItemContent)); - if (!sPrefItemContent) - sPrefItemContent = mPrefItemContent; - - // hide items that we use for the Application menu - HideItem(domDoc, NS_LITERAL_STRING("menu_mac_services"), nullptr); - HideItem(domDoc, NS_LITERAL_STRING("menu_mac_hide_app"), nullptr); - HideItem(domDoc, NS_LITERAL_STRING("menu_mac_hide_others"), nullptr); - HideItem(domDoc, NS_LITERAL_STRING("menu_mac_show_all"), nullptr); - } -} - -// for creating menu items destined for the Application menu -NSMenuItem* nsMenuBarX::CreateNativeAppMenuItem(nsMenuX* inMenu, const nsAString& nodeID, SEL action, - int tag, NativeMenuItemTarget* target) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - nsCOMPtr<nsIDocument> doc = inMenu->Content()->GetUncomposedDoc(); - if (!doc) { - return nil; - } - - nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(doc)); - if (!domdoc) { - return nil; - } - - // Get information from the gecko menu item - nsAutoString label; - nsAutoString modifiers; - nsAutoString key; - nsCOMPtr<nsIDOMElement> menuItem; - domdoc->GetElementById(nodeID, getter_AddRefs(menuItem)); - if (menuItem) { - menuItem->GetAttribute(NS_LITERAL_STRING("label"), label); - menuItem->GetAttribute(NS_LITERAL_STRING("modifiers"), modifiers); - menuItem->GetAttribute(NS_LITERAL_STRING("key"), key); - } - else { - return nil; - } - - // Get more information about the key equivalent. Start by - // finding the key node we need. - NSString* keyEquiv = nil; - unsigned int macKeyModifiers = 0; - if (!key.IsEmpty()) { - nsCOMPtr<nsIDOMElement> keyElement; - domdoc->GetElementById(key, getter_AddRefs(keyElement)); - if (keyElement) { - nsCOMPtr<nsIContent> keyContent (do_QueryInterface(keyElement)); - // first grab the key equivalent character - nsAutoString keyChar(NS_LITERAL_STRING(" ")); - keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyChar); - if (!keyChar.EqualsLiteral(" ")) { - keyEquiv = [[NSString stringWithCharacters:reinterpret_cast<const unichar*>(keyChar.get()) - length:keyChar.Length()] lowercaseString]; - } - // now grab the key equivalent modifiers - nsAutoString modifiersStr; - keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiersStr); - uint8_t geckoModifiers = nsMenuUtilsX::GeckoModifiersForNodeAttribute(modifiersStr); - macKeyModifiers = nsMenuUtilsX::MacModifiersForGeckoModifiers(geckoModifiers); - } - } - // get the label into NSString-form - NSString* labelString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(label.get()) - length:label.Length()]; - - if (!labelString) - labelString = @""; - if (!keyEquiv) - keyEquiv = @""; - - // put together the actual NSMenuItem - NSMenuItem* newMenuItem = [[NSMenuItem alloc] initWithTitle:labelString action:action keyEquivalent:keyEquiv]; - - [newMenuItem setTag:tag]; - [newMenuItem setTarget:target]; - [newMenuItem setKeyEquivalentModifierMask:macKeyModifiers]; - - MenuItemInfo * info = [[MenuItemInfo alloc] initWithMenuGroupOwner:this]; - [newMenuItem setRepresentedObject:info]; - [info release]; - - return newMenuItem; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -// build the Application menu shared by all menu bars -nsresult nsMenuBarX::CreateApplicationMenu(nsMenuX* inMenu) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // At this point, the application menu is the application menu from - // the nib in cocoa widgets. We do not have a way to create an application - // menu manually, so we grab the one from the nib and use that. - sApplicationMenu = [[[[NSApp mainMenu] itemAtIndex:0] submenu] retain]; - -/* - We support the following menu items here: - - Menu Item DOM Node ID Notes - - ======================== - = About This App = <- aboutName - ======================== - = Preferences... = <- menu_preferences - ======================== - = Services > = <- menu_mac_services <- (do not define key equivalent) - ======================== - = Hide App = <- menu_mac_hide_app - = Hide Others = <- menu_mac_hide_others - = Show All = <- menu_mac_show_all - ======================== - = Quit = <- menu_FileQuitItem - ======================== - - If any of them are ommitted from the application's DOM, we just don't add - them. We always add a "Quit" item, but if an app developer does not provide a - DOM node with the right ID for the Quit item, we add it in English. App - developers need only add each node with a label and a key equivalent (if they - want one). Other attributes are optional. Like so: - - <menuitem id="menu_preferences" - label="&preferencesCmdMac.label;" - key="open_prefs_key"/> - - We need to use this system for localization purposes, until we have a better way - to define the Application menu to be used on Mac OS X. -*/ - - if (sApplicationMenu) { - // This code reads attributes we are going to care about from the DOM elements - - NSMenuItem *itemBeingAdded = nil; - BOOL addAboutSeparator = FALSE; - - // Add the About menu item - itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("aboutName"), @selector(menuItemHit:), - eCommand_ID_About, nsMenuBarX::sNativeEventTarget); - if (itemBeingAdded) { - [sApplicationMenu addItem:itemBeingAdded]; - [itemBeingAdded release]; - itemBeingAdded = nil; - - addAboutSeparator = TRUE; - } - - // Add separator if either the About item or software update item exists - if (addAboutSeparator) - [sApplicationMenu addItem:[NSMenuItem separatorItem]]; - - // Add the Preferences menu item - itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("menu_preferences"), @selector(menuItemHit:), - eCommand_ID_Prefs, nsMenuBarX::sNativeEventTarget); - if (itemBeingAdded) { - [sApplicationMenu addItem:itemBeingAdded]; - [itemBeingAdded release]; - itemBeingAdded = nil; - - // Add separator after Preferences menu - [sApplicationMenu addItem:[NSMenuItem separatorItem]]; - } - - // Add Services menu item - itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("menu_mac_services"), nil, - 0, nil); - if (itemBeingAdded) { - [sApplicationMenu addItem:itemBeingAdded]; - - // set this menu item up as the Mac OS X Services menu - NSMenu* servicesMenu = [[GeckoServicesNSMenu alloc] initWithTitle:@""]; - [itemBeingAdded setSubmenu:servicesMenu]; - [NSApp setServicesMenu:servicesMenu]; - - [itemBeingAdded release]; - itemBeingAdded = nil; - - // Add separator after Services menu - [sApplicationMenu addItem:[NSMenuItem separatorItem]]; - } - - BOOL addHideShowSeparator = FALSE; - - // Add menu item to hide this application - itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("menu_mac_hide_app"), @selector(menuItemHit:), - eCommand_ID_HideApp, nsMenuBarX::sNativeEventTarget); - if (itemBeingAdded) { - [sApplicationMenu addItem:itemBeingAdded]; - [itemBeingAdded release]; - itemBeingAdded = nil; - - addHideShowSeparator = TRUE; - } - - // Add menu item to hide other applications - itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("menu_mac_hide_others"), @selector(menuItemHit:), - eCommand_ID_HideOthers, nsMenuBarX::sNativeEventTarget); - if (itemBeingAdded) { - [sApplicationMenu addItem:itemBeingAdded]; - [itemBeingAdded release]; - itemBeingAdded = nil; - - addHideShowSeparator = TRUE; - } - - // Add menu item to show all applications - itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("menu_mac_show_all"), @selector(menuItemHit:), - eCommand_ID_ShowAll, nsMenuBarX::sNativeEventTarget); - if (itemBeingAdded) { - [sApplicationMenu addItem:itemBeingAdded]; - [itemBeingAdded release]; - itemBeingAdded = nil; - - addHideShowSeparator = TRUE; - } - - // Add a separator after the hide/show menus if at least one exists - if (addHideShowSeparator) - [sApplicationMenu addItem:[NSMenuItem separatorItem]]; - - // Add quit menu item - itemBeingAdded = CreateNativeAppMenuItem(inMenu, NS_LITERAL_STRING("menu_FileQuitItem"), @selector(menuItemHit:), - eCommand_ID_Quit, nsMenuBarX::sNativeEventTarget); - if (itemBeingAdded) { - [sApplicationMenu addItem:itemBeingAdded]; - [itemBeingAdded release]; - itemBeingAdded = nil; - } - else { - // the current application does not have a DOM node for "Quit". Add one - // anyway, in English. - NSMenuItem* defaultQuitItem = [[[NSMenuItem alloc] initWithTitle:@"Quit" action:@selector(menuItemHit:) - keyEquivalent:@"q"] autorelease]; - [defaultQuitItem setTarget:nsMenuBarX::sNativeEventTarget]; - [defaultQuitItem setTag:eCommand_ID_Quit]; - [sApplicationMenu addItem:defaultQuitItem]; - } - } - - return (sApplicationMenu) ? NS_OK : NS_ERROR_FAILURE; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -void nsMenuBarX::SetParent(nsIWidget* aParent) -{ - mParentWindow = aParent; -} - -// -// Objective-C class used to allow us to have keyboard commands -// look like they are doing something but actually do nothing. -// We allow mouse actions to work normally. -// - -// Controls whether or not native menu items should invoke their commands. -static BOOL gMenuItemsExecuteCommands = YES; - -@implementation GeckoNSMenu - -// Keyboard commands should not cause menu items to invoke their -// commands when there is a key window because we'd rather send -// the keyboard command to the window. We still have the menus -// go through the mechanics so they'll give the proper visual -// feedback. -- (BOOL)performKeyEquivalent:(NSEvent *)theEvent -{ - // We've noticed that Mac OS X expects this check in subclasses before - // calling NSMenu's "performKeyEquivalent:". - // - // There is no case in which we'd need to do anything or return YES - // when we have no items so we can just do this check first. - if ([self numberOfItems] <= 0) { - return NO; - } - - NSWindow *keyWindow = [NSApp keyWindow]; - - // If there is no key window then just behave normally. This - // probably means that this menu is associated with Gecko's - // hidden window. - if (!keyWindow) { - return [super performKeyEquivalent:theEvent]; - } - - NSResponder *firstResponder = [keyWindow firstResponder]; - - gMenuItemsExecuteCommands = NO; - [super performKeyEquivalent:theEvent]; - gMenuItemsExecuteCommands = YES; // return to default - - // Return YES if we invoked a command and there is now no key window or we changed - // the first responder. In this case we do not want to propagate the event because - // we don't want it handled again. - if (![NSApp keyWindow] || [[NSApp keyWindow] firstResponder] != firstResponder) { - return YES; - } - - // Return NO so that we can handle the event via NSView's "keyDown:". - return NO; -} - -@end - -// -// Objective-C class used as action target for menu items -// - -@implementation NativeMenuItemTarget - -// called when some menu item in this menu gets hit --(IBAction)menuItemHit:(id)sender -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!gMenuItemsExecuteCommands) { - return; - } - - int tag = [sender tag]; - - nsMenuGroupOwnerX* menuGroupOwner = nullptr; - nsMenuBarX* menuBar = nullptr; - MenuItemInfo* info = [sender representedObject]; - - if (info) { - menuGroupOwner = [info menuGroupOwner]; - if (!menuGroupOwner) { - return; - } - if (menuGroupOwner->MenuObjectType() == eMenuBarObjectType) { - menuBar = static_cast<nsMenuBarX*>(menuGroupOwner); - } - } - - // Do special processing if this is for an app-global command. - if (tag == eCommand_ID_About) { - nsIContent* mostSpecificContent = sAboutItemContent; - if (menuBar && menuBar->mAboutItemContent) - mostSpecificContent = menuBar->mAboutItemContent; - nsMenuUtilsX::DispatchCommandTo(mostSpecificContent); - return; - } - else if (tag == eCommand_ID_Prefs) { - nsIContent* mostSpecificContent = sPrefItemContent; - if (menuBar && menuBar->mPrefItemContent) - mostSpecificContent = menuBar->mPrefItemContent; - nsMenuUtilsX::DispatchCommandTo(mostSpecificContent); - return; - } - else if (tag == eCommand_ID_HideApp) { - [NSApp hide:sender]; - return; - } - else if (tag == eCommand_ID_HideOthers) { - [NSApp hideOtherApplications:sender]; - return; - } - else if (tag == eCommand_ID_ShowAll) { - [NSApp unhideAllApplications:sender]; - return; - } - else if (tag == eCommand_ID_Quit) { - nsIContent* mostSpecificContent = sQuitItemContent; - if (menuBar && menuBar->mQuitItemContent) - mostSpecificContent = menuBar->mQuitItemContent; - // If we have some content for quit we execute it. Otherwise we send a native app terminate - // message. If you want to stop a quit from happening, provide quit content and return - // the event as unhandled. - if (mostSpecificContent) { - nsMenuUtilsX::DispatchCommandTo(mostSpecificContent); - } - else { - nsCOMPtr<nsIAppStartup> appStartup = do_GetService(NS_APPSTARTUP_CONTRACTID); - if (appStartup) { - appStartup->Quit(nsIAppStartup::eAttemptQuit); - } - } - return; - } - - // given the commandID, look it up in our hashtable and dispatch to - // that menu item. - if (menuGroupOwner) { - nsMenuItemX* menuItem = menuGroupOwner->GetMenuItemForCommandID(static_cast<uint32_t>(tag)); - if (menuItem) - menuItem->DoCommand(); - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -@end - -// Objective-C class used for menu items on the Services menu to allow Gecko -// to override their standard behavior in order to stop key equivalents from -// firing in certain instances. When gMenuItemsExecuteCommands is NO, we return -// a dummy target and action instead of the actual target and action. - -@implementation GeckoServicesNSMenuItem - -- (id) target -{ - id realTarget = [super target]; - if (gMenuItemsExecuteCommands) - return realTarget; - else - return realTarget ? self : nil; -} - -- (SEL) action -{ - SEL realAction = [super action]; - if (gMenuItemsExecuteCommands) - return realAction; - else - return realAction ? @selector(_doNothing:) : NULL; -} - -- (void) _doNothing:(id)sender -{ -} - -@end - -// Objective-C class used as the Services menu so that Gecko can override the -// standard behavior of the Services menu in order to stop key equivalents -// from firing in certain instances. - -@implementation GeckoServicesNSMenu - -- (void)addItem:(NSMenuItem *)newItem -{ - [self _overrideClassOfMenuItem:newItem]; - [super addItem:newItem]; -} - -- (NSMenuItem *)addItemWithTitle:(NSString *)aString action:(SEL)aSelector keyEquivalent:(NSString *)keyEquiv -{ - NSMenuItem * newItem = [super addItemWithTitle:aString action:aSelector keyEquivalent:keyEquiv]; - [self _overrideClassOfMenuItem:newItem]; - return newItem; -} - -- (void)insertItem:(NSMenuItem *)newItem atIndex:(NSInteger)index -{ - [self _overrideClassOfMenuItem:newItem]; - [super insertItem:newItem atIndex:index]; -} - -- (NSMenuItem *)insertItemWithTitle:(NSString *)aString action:(SEL)aSelector keyEquivalent:(NSString *)keyEquiv atIndex:(NSInteger)index -{ - NSMenuItem * newItem = [super insertItemWithTitle:aString action:aSelector keyEquivalent:keyEquiv atIndex:index]; - [self _overrideClassOfMenuItem:newItem]; - return newItem; -} - -- (void) _overrideClassOfMenuItem:(NSMenuItem *)menuItem -{ - if ([menuItem class] == [NSMenuItem class]) - object_setClass(menuItem, [GeckoServicesNSMenuItem class]); -} - -@end diff --git a/widget/cocoa/nsMenuBaseX.h b/widget/cocoa/nsMenuBaseX.h deleted file mode 100644 index 5b9f89c560..0000000000 --- a/widget/cocoa/nsMenuBaseX.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -*- 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 nsMenuBaseX_h_ -#define nsMenuBaseX_h_ - -#import <Foundation/Foundation.h> - -#include "nsCOMPtr.h" -#include "nsIContent.h" - -enum nsMenuObjectTypeX { - eMenuBarObjectType, - eSubmenuObjectType, - eMenuItemObjectType, - eStandaloneNativeMenuObjectType, -}; - -// All menu objects subclass this. -// Menu bars are owned by their top-level nsIWidgets. -// All other objects are memory-managed based on the DOM. -// Content removal deletes them immediately and nothing else should. -// Do not attempt to hold strong references to them or delete them. -class nsMenuObjectX -{ -public: - virtual ~nsMenuObjectX() { } - virtual nsMenuObjectTypeX MenuObjectType()=0; - virtual void* NativeData()=0; - nsIContent* Content() { return mContent; } - - /** - * Called when an icon of a menu item somewhere in this menu has updated. - * Menu objects with parents need to propagate the notification to their - * parent. - */ - virtual void IconUpdated() {} - -protected: - nsCOMPtr<nsIContent> mContent; -}; - - -// -// Object stored as "representedObject" for all menu items -// - -class nsMenuGroupOwnerX; - -@interface MenuItemInfo : NSObject -{ - nsMenuGroupOwnerX * mMenuGroupOwner; -} - -- (id) initWithMenuGroupOwner:(nsMenuGroupOwnerX *)aMenuGroupOwner; -- (nsMenuGroupOwnerX *) menuGroupOwner; -- (void) setMenuGroupOwner:(nsMenuGroupOwnerX *)aMenuGroupOwner; - -@end - - -// Special command IDs that we know Mac OS X does not use for anything else. -// We use these in place of carbon's IDs for these commands in order to stop -// Carbon from messing with our event handlers. See bug 346883. - -enum { - eCommand_ID_About = 1, - eCommand_ID_Prefs = 2, - eCommand_ID_Quit = 3, - eCommand_ID_HideApp = 4, - eCommand_ID_HideOthers = 5, - eCommand_ID_ShowAll = 6, - eCommand_ID_Update = 7, - eCommand_ID_Last = 8 -}; - -#endif // nsMenuBaseX_h_ diff --git a/widget/cocoa/nsMenuGroupOwnerX.h b/widget/cocoa/nsMenuGroupOwnerX.h deleted file mode 100644 index 657f420b56..0000000000 --- a/widget/cocoa/nsMenuGroupOwnerX.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- 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 nsMenuGroupOwnerX_h_ -#define nsMenuGroupOwnerX_h_ - -#import <Cocoa/Cocoa.h> - -#include "nsMenuBaseX.h" -#include "nsIMutationObserver.h" -#include "nsHashKeys.h" -#include "nsDataHashtable.h" -#include "nsString.h" - -class nsMenuItemX; -class nsChangeObserver; -class nsIWidget; -class nsIContent; - -class nsMenuGroupOwnerX : public nsMenuObjectX, public nsIMutationObserver -{ -public: - nsMenuGroupOwnerX(); - - nsresult Create(nsIContent * aContent); - - void RegisterForContentChanges(nsIContent* aContent, - nsChangeObserver* aMenuObject); - void UnregisterForContentChanges(nsIContent* aContent); - uint32_t RegisterForCommand(nsMenuItemX* aItem); - void UnregisterCommand(uint32_t aCommandID); - nsMenuItemX* GetMenuItemForCommandID(uint32_t inCommandID); - void AddMenuItemInfoToSet(MenuItemInfo* info); - - NS_DECL_ISUPPORTS - NS_DECL_NSIMUTATIONOBSERVER - -protected: - virtual ~nsMenuGroupOwnerX(); - - nsChangeObserver* LookupContentChangeObserver(nsIContent* aContent); - - uint32_t mCurrentCommandID; // unique command id (per menu-bar) to - // give to next item that asks - - // stores observers for content change notification - nsDataHashtable<nsPtrHashKey<nsIContent>, nsChangeObserver *> mContentToObserverTable; - - // stores mapping of command IDs to menu objects - nsDataHashtable<nsUint32HashKey, nsMenuItemX *> mCommandToMenuObjectTable; - - // Stores references to all the MenuItemInfo objects created with weak - // references to us. They may live longer than we do, so when we're - // destroyed we need to clear all their weak references. This avoids - // crashes in -[NativeMenuItemTarget menuItemHit:]. See bug 1131473. - NSMutableSet* mInfoSet; -}; - -#endif // nsMenuGroupOwner_h_ diff --git a/widget/cocoa/nsMenuGroupOwnerX.mm b/widget/cocoa/nsMenuGroupOwnerX.mm deleted file mode 100644 index 661a52bd80..0000000000 --- a/widget/cocoa/nsMenuGroupOwnerX.mm +++ /dev/null @@ -1,261 +0,0 @@ -/* -*- 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/. */ - -#include "nsMenuGroupOwnerX.h" -#include "nsMenuBarX.h" -#include "nsMenuX.h" -#include "nsMenuItemX.h" -#include "nsMenuUtilsX.h" -#include "nsCocoaUtils.h" -#include "nsCocoaWindow.h" - -#include "nsCOMPtr.h" -#include "nsString.h" -#include "nsObjCExceptions.h" -#include "nsThreadUtils.h" - -#include "mozilla/dom/Element.h" -#include "nsIWidget.h" -#include "nsIDocument.h" -#include "nsIDOMDocument.h" -#include "nsIDOMElement.h" - -#include "nsINode.h" - -using namespace mozilla; - -NS_IMPL_ISUPPORTS(nsMenuGroupOwnerX, nsIMutationObserver) - - -nsMenuGroupOwnerX::nsMenuGroupOwnerX() -: mCurrentCommandID(eCommand_ID_Last) -{ - mInfoSet = [[NSMutableSet setWithCapacity:10] retain]; -} - - -nsMenuGroupOwnerX::~nsMenuGroupOwnerX() -{ - MOZ_ASSERT(mContentToObserverTable.Count() == 0, "have outstanding mutation observers!\n"); - - // The MenuItemInfo objects in mInfoSet may live longer than we do. So when - // we get destroyed we need to invalidate all their mMenuGroupOwner pointers. - NSEnumerator* counter = [mInfoSet objectEnumerator]; - MenuItemInfo* info; - while ((info = (MenuItemInfo*) [counter nextObject])) { - [info setMenuGroupOwner:nil]; - } - [mInfoSet release]; -} - - -nsresult nsMenuGroupOwnerX::Create(nsIContent* aContent) -{ - if (!aContent) - return NS_ERROR_INVALID_ARG; - - mContent = aContent; - - return NS_OK; -} - - -// -// nsIMutationObserver -// - - -void nsMenuGroupOwnerX::CharacterDataWillChange(nsIDocument* aDocument, - nsIContent* aContent, - CharacterDataChangeInfo* aInfo) -{ -} - - -void nsMenuGroupOwnerX::CharacterDataChanged(nsIDocument* aDocument, - nsIContent* aContent, - CharacterDataChangeInfo* aInfo) -{ -} - - -void nsMenuGroupOwnerX::ContentAppended(nsIDocument* aDocument, - nsIContent* aContainer, - nsIContent* aFirstNewContent, - int32_t /* unused */) -{ - for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) { - ContentInserted(aDocument, aContainer, cur, 0); - } -} - - -void nsMenuGroupOwnerX::NodeWillBeDestroyed(const nsINode * aNode) -{ -} - - -void nsMenuGroupOwnerX::AttributeWillChange(nsIDocument* aDocument, - dom::Element* aContent, - int32_t aNameSpaceID, - nsIAtom* aAttribute, - int32_t aModType, - const nsAttrValue* aNewValue) -{ -} - -void nsMenuGroupOwnerX::NativeAnonymousChildListChange(nsIDocument* aDocument, - nsIContent* aContent, - bool aIsRemove) -{ -} - -void nsMenuGroupOwnerX::AttributeChanged(nsIDocument* aDocument, - dom::Element* aElement, - int32_t aNameSpaceID, - nsIAtom* aAttribute, - int32_t aModType, - const nsAttrValue* aOldValue) -{ - nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); - nsChangeObserver* obs = LookupContentChangeObserver(aElement); - if (obs) - obs->ObserveAttributeChanged(aDocument, aElement, aAttribute); -} - - -void nsMenuGroupOwnerX::ContentRemoved(nsIDocument * aDocument, - nsIContent * aContainer, - nsIContent * aChild, - int32_t aIndexInContainer, - nsIContent * aPreviousSibling) -{ - if (!aContainer) { - return; - } - - nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); - nsChangeObserver* obs = LookupContentChangeObserver(aContainer); - if (obs) - obs->ObserveContentRemoved(aDocument, aChild, aIndexInContainer); - else if (aContainer != mContent) { - // We do a lookup on the parent container in case things were removed - // under a "menupopup" item. That is basically a wrapper for the contents - // of a "menu" node. - nsCOMPtr<nsIContent> parent = aContainer->GetParent(); - if (parent) { - obs = LookupContentChangeObserver(parent); - if (obs) - obs->ObserveContentRemoved(aDocument, aChild, aIndexInContainer); - } - } -} - - -void nsMenuGroupOwnerX::ContentInserted(nsIDocument * aDocument, - nsIContent * aContainer, - nsIContent * aChild, - int32_t /* unused */) -{ - if (!aContainer) { - return; - } - - nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); - nsChangeObserver* obs = LookupContentChangeObserver(aContainer); - if (obs) - obs->ObserveContentInserted(aDocument, aContainer, aChild); - else if (aContainer != mContent) { - // We do a lookup on the parent container in case things were removed - // under a "menupopup" item. That is basically a wrapper for the contents - // of a "menu" node. - nsCOMPtr<nsIContent> parent = aContainer->GetParent(); - if (parent) { - obs = LookupContentChangeObserver(parent); - if (obs) - obs->ObserveContentInserted(aDocument, aContainer, aChild); - } - } -} - - -void nsMenuGroupOwnerX::ParentChainChanged(nsIContent *aContent) -{ -} - - -// For change management, we don't use a |nsSupportsHashtable| because -// we know that the lifetime of all these items is bounded by the -// lifetime of the menubar. No need to add any more strong refs to the -// picture because the containment hierarchy already uses strong refs. -void nsMenuGroupOwnerX::RegisterForContentChanges(nsIContent *aContent, - nsChangeObserver *aMenuObject) -{ - if (!mContentToObserverTable.Contains(aContent)) { - aContent->AddMutationObserver(this); - } - mContentToObserverTable.Put(aContent, aMenuObject); -} - - -void nsMenuGroupOwnerX::UnregisterForContentChanges(nsIContent *aContent) -{ - if (mContentToObserverTable.Contains(aContent)) { - aContent->RemoveMutationObserver(this); - } - mContentToObserverTable.Remove(aContent); -} - - -nsChangeObserver* nsMenuGroupOwnerX::LookupContentChangeObserver(nsIContent* aContent) -{ - nsChangeObserver * result; - if (mContentToObserverTable.Get(aContent, &result)) - return result; - else - return nullptr; -} - - -// Given a menu item, creates a unique 4-character command ID and -// maps it to the item. Returns the id for use by the client. -uint32_t nsMenuGroupOwnerX::RegisterForCommand(nsMenuItemX* inMenuItem) -{ - // no real need to check for uniqueness. We always start afresh with each - // window at 1. Even if we did get close to the reserved Apple command id's, - // those don't start until at least ' ', which is integer 538976288. If - // we have that many menu items in one window, I think we have other - // problems. - - // make id unique - ++mCurrentCommandID; - - mCommandToMenuObjectTable.Put(mCurrentCommandID, inMenuItem); - - return mCurrentCommandID; -} - - -// Removes the mapping between the given 4-character command ID -// and its associated menu item. -void nsMenuGroupOwnerX::UnregisterCommand(uint32_t inCommandID) -{ - mCommandToMenuObjectTable.Remove(inCommandID); -} - - -nsMenuItemX* nsMenuGroupOwnerX::GetMenuItemForCommandID(uint32_t inCommandID) -{ - nsMenuItemX * result; - if (mCommandToMenuObjectTable.Get(inCommandID, &result)) - return result; - else - return nullptr; -} - -void nsMenuGroupOwnerX::AddMenuItemInfoToSet(MenuItemInfo* info) -{ - [mInfoSet addObject:info]; -} diff --git a/widget/cocoa/nsMenuItemIconX.h b/widget/cocoa/nsMenuItemIconX.h deleted file mode 100644 index 7352a94e2a..0000000000 --- a/widget/cocoa/nsMenuItemIconX.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- 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. - */ - -#ifndef nsMenuItemIconX_h_ -#define nsMenuItemIconX_h_ - -#include "mozilla/RefPtr.h" -#include "nsCOMPtr.h" -#include "imgINotificationObserver.h" - -class nsIURI; -class nsIContent; -class imgRequestProxy; -class nsMenuObjectX; - -#import <Cocoa/Cocoa.h> - -class nsMenuItemIconX : public imgINotificationObserver -{ -public: - nsMenuItemIconX(nsMenuObjectX* aMenuItem, - nsIContent* aContent, - NSMenuItem* aNativeMenuItem); -private: - virtual ~nsMenuItemIconX(); - -public: - NS_DECL_ISUPPORTS - NS_DECL_IMGINOTIFICATIONOBSERVER - - // SetupIcon succeeds if it was able to set up the icon, or if there should - // be no icon, in which case it clears any existing icon but still succeeds. - nsresult SetupIcon(); - - // GetIconURI fails if the item should not have any icon. - nsresult GetIconURI(nsIURI** aIconURI); - - // LoadIcon will set a placeholder image and start a load request for the - // icon. The request may not complete until after LoadIcon returns. - nsresult LoadIcon(nsIURI* aIconURI); - - // Unless we take precautions, we may outlive the object that created us - // (mMenuObject, which owns our native menu item (mNativeMenuItem)). - // Destroy() should be called from mMenuObject's destructor to prevent - // this from happening. See bug 499600. - void Destroy(); - -protected: - nsresult OnFrameComplete(imgIRequest* aRequest); - - nsCOMPtr<nsIContent> mContent; - RefPtr<imgRequestProxy> mIconRequest; - nsMenuObjectX* mMenuObject; // [weak] - nsIntRect mImageRegionRect; - bool mLoadedIcon; - bool mSetIcon; - NSMenuItem* mNativeMenuItem; // [weak] -}; - -#endif // nsMenuItemIconX_h_ diff --git a/widget/cocoa/nsMenuItemIconX.mm b/widget/cocoa/nsMenuItemIconX.mm deleted file mode 100644 index 7589c279e5..0000000000 --- a/widget/cocoa/nsMenuItemIconX.mm +++ /dev/null @@ -1,466 +0,0 @@ -/* -*- 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 "nsMenuItemIconX.h" -#include "nsObjCExceptions.h" -#include "nsIContent.h" -#include "nsIDocument.h" -#include "nsNameSpaceManager.h" -#include "nsGkAtoms.h" -#include "nsIDOMElement.h" -#include "nsICSSDeclaration.h" -#include "nsIDOMCSSValue.h" -#include "nsIDOMCSSPrimitiveValue.h" -#include "nsIDOMRect.h" -#include "nsThreadUtils.h" -#include "nsToolkit.h" -#include "nsNetUtil.h" -#include "imgLoader.h" -#include "imgRequestProxy.h" -#include "nsMenuItemX.h" -#include "gfxPlatform.h" -#include "imgIContainer.h" -#include "nsCocoaUtils.h" -#include "nsContentUtils.h" -#include "nsIContentPolicy.h" - -using mozilla::dom::Element; -using mozilla::gfx::SourceSurface; - -static const uint32_t kIconWidth = 16; -static const uint32_t kIconHeight = 16; - -typedef NS_STDCALL_FUNCPROTO(nsresult, GetRectSideMethod, nsIDOMRect, - GetBottom, (nsIDOMCSSPrimitiveValue**)); - -NS_IMPL_ISUPPORTS(nsMenuItemIconX, imgINotificationObserver) - -nsMenuItemIconX::nsMenuItemIconX(nsMenuObjectX* aMenuItem, - nsIContent* aContent, - NSMenuItem* aNativeMenuItem) -: mContent(aContent) -, mMenuObject(aMenuItem) -, mLoadedIcon(false) -, mSetIcon(false) -, mNativeMenuItem(aNativeMenuItem) -{ - // printf("Creating icon for menu item %d, menu %d, native item is %d\n", aMenuItem, aMenu, aNativeMenuItem); -} - -nsMenuItemIconX::~nsMenuItemIconX() -{ - if (mIconRequest) - mIconRequest->CancelAndForgetObserver(NS_BINDING_ABORTED); -} - -// Called from mMenuObjectX's destructor, to prevent us from outliving it -// (as might otherwise happen if calls to our imgINotificationObserver methods -// are still outstanding). mMenuObjectX owns our nNativeMenuItem. -void nsMenuItemIconX::Destroy() -{ - if (mIconRequest) { - mIconRequest->CancelAndForgetObserver(NS_BINDING_ABORTED); - mIconRequest = nullptr; - } - mMenuObject = nullptr; - mNativeMenuItem = nil; -} - -nsresult -nsMenuItemIconX::SetupIcon() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // Still don't have one, then something is wrong, get out of here. - if (!mNativeMenuItem) { - NS_ERROR("No native menu item"); - return NS_ERROR_FAILURE; - } - - nsCOMPtr<nsIURI> iconURI; - nsresult rv = GetIconURI(getter_AddRefs(iconURI)); - if (NS_FAILED(rv)) { - // There is no icon for this menu item. An icon might have been set - // earlier. Clear it. - [mNativeMenuItem setImage:nil]; - - return NS_OK; - } - - rv = LoadIcon(iconURI); - if (NS_FAILED(rv)) { - // There is no icon for this menu item, as an error occurred while loading it. - // An icon might have been set earlier or the place holder icon may have - // been set. Clear it. - [mNativeMenuItem setImage:nil]; - } - return rv; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -static int32_t -GetDOMRectSide(nsIDOMRect* aRect, GetRectSideMethod aMethod) -{ - nsCOMPtr<nsIDOMCSSPrimitiveValue> dimensionValue; - (aRect->*aMethod)(getter_AddRefs(dimensionValue)); - if (!dimensionValue) - return -1; - - uint16_t primitiveType; - nsresult rv = dimensionValue->GetPrimitiveType(&primitiveType); - if (NS_FAILED(rv) || primitiveType != nsIDOMCSSPrimitiveValue::CSS_PX) - return -1; - - float dimension = 0; - rv = dimensionValue->GetFloatValue(nsIDOMCSSPrimitiveValue::CSS_PX, - &dimension); - if (NS_FAILED(rv)) - return -1; - - return NSToIntRound(dimension); -} - -nsresult -nsMenuItemIconX::GetIconURI(nsIURI** aIconURI) -{ - if (!mMenuObject) - return NS_ERROR_FAILURE; - - // Mac native menu items support having both a checkmark and an icon - // simultaneously, but this is unheard of in the cross-platform toolkit, - // seemingly because the win32 theme is unable to cope with both at once. - // The downside is that it's possible to get a menu item marked with a - // native checkmark and a checkmark for an icon. Head off that possibility - // by pretending that no icon exists if this is a checkable menu item. - if (mMenuObject->MenuObjectType() == eMenuItemObjectType) { - nsMenuItemX* menuItem = static_cast<nsMenuItemX*>(mMenuObject); - if (menuItem->GetMenuItemType() != eRegularMenuItemType) - return NS_ERROR_FAILURE; - } - - if (!mContent) - return NS_ERROR_FAILURE; - - // First, look at the content node's "image" attribute. - nsAutoString imageURIString; - bool hasImageAttr = mContent->GetAttr(kNameSpaceID_None, - nsGkAtoms::image, - imageURIString); - - nsresult rv; - nsCOMPtr<nsIDOMCSSValue> cssValue; - nsCOMPtr<nsICSSDeclaration> cssStyleDecl; - nsCOMPtr<nsIDOMCSSPrimitiveValue> primitiveValue; - uint16_t primitiveType; - if (!hasImageAttr) { - // If the content node has no "image" attribute, get the - // "list-style-image" property from CSS. - nsCOMPtr<nsIDocument> document = mContent->GetComposedDoc(); - if (!document) - return NS_ERROR_FAILURE; - - nsCOMPtr<nsPIDOMWindowInner> window = document->GetInnerWindow(); - if (!window) - return NS_ERROR_FAILURE; - - nsCOMPtr<Element> domElement = do_QueryInterface(mContent); - if (!domElement) - return NS_ERROR_FAILURE; - - ErrorResult dummy; - cssStyleDecl = window->GetComputedStyle(*domElement, EmptyString(), dummy); - dummy.SuppressException(); - if (!cssStyleDecl) - return NS_ERROR_FAILURE; - - NS_NAMED_LITERAL_STRING(listStyleImage, "list-style-image"); - rv = cssStyleDecl->GetPropertyCSSValue(listStyleImage, - getter_AddRefs(cssValue)); - if (NS_FAILED(rv)) return rv; - - primitiveValue = do_QueryInterface(cssValue); - if (!primitiveValue) return NS_ERROR_FAILURE; - - rv = primitiveValue->GetPrimitiveType(&primitiveType); - if (NS_FAILED(rv)) return rv; - if (primitiveType != nsIDOMCSSPrimitiveValue::CSS_URI) - return NS_ERROR_FAILURE; - - rv = primitiveValue->GetStringValue(imageURIString); - if (NS_FAILED(rv)) return rv; - } - - // Empty the mImageRegionRect initially as the image region CSS could - // have been changed and now have an error or have been removed since the - // last GetIconURI call. - mImageRegionRect.SetEmpty(); - - // If this menu item shouldn't have an icon, the string will be empty, - // and NS_NewURI will fail. - nsCOMPtr<nsIURI> iconURI; - rv = NS_NewURI(getter_AddRefs(iconURI), imageURIString); - if (NS_FAILED(rv)) return rv; - - *aIconURI = iconURI; - NS_ADDREF(*aIconURI); - - if (!hasImageAttr) { - // Check if the icon has a specified image region so that it can be - // cropped appropriately before being displayed. - NS_NAMED_LITERAL_STRING(imageRegion, "-moz-image-region"); - rv = cssStyleDecl->GetPropertyCSSValue(imageRegion, - getter_AddRefs(cssValue)); - // Just return NS_OK if there if there is a failure due to no - // moz-image region specified so the whole icon will be drawn anyway. - if (NS_FAILED(rv)) return NS_OK; - - primitiveValue = do_QueryInterface(cssValue); - if (!primitiveValue) return NS_OK; - - rv = primitiveValue->GetPrimitiveType(&primitiveType); - if (NS_FAILED(rv)) return NS_OK; - if (primitiveType != nsIDOMCSSPrimitiveValue::CSS_RECT) - return NS_OK; - - nsCOMPtr<nsIDOMRect> imageRegionRect; - rv = primitiveValue->GetRectValue(getter_AddRefs(imageRegionRect)); - if (NS_FAILED(rv)) return NS_OK; - - if (imageRegionRect) { - // Return NS_ERROR_FAILURE if the image region is invalid so the image - // is not drawn, and behavior is similar to XUL menus. - int32_t bottom = GetDOMRectSide(imageRegionRect, &nsIDOMRect::GetBottom); - int32_t right = GetDOMRectSide(imageRegionRect, &nsIDOMRect::GetRight); - int32_t top = GetDOMRectSide(imageRegionRect, &nsIDOMRect::GetTop); - int32_t left = GetDOMRectSide(imageRegionRect, &nsIDOMRect::GetLeft); - - if (top < 0 || left < 0 || bottom <= top || right <= left) - return NS_ERROR_FAILURE; - - mImageRegionRect.SetRect(left, top, right - left, bottom - top); - } - } - - return NS_OK; -} - -nsresult -nsMenuItemIconX::LoadIcon(nsIURI* aIconURI) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (mIconRequest) { - // Another icon request is already in flight. Kill it. - mIconRequest->Cancel(NS_BINDING_ABORTED); - mIconRequest = nullptr; - } - - mLoadedIcon = false; - - if (!mContent) return NS_ERROR_FAILURE; - - nsCOMPtr<nsIDocument> document = mContent->OwnerDoc(); - - nsCOMPtr<nsILoadGroup> loadGroup = document->GetDocumentLoadGroup(); - if (!loadGroup) return NS_ERROR_FAILURE; - - RefPtr<imgLoader> loader = nsContentUtils::GetImgLoaderForDocument(document); - if (!loader) return NS_ERROR_FAILURE; - - if (!mSetIcon) { - // Set a completely transparent 16x16 image as the icon on this menu item - // as a placeholder. This keeps the menu item text displayed in the same - // position that it will be displayed when the real icon is loaded, and - // prevents it from jumping around or looking misaligned. - - static bool sInitializedPlaceholder; - static NSImage* sPlaceholderIconImage; - if (!sInitializedPlaceholder) { - sInitializedPlaceholder = true; - - // Note that we only create the one and reuse it forever, so this is not a leak. - sPlaceholderIconImage = [[NSImage alloc] initWithSize:NSMakeSize(kIconWidth, kIconHeight)]; - } - - if (!sPlaceholderIconImage) return NS_ERROR_FAILURE; - - if (mNativeMenuItem) - [mNativeMenuItem setImage:sPlaceholderIconImage]; - } - - nsresult rv = loader->LoadImage(aIconURI, nullptr, nullptr, - mozilla::net::RP_Default, - nullptr, loadGroup, this, - nullptr, nullptr, nsIRequest::LOAD_NORMAL, nullptr, - nsIContentPolicy::TYPE_INTERNAL_IMAGE, EmptyString(), - getter_AddRefs(mIconRequest)); - if (NS_FAILED(rv)) return rv; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// -// imgINotificationObserver -// - -NS_IMETHODIMP -nsMenuItemIconX::Notify(imgIRequest* aRequest, - int32_t aType, - const nsIntRect* aData) -{ - if (aType == imgINotificationObserver::LOAD_COMPLETE) { - // Make sure the image loaded successfully. - uint32_t status = imgIRequest::STATUS_ERROR; - if (NS_FAILED(aRequest->GetImageStatus(&status)) || - (status & imgIRequest::STATUS_ERROR)) { - mIconRequest->Cancel(NS_BINDING_ABORTED); - mIconRequest = nullptr; - return NS_ERROR_FAILURE; - } - - nsCOMPtr<imgIContainer> image; - aRequest->GetImage(getter_AddRefs(image)); - MOZ_ASSERT(image); - - // Ask the image to decode at its intrinsic size. - int32_t width = 0, height = 0; - image->GetWidth(&width); - image->GetHeight(&height); - image->RequestDecodeForSize(nsIntSize(width, height), imgIContainer::FLAG_NONE); - } - - if (aType == imgINotificationObserver::FRAME_COMPLETE) { - return OnFrameComplete(aRequest); - } - - if (aType == imgINotificationObserver::DECODE_COMPLETE) { - if (mIconRequest && mIconRequest == aRequest) { - mIconRequest->Cancel(NS_BINDING_ABORTED); - mIconRequest = nullptr; - } - } - - return NS_OK; -} - -nsresult -nsMenuItemIconX::OnFrameComplete(imgIRequest* aRequest) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (aRequest != mIconRequest) - return NS_ERROR_FAILURE; - - // Only support one frame. - if (mLoadedIcon) - return NS_OK; - - if (!mNativeMenuItem) - return NS_ERROR_FAILURE; - - nsCOMPtr<imgIContainer> imageContainer; - aRequest->GetImage(getter_AddRefs(imageContainer)); - if (!imageContainer) { - [mNativeMenuItem setImage:nil]; - return NS_ERROR_FAILURE; - } - - int32_t origWidth = 0, origHeight = 0; - imageContainer->GetWidth(&origWidth); - imageContainer->GetHeight(&origHeight); - - // If the image region is invalid, don't draw the image to almost match - // the behavior of other platforms. - if (!mImageRegionRect.IsEmpty() && - (mImageRegionRect.XMost() > origWidth || - mImageRegionRect.YMost() > origHeight)) { - [mNativeMenuItem setImage:nil]; - return NS_ERROR_FAILURE; - } - - if (mImageRegionRect.IsEmpty()) { - mImageRegionRect.SetRect(0, 0, origWidth, origHeight); - } - - RefPtr<SourceSurface> surface = - imageContainer->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_SYNC_DECODE); - if (!surface) { - [mNativeMenuItem setImage:nil]; - return NS_ERROR_FAILURE; - } - - CGImageRef origImage = NULL; - nsresult rv = nsCocoaUtils::CreateCGImageFromSurface(surface, &origImage); - if (NS_FAILED(rv) || !origImage) { - [mNativeMenuItem setImage:nil]; - return NS_ERROR_FAILURE; - } - - bool createSubImage = !(mImageRegionRect.x == 0 && mImageRegionRect.y == 0 && - mImageRegionRect.width == origWidth && mImageRegionRect.height == origHeight); - - CGImageRef finalImage = origImage; - if (createSubImage) { - // if mImageRegionRect is set using CSS, we need to slice a piece out of the overall - // image to use as the icon - finalImage = ::CGImageCreateWithImageInRect(origImage, - ::CGRectMake(mImageRegionRect.x, - mImageRegionRect.y, - mImageRegionRect.width, - mImageRegionRect.height)); - ::CGImageRelease(origImage); - if (!finalImage) { - [mNativeMenuItem setImage:nil]; - return NS_ERROR_FAILURE; - } - } - - NSImage *newImage = nil; - rv = nsCocoaUtils::CreateNSImageFromCGImage(finalImage, &newImage); - if (NS_FAILED(rv) || !newImage) { - [mNativeMenuItem setImage:nil]; - ::CGImageRelease(finalImage); - return NS_ERROR_FAILURE; - } - - [newImage setSize:NSMakeSize(kIconWidth, kIconHeight)]; - [mNativeMenuItem setImage:newImage]; - - [newImage release]; - ::CGImageRelease(finalImage); - - mLoadedIcon = true; - mSetIcon = true; - - if (mMenuObject) { - mMenuObject->IconUpdated(); - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} diff --git a/widget/cocoa/nsMenuItemX.h b/widget/cocoa/nsMenuItemX.h deleted file mode 100644 index 67ae32c99c..0000000000 --- a/widget/cocoa/nsMenuItemX.h +++ /dev/null @@ -1,75 +0,0 @@ -/* -*- 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 nsMenuItemX_h_ -#define nsMenuItemX_h_ - -#include "mozilla/RefPtr.h" -#include "nsMenuBaseX.h" -#include "nsMenuGroupOwnerX.h" -#include "nsChangeObserver.h" - -#import <Cocoa/Cocoa.h> - -class nsString; -class nsMenuItemIconX; -class nsMenuX; - -enum { - knsMenuItemNoModifier = 0, - knsMenuItemShiftModifier = (1 << 0), - knsMenuItemAltModifier = (1 << 1), - knsMenuItemControlModifier = (1 << 2), - knsMenuItemCommandModifier = (1 << 3) -}; - -enum EMenuItemType { - eRegularMenuItemType = 0, - eCheckboxMenuItemType, - eRadioMenuItemType, - eSeparatorMenuItemType -}; - - -// Once instantiated, this object lives until its DOM node or its parent window is destroyed. -// Do not hold references to this, they can become invalid any time the DOM node can be destroyed. -class nsMenuItemX : public nsMenuObjectX, - public nsChangeObserver -{ -public: - nsMenuItemX(); - virtual ~nsMenuItemX(); - - NS_DECL_CHANGEOBSERVER - - // nsMenuObjectX - void* NativeData() override {return (void*)mNativeMenuItem;} - nsMenuObjectTypeX MenuObjectType() override {return eMenuItemObjectType;} - - // nsMenuItemX - nsresult Create(nsMenuX* aParent, const nsString& aLabel, EMenuItemType aItemType, - nsMenuGroupOwnerX* aMenuGroupOwner, nsIContent* aNode); - nsresult SetChecked(bool aIsChecked); - EMenuItemType GetMenuItemType(); - void DoCommand(); - nsresult DispatchDOMEvent(const nsString &eventName, bool* preventDefaultCalled); - void SetupIcon(); - -protected: - void UncheckRadioSiblings(nsIContent* inCheckedElement); - void SetKeyEquiv(); - - EMenuItemType mType; - // nsMenuItemX objects should always have a valid native menu item. - NSMenuItem* mNativeMenuItem; // [strong] - nsMenuX* mMenuParent; // [weak] - nsMenuGroupOwnerX* mMenuGroupOwner; // [weak] - nsCOMPtr<nsIContent> mCommandContent; - // The icon object should never outlive its creating nsMenuItemX object. - RefPtr<nsMenuItemIconX> mIcon; - bool mIsChecked; -}; - -#endif // nsMenuItemX_h_ diff --git a/widget/cocoa/nsMenuItemX.mm b/widget/cocoa/nsMenuItemX.mm deleted file mode 100644 index 114b69f430..0000000000 --- a/widget/cocoa/nsMenuItemX.mm +++ /dev/null @@ -1,369 +0,0 @@ -/* -*- 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/. */ - -#include "nsMenuItemX.h" -#include "nsMenuBarX.h" -#include "nsMenuX.h" -#include "nsMenuItemIconX.h" -#include "nsMenuUtilsX.h" -#include "nsCocoaUtils.h" - -#include "nsObjCExceptions.h" - -#include "nsCOMPtr.h" -#include "nsGkAtoms.h" - -#include "mozilla/dom/Element.h" -#include "nsIWidget.h" -#include "nsIDocument.h" -#include "nsIDOMDocument.h" -#include "nsIDOMElement.h" -#include "nsIDOMEvent.h" - -nsMenuItemX::nsMenuItemX() -{ - mType = eRegularMenuItemType; - mNativeMenuItem = nil; - mMenuParent = nullptr; - mMenuGroupOwner = nullptr; - mIsChecked = false; - - MOZ_COUNT_CTOR(nsMenuItemX); -} - -nsMenuItemX::~nsMenuItemX() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Prevent the icon object from outliving us. - if (mIcon) - mIcon->Destroy(); - - // autorelease the native menu item so that anything else happening to this - // object happens before the native menu item actually dies - [mNativeMenuItem autorelease]; - - if (mContent) - mMenuGroupOwner->UnregisterForContentChanges(mContent); - if (mCommandContent) - mMenuGroupOwner->UnregisterForContentChanges(mCommandContent); - - MOZ_COUNT_DTOR(nsMenuItemX); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -nsresult nsMenuItemX::Create(nsMenuX* aParent, const nsString& aLabel, EMenuItemType aItemType, - nsMenuGroupOwnerX* aMenuGroupOwner, nsIContent* aNode) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - mType = aItemType; - mMenuParent = aParent; - mContent = aNode; - - mMenuGroupOwner = aMenuGroupOwner; - NS_ASSERTION(mMenuGroupOwner, "No menu owner given, must have one!"); - - mMenuGroupOwner->RegisterForContentChanges(mContent, this); - - nsIDocument *doc = mContent->GetUncomposedDoc(); - - // if we have a command associated with this menu item, register for changes - // to the command DOM node - if (doc) { - nsAutoString ourCommand; - mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::command, ourCommand); - - if (!ourCommand.IsEmpty()) { - nsIContent *commandElement = doc->GetElementById(ourCommand); - - if (commandElement) { - mCommandContent = commandElement; - // register to observe the command DOM element - mMenuGroupOwner->RegisterForContentChanges(mCommandContent, this); - } - } - } - - // decide enabled state based on command content if it exists, otherwise do it based - // on our own content - bool isEnabled; - if (mCommandContent) - isEnabled = !mCommandContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, nsGkAtoms::_true, eCaseMatters); - else - isEnabled = !mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, nsGkAtoms::_true, eCaseMatters); - - // set up the native menu item - if (mType == eSeparatorMenuItemType) { - mNativeMenuItem = [[NSMenuItem separatorItem] retain]; - } - else { - NSString *newCocoaLabelString = nsMenuUtilsX::GetTruncatedCocoaLabel(aLabel); - mNativeMenuItem = [[NSMenuItem alloc] initWithTitle:newCocoaLabelString action:nil keyEquivalent:@""]; - - [mNativeMenuItem setEnabled:(BOOL)isEnabled]; - - SetChecked(mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked, - nsGkAtoms::_true, eCaseMatters)); - SetKeyEquiv(); - } - - mIcon = new nsMenuItemIconX(this, mContent, mNativeMenuItem); - if (!mIcon) - return NS_ERROR_OUT_OF_MEMORY; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -nsresult nsMenuItemX::SetChecked(bool aIsChecked) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - mIsChecked = aIsChecked; - - // update the content model. This will also handle unchecking our siblings - // if we are a radiomenu - mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::checked, - mIsChecked ? NS_LITERAL_STRING("true") : NS_LITERAL_STRING("false"), true); - - // update native menu item - if (mIsChecked) - [mNativeMenuItem setState:NSOnState]; - else - [mNativeMenuItem setState:NSOffState]; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -EMenuItemType nsMenuItemX::GetMenuItemType() -{ - return mType; -} - -// Executes the "cached" javaScript command. -// Returns NS_OK if the command was executed properly, otherwise an error code. -void nsMenuItemX::DoCommand() -{ - // flip "checked" state if we're a checkbox menu, or an un-checked radio menu - if (mType == eCheckboxMenuItemType || - (mType == eRadioMenuItemType && !mIsChecked)) { - if (!mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::autocheck, - nsGkAtoms::_false, eCaseMatters)) - SetChecked(!mIsChecked); - /* the AttributeChanged code will update all the internal state */ - } - - nsMenuUtilsX::DispatchCommandTo(mContent); -} - -nsresult nsMenuItemX::DispatchDOMEvent(const nsString &eventName, bool *preventDefaultCalled) -{ - if (!mContent) - return NS_ERROR_FAILURE; - - // get owner document for content - nsCOMPtr<nsIDocument> parentDoc = mContent->OwnerDoc(); - - // get interface for creating DOM events from content owner document - nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(parentDoc); - if (!domDoc) { - NS_WARNING("Failed to QI parent nsIDocument to nsIDOMDocument"); - return NS_ERROR_FAILURE; - } - - // create DOM event - nsCOMPtr<nsIDOMEvent> event; - nsresult rv = domDoc->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event)); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to create nsIDOMEvent"); - return rv; - } - event->InitEvent(eventName, true, true); - - // mark DOM event as trusted - event->SetTrusted(true); - - // send DOM event - rv = mContent->DispatchEvent(event, preventDefaultCalled); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to send DOM event via EventTarget"); - return rv; - } - - return NS_OK; -} - -// Walk the sibling list looking for nodes with the same name and -// uncheck them all. -void nsMenuItemX::UncheckRadioSiblings(nsIContent* inCheckedContent) -{ - nsAutoString myGroupName; - inCheckedContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, myGroupName); - if (!myGroupName.Length()) // no groupname, nothing to do - return; - - nsCOMPtr<nsIContent> parent = inCheckedContent->GetParent(); - if (!parent) - return; - - // loop over siblings - uint32_t count = parent->GetChildCount(); - for (uint32_t i = 0; i < count; i++) { - nsIContent *sibling = parent->GetChildAt(i); - if (sibling) { - if (sibling != inCheckedContent) { // skip this node - // if the current sibling is in the same group, clear it - if (sibling->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, - myGroupName, eCaseMatters)) - sibling->SetAttr(kNameSpaceID_None, nsGkAtoms::checked, NS_LITERAL_STRING("false"), true); - } - } - } -} - -void nsMenuItemX::SetKeyEquiv() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Set key shortcut and modifiers - nsAutoString keyValue; - mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyValue); - if (!keyValue.IsEmpty() && mContent->GetUncomposedDoc()) { - nsIContent *keyContent = mContent->GetUncomposedDoc()->GetElementById(keyValue); - if (keyContent) { - nsAutoString keyChar; - bool hasKey = keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyChar); - - if (!hasKey || keyChar.IsEmpty()) { - nsAutoString keyCodeName; - keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, keyCodeName); - uint32_t charCode = - nsCocoaUtils::ConvertGeckoNameToMacCharCode(keyCodeName); - if (charCode) { - keyChar.Assign(charCode); - } - else { - keyChar.Assign(NS_LITERAL_STRING(" ")); - } - } - - nsAutoString modifiersStr; - keyContent->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiersStr); - uint8_t modifiers = nsMenuUtilsX::GeckoModifiersForNodeAttribute(modifiersStr); - - unsigned int macModifiers = nsMenuUtilsX::MacModifiersForGeckoModifiers(modifiers); - [mNativeMenuItem setKeyEquivalentModifierMask:macModifiers]; - - NSString *keyEquivalent = [[NSString stringWithCharacters:(unichar*)keyChar.get() - length:keyChar.Length()] lowercaseString]; - if ([keyEquivalent isEqualToString:@" "]) - [mNativeMenuItem setKeyEquivalent:@""]; - else - [mNativeMenuItem setKeyEquivalent:keyEquivalent]; - - return; - } - } - - // if the key was removed, clear the key - [mNativeMenuItem setKeyEquivalent:@""]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// -// nsChangeObserver -// - -void -nsMenuItemX::ObserveAttributeChanged(nsIDocument *aDocument, nsIContent *aContent, nsIAtom *aAttribute) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!aContent) - return; - - if (aContent == mContent) { // our own content node changed - if (aAttribute == nsGkAtoms::checked) { - // if we're a radio menu, uncheck our sibling radio items. No need to - // do any of this if we're just a normal check menu. - if (mType == eRadioMenuItemType) { - if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked, - nsGkAtoms::_true, eCaseMatters)) - UncheckRadioSiblings(mContent); - } - mMenuParent->SetRebuild(true); - } - else if (aAttribute == nsGkAtoms::hidden || - aAttribute == nsGkAtoms::collapsed || - aAttribute == nsGkAtoms::label) { - mMenuParent->SetRebuild(true); - } - else if (aAttribute == nsGkAtoms::key) { - SetKeyEquiv(); - } - else if (aAttribute == nsGkAtoms::image) { - SetupIcon(); - } - else if (aAttribute == nsGkAtoms::disabled) { - if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, nsGkAtoms::_true, eCaseMatters)) - [mNativeMenuItem setEnabled:NO]; - else - [mNativeMenuItem setEnabled:YES]; - } - } - else if (aContent == mCommandContent) { - // the only thing that really matters when the menu isn't showing is the - // enabled state since it enables/disables keyboard commands - if (aAttribute == nsGkAtoms::disabled) { - // first we sync our menu item DOM node with the command DOM node - nsAutoString commandDisabled; - nsAutoString menuDisabled; - aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::disabled, commandDisabled); - mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::disabled, menuDisabled); - if (!commandDisabled.Equals(menuDisabled)) { - // The menu's disabled state needs to be updated to match the command. - if (commandDisabled.IsEmpty()) - mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true); - else - mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, commandDisabled, true); - } - // now we sync our native menu item with the command DOM node - if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, nsGkAtoms::_true, eCaseMatters)) - [mNativeMenuItem setEnabled:NO]; - else - [mNativeMenuItem setEnabled:YES]; - } - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void nsMenuItemX::ObserveContentRemoved(nsIDocument *aDocument, nsIContent *aChild, int32_t aIndexInContainer) -{ - if (aChild == mCommandContent) { - mMenuGroupOwner->UnregisterForContentChanges(mCommandContent); - mCommandContent = nullptr; - } - - mMenuParent->SetRebuild(true); -} - -void nsMenuItemX::ObserveContentInserted(nsIDocument *aDocument, nsIContent* aContainer, - nsIContent *aChild) -{ - mMenuParent->SetRebuild(true); -} - -void nsMenuItemX::SetupIcon() -{ - if (mIcon) - mIcon->SetupIcon(); -} diff --git a/widget/cocoa/nsMenuUtilsX.h b/widget/cocoa/nsMenuUtilsX.h deleted file mode 100644 index 1571cdfb0a..0000000000 --- a/widget/cocoa/nsMenuUtilsX.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- 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 nsMenuUtilsX_h_ -#define nsMenuUtilsX_h_ - -#include "nscore.h" -#include "nsMenuBaseX.h" - -#import <Cocoa/Cocoa.h> - -class nsIContent; -class nsString; -class nsMenuBarX; - -// Namespace containing utility functions used in our native menu implementation. -namespace nsMenuUtilsX -{ - void DispatchCommandTo(nsIContent* aTargetContent); - NSString* GetTruncatedCocoaLabel(const nsString& itemLabel); - uint8_t GeckoModifiersForNodeAttribute(const nsString& modifiersAttribute); - unsigned int MacModifiersForGeckoModifiers(uint8_t geckoModifiers); - nsMenuBarX* GetHiddenWindowMenuBar(); // returned object is not retained - NSMenuItem* GetStandardEditMenuItem(); // returned object is not retained - bool NodeIsHiddenOrCollapsed(nsIContent* inContent); - int CalculateNativeInsertionPoint(nsMenuObjectX* aParent, nsMenuObjectX* aChild); -} // namespace nsMenuUtilsX - -#endif // nsMenuUtilsX_h_ diff --git a/widget/cocoa/nsMenuUtilsX.mm b/widget/cocoa/nsMenuUtilsX.mm deleted file mode 100644 index db64717127..0000000000 --- a/widget/cocoa/nsMenuUtilsX.mm +++ /dev/null @@ -1,223 +0,0 @@ -/* -*- 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/. */ - -#include "mozilla/dom/Event.h" -#include "nsMenuUtilsX.h" -#include "nsMenuBarX.h" -#include "nsMenuX.h" -#include "nsMenuItemX.h" -#include "nsStandaloneNativeMenu.h" -#include "nsObjCExceptions.h" -#include "nsCocoaUtils.h" -#include "nsCocoaWindow.h" -#include "nsGkAtoms.h" -#include "nsIDocument.h" -#include "nsIDOMDocument.h" -#include "nsIDOMXULCommandEvent.h" -#include "nsPIDOMWindow.h" -#include "nsQueryObject.h" - -using namespace mozilla; - -void nsMenuUtilsX::DispatchCommandTo(nsIContent* aTargetContent) -{ - NS_PRECONDITION(aTargetContent, "null ptr"); - - nsIDocument* doc = aTargetContent->OwnerDoc(); - if (doc) { - ErrorResult rv; - RefPtr<dom::Event> event = - doc->CreateEvent(NS_LITERAL_STRING("xulcommandevent"), rv); - nsCOMPtr<nsIDOMXULCommandEvent> command = do_QueryObject(event); - - // FIXME: Should probably figure out how to init this with the actual - // pressed keys, but this is a big old edge case anyway. -dwh - if (command && - NS_SUCCEEDED(command->InitCommandEvent(NS_LITERAL_STRING("command"), - true, true, - doc->GetInnerWindow(), 0, - false, false, false, - false, nullptr))) { - event->SetTrusted(true); - bool dummy; - aTargetContent->DispatchEvent(event, &dummy); - } - } -} - -NSString* nsMenuUtilsX::GetTruncatedCocoaLabel(const nsString& itemLabel) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - // We want to truncate long strings to some reasonable pixel length but there is no - // good API for doing that which works for all OS versions and architectures. For now - // we'll do nothing for consistency and depend on good user interface design to limit - // string lengths. - return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(itemLabel.get()) - length:itemLabel.Length()]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -uint8_t nsMenuUtilsX::GeckoModifiersForNodeAttribute(const nsString& modifiersAttribute) -{ - uint8_t modifiers = knsMenuItemNoModifier; - char* str = ToNewCString(modifiersAttribute); - char* newStr; - char* token = strtok_r(str, ", \t", &newStr); - while (token != NULL) { - if (strcmp(token, "shift") == 0) - modifiers |= knsMenuItemShiftModifier; - else if (strcmp(token, "alt") == 0) - modifiers |= knsMenuItemAltModifier; - else if (strcmp(token, "control") == 0) - modifiers |= knsMenuItemControlModifier; - else if ((strcmp(token, "accel") == 0) || - (strcmp(token, "meta") == 0)) { - modifiers |= knsMenuItemCommandModifier; - } - token = strtok_r(newStr, ", \t", &newStr); - } - free(str); - - return modifiers; -} - -unsigned int nsMenuUtilsX::MacModifiersForGeckoModifiers(uint8_t geckoModifiers) -{ - unsigned int macModifiers = 0; - - if (geckoModifiers & knsMenuItemShiftModifier) - macModifiers |= NSShiftKeyMask; - if (geckoModifiers & knsMenuItemAltModifier) - macModifiers |= NSAlternateKeyMask; - if (geckoModifiers & knsMenuItemControlModifier) - macModifiers |= NSControlKeyMask; - if (geckoModifiers & knsMenuItemCommandModifier) - macModifiers |= NSCommandKeyMask; - - return macModifiers; -} - -nsMenuBarX* nsMenuUtilsX::GetHiddenWindowMenuBar() -{ - nsIWidget* hiddenWindowWidgetNoCOMPtr = nsCocoaUtils::GetHiddenWindowWidget(); - if (hiddenWindowWidgetNoCOMPtr) - return static_cast<nsCocoaWindow*>(hiddenWindowWidgetNoCOMPtr)->GetMenuBar(); - else - return nullptr; -} - -// It would be nice if we could localize these edit menu names. -NSMenuItem* nsMenuUtilsX::GetStandardEditMenuItem() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - // In principle we should be able to allocate this once and then always - // return the same object. But weird interactions happen between native - // app-modal dialogs and Gecko-modal dialogs that open above them. So what - // we return here isn't always released before it needs to be added to - // another menu. See bmo bug 468393. - NSMenuItem* standardEditMenuItem = - [[[NSMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:@""] autorelease]; - NSMenu* standardEditMenu = [[NSMenu alloc] initWithTitle:@"Edit"]; - [standardEditMenuItem setSubmenu:standardEditMenu]; - [standardEditMenu release]; - - // Add Undo - NSMenuItem* undoItem = [[NSMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:@"z"]; - [standardEditMenu addItem:undoItem]; - [undoItem release]; - - // Add Redo - NSMenuItem* redoItem = [[NSMenuItem alloc] initWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:@"Z"]; - [standardEditMenu addItem:redoItem]; - [redoItem release]; - - // Add separator - [standardEditMenu addItem:[NSMenuItem separatorItem]]; - - // Add Cut - NSMenuItem* cutItem = [[NSMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"]; - [standardEditMenu addItem:cutItem]; - [cutItem release]; - - // Add Copy - NSMenuItem* copyItem = [[NSMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"]; - [standardEditMenu addItem:copyItem]; - [copyItem release]; - - // Add Paste - NSMenuItem* pasteItem = [[NSMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"]; - [standardEditMenu addItem:pasteItem]; - [pasteItem release]; - - // Add Delete - NSMenuItem* deleteItem = [[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(delete:) keyEquivalent:@""]; - [standardEditMenu addItem:deleteItem]; - [deleteItem release]; - - // Add Select All - NSMenuItem* selectAllItem = [[NSMenuItem alloc] initWithTitle:@"Select All" action:@selector(selectAll:) keyEquivalent:@"a"]; - [standardEditMenu addItem:selectAllItem]; - [selectAllItem release]; - - return standardEditMenuItem; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -bool nsMenuUtilsX::NodeIsHiddenOrCollapsed(nsIContent* inContent) -{ - return (inContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden, - nsGkAtoms::_true, eCaseMatters) || - inContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::collapsed, - nsGkAtoms::_true, eCaseMatters)); -} - -// Determines how many items are visible among the siblings in a menu that are -// before the given child. This will not count the application menu. -int nsMenuUtilsX::CalculateNativeInsertionPoint(nsMenuObjectX* aParent, - nsMenuObjectX* aChild) -{ - int insertionPoint = 0; - nsMenuObjectTypeX parentType = aParent->MenuObjectType(); - if (parentType == eMenuBarObjectType) { - nsMenuBarX* menubarParent = static_cast<nsMenuBarX*>(aParent); - uint32_t numMenus = menubarParent->GetMenuCount(); - for (uint32_t i = 0; i < numMenus; i++) { - nsMenuX* currMenu = menubarParent->GetMenuAt(i); - if (currMenu == aChild) - return insertionPoint; // we found ourselves, break out - if (currMenu && [currMenu->NativeMenuItem() menu]) - insertionPoint++; - } - } - else if (parentType == eSubmenuObjectType || - parentType == eStandaloneNativeMenuObjectType) { - nsMenuX* menuParent; - if (parentType == eSubmenuObjectType) - menuParent = static_cast<nsMenuX*>(aParent); - else - menuParent = static_cast<nsStandaloneNativeMenu*>(aParent)->GetMenuXObject(); - - uint32_t numItems = menuParent->GetItemCount(); - for (uint32_t i = 0; i < numItems; i++) { - // Using GetItemAt instead of GetVisibleItemAt to avoid O(N^2) - nsMenuObjectX* currItem = menuParent->GetItemAt(i); - if (currItem == aChild) - return insertionPoint; // we found ourselves, break out - NSMenuItem* nativeItem = nil; - nsMenuObjectTypeX currItemType = currItem->MenuObjectType(); - if (currItemType == eSubmenuObjectType) - nativeItem = static_cast<nsMenuX*>(currItem)->NativeMenuItem(); - else - nativeItem = (NSMenuItem*)(currItem->NativeData()); - if ([nativeItem menu]) - insertionPoint++; - } - } - return insertionPoint; -} diff --git a/widget/cocoa/nsMenuX.h b/widget/cocoa/nsMenuX.h deleted file mode 100644 index 7b5146a0b8..0000000000 --- a/widget/cocoa/nsMenuX.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -*- 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 nsMenuX_h_ -#define nsMenuX_h_ - -#import <Cocoa/Cocoa.h> - -#include "mozilla/RefPtr.h" -#include "mozilla/UniquePtr.h" -#include "nsMenuBaseX.h" -#include "nsMenuBarX.h" -#include "nsMenuGroupOwnerX.h" -#include "nsCOMPtr.h" -#include "nsChangeObserver.h" - -class nsMenuX; -class nsMenuItemIconX; -class nsMenuItemX; -class nsIWidget; - -// MenuDelegate is used to receive Cocoa notifications for setting -// up carbon events. Protocol is defined as of 10.6 SDK. -@interface MenuDelegate : NSObject < NSMenuDelegate > -{ - nsMenuX* mGeckoMenu; // weak ref -} -- (id)initWithGeckoMenu:(nsMenuX*)geckoMenu; -@end - -// Once instantiated, this object lives until its DOM node or its parent window is destroyed. -// Do not hold references to this, they can become invalid any time the DOM node can be destroyed. -class nsMenuX : public nsMenuObjectX, - public nsChangeObserver -{ -public: - nsMenuX(); - virtual ~nsMenuX(); - - // If > 0, the OS is indexing all the app's menus (triggered by opening - // Help menu on Leopard and higher). There are some things that are - // unsafe to do while this is happening. - static int32_t sIndexingMenuLevel; - - NS_DECL_CHANGEOBSERVER - - // nsMenuObjectX - void* NativeData() override {return (void*)mNativeMenu;} - nsMenuObjectTypeX MenuObjectType() override {return eSubmenuObjectType;} - void IconUpdated() override { mParent->IconUpdated(); } - - // nsMenuX - nsresult Create(nsMenuObjectX* aParent, nsMenuGroupOwnerX* aMenuGroupOwner, nsIContent* aNode); - uint32_t GetItemCount(); - nsMenuObjectX* GetItemAt(uint32_t aPos); - nsresult GetVisibleItemCount(uint32_t &aCount); - nsMenuObjectX* GetVisibleItemAt(uint32_t aPos); - nsEventStatus MenuOpened(); - void MenuClosed(); - void SetRebuild(bool aMenuEvent); - NSMenuItem* NativeMenuItem(); - nsresult SetupIcon(); - - static bool IsXULHelpMenu(nsIContent* aMenuContent); - -protected: - void MenuConstruct(); - nsresult RemoveAll(); - nsresult SetEnabled(bool aIsEnabled); - nsresult GetEnabled(bool* aIsEnabled); - void GetMenuPopupContent(nsIContent** aResult); - bool OnOpen(); - bool OnClose(); - nsresult AddMenuItem(nsMenuItemX* aMenuItem); - nsMenuX* AddMenu(mozilla::UniquePtr<nsMenuX> aMenu); - void LoadMenuItem(nsIContent* inMenuItemContent); - void LoadSubMenu(nsIContent* inMenuContent); - GeckoNSMenu* CreateMenuWithGeckoString(nsString& menuTitle); - - nsTArray<mozilla::UniquePtr<nsMenuObjectX>> mMenuObjectsArray; - nsString mLabel; - uint32_t mVisibleItemsCount; // cache - nsMenuObjectX* mParent; // [weak] - nsMenuGroupOwnerX* mMenuGroupOwner; // [weak] - // The icon object should never outlive its creating nsMenuX object. - RefPtr<nsMenuItemIconX> mIcon; - GeckoNSMenu* mNativeMenu; // [strong] - MenuDelegate* mMenuDelegate; // [strong] - // nsMenuX objects should always have a valid native menu item. - NSMenuItem* mNativeMenuItem; // [strong] - bool mIsEnabled; - bool mDestroyHandlerCalled; - bool mNeedsRebuild; - bool mConstructed; - bool mVisible; - bool mXBLAttached; -}; - -#endif // nsMenuX_h_ diff --git a/widget/cocoa/nsMenuX.mm b/widget/cocoa/nsMenuX.mm deleted file mode 100644 index 757221eac6..0000000000 --- a/widget/cocoa/nsMenuX.mm +++ /dev/null @@ -1,1051 +0,0 @@ -/* -*- 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/. */ - -#include <dlfcn.h> - -#include "nsMenuX.h" -#include "nsMenuItemX.h" -#include "nsMenuUtilsX.h" -#include "nsMenuItemIconX.h" -#include "nsStandaloneNativeMenu.h" - -#include "nsObjCExceptions.h" - -#include "nsToolkit.h" -#include "nsCocoaUtils.h" -#include "nsCOMPtr.h" -#include "prinrval.h" -#include "nsString.h" -#include "nsReadableUtils.h" -#include "nsUnicharUtils.h" -#include "plstr.h" -#include "nsGkAtoms.h" -#include "nsCRT.h" -#include "nsBaseWidget.h" - -#include "nsIDocument.h" -#include "nsIContent.h" -#include "nsIDOMDocument.h" -#include "nsIDocumentObserver.h" -#include "nsIComponentManager.h" -#include "nsIRollupListener.h" -#include "nsIDOMElement.h" -#include "nsBindingManager.h" -#include "nsIServiceManager.h" -#include "nsXULPopupManager.h" -#include "mozilla/dom/ScriptSettings.h" - -#include "jsapi.h" -#include "nsIScriptGlobalObject.h" -#include "nsIScriptContext.h" -#include "nsIXPConnect.h" - -#include "mozilla/MouseEvents.h" - -using namespace mozilla; - -static bool gConstructingMenu = false; -static bool gMenuMethodsSwizzled = false; - -int32_t nsMenuX::sIndexingMenuLevel = 0; - - -// -// Objective-C class used for representedObject -// - -@implementation MenuItemInfo - -- (id) initWithMenuGroupOwner:(nsMenuGroupOwnerX *)aMenuGroupOwner -{ - if ((self = [super init]) != nil) { - [self setMenuGroupOwner:aMenuGroupOwner]; - } - return self; -} - -- (void) dealloc -{ - [self setMenuGroupOwner:nullptr]; - [super dealloc]; -} - -- (nsMenuGroupOwnerX *) menuGroupOwner -{ - return mMenuGroupOwner; -} - -- (void) setMenuGroupOwner:(nsMenuGroupOwnerX *)aMenuGroupOwner -{ - // weak reference as the nsMenuGroupOwnerX owns all of its sub-objects - mMenuGroupOwner = aMenuGroupOwner; - if (aMenuGroupOwner) { - aMenuGroupOwner->AddMenuItemInfoToSet(self); - } -} - -@end - - -// -// nsMenuX -// - -nsMenuX::nsMenuX() -: mVisibleItemsCount(0), mParent(nullptr), mMenuGroupOwner(nullptr), - mNativeMenu(nil), mNativeMenuItem(nil), mIsEnabled(true), - mDestroyHandlerCalled(false), mNeedsRebuild(true), - mConstructed(false), mVisible(true), mXBLAttached(false) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!gMenuMethodsSwizzled) { - nsToolkit::SwizzleMethods([NSMenu class], @selector(_addItem:toTable:), - @selector(nsMenuX_NSMenu_addItem:toTable:), true); - nsToolkit::SwizzleMethods([NSMenu class], @selector(_removeItem:fromTable:), - @selector(nsMenuX_NSMenu_removeItem:fromTable:), true); - // On SnowLeopard the Shortcut framework (which contains the - // SCTGRLIndex class) is loaded on demand, whenever the user first opens - // a menu (which normally hasn't happened yet). So we need to load it - // here explicitly. - dlopen("/System/Library/PrivateFrameworks/Shortcut.framework/Shortcut", RTLD_LAZY); - Class SCTGRLIndexClass = ::NSClassFromString(@"SCTGRLIndex"); - nsToolkit::SwizzleMethods(SCTGRLIndexClass, @selector(indexMenuBarDynamically), - @selector(nsMenuX_SCTGRLIndex_indexMenuBarDynamically)); - - gMenuMethodsSwizzled = true; - } - - mMenuDelegate = [[MenuDelegate alloc] initWithGeckoMenu:this]; - - if (!nsMenuBarX::sNativeEventTarget) - nsMenuBarX::sNativeEventTarget = [[NativeMenuItemTarget alloc] init]; - - MOZ_COUNT_CTOR(nsMenuX); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -nsMenuX::~nsMenuX() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Prevent the icon object from outliving us. - if (mIcon) - mIcon->Destroy(); - - RemoveAll(); - - [mNativeMenu setDelegate:nil]; - [mNativeMenu release]; - [mMenuDelegate release]; - // autorelease the native menu item so that anything else happening to this - // object happens before the native menu item actually dies - [mNativeMenuItem autorelease]; - - // alert the change notifier we don't care no more - if (mContent) - mMenuGroupOwner->UnregisterForContentChanges(mContent); - - MOZ_COUNT_DTOR(nsMenuX); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -nsresult nsMenuX::Create(nsMenuObjectX* aParent, nsMenuGroupOwnerX* aMenuGroupOwner, nsIContent* aNode) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - mContent = aNode; - mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, mLabel); - mNativeMenu = CreateMenuWithGeckoString(mLabel); - - // register this menu to be notified when changes are made to our content object - mMenuGroupOwner = aMenuGroupOwner; // weak ref - NS_ASSERTION(mMenuGroupOwner, "No menu owner given, must have one"); - mMenuGroupOwner->RegisterForContentChanges(mContent, this); - - mParent = aParent; - // our parent could be either a menu bar (if we're toplevel) or a menu (if we're a submenu) - -#ifdef DEBUG - nsMenuObjectTypeX parentType = -#endif - mParent->MenuObjectType(); - NS_ASSERTION((parentType == eMenuBarObjectType || parentType == eSubmenuObjectType || parentType == eStandaloneNativeMenuObjectType), - "Menu parent not a menu bar, menu, or native menu!"); - - if (nsMenuUtilsX::NodeIsHiddenOrCollapsed(mContent)) - mVisible = false; - if (mContent->GetChildCount() == 0) - mVisible = false; - - NSString *newCocoaLabelString = nsMenuUtilsX::GetTruncatedCocoaLabel(mLabel); - mNativeMenuItem = [[NSMenuItem alloc] initWithTitle:newCocoaLabelString action:nil keyEquivalent:@""]; - [mNativeMenuItem setSubmenu:mNativeMenu]; - - SetEnabled(!mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, - nsGkAtoms::_true, eCaseMatters)); - - // We call MenuConstruct here because keyboard commands are dependent upon - // native menu items being created. If we only call MenuConstruct when a menu - // is actually selected, then we can't access keyboard commands until the - // menu gets selected, which is bad. - MenuConstruct(); - - mIcon = new nsMenuItemIconX(this, mContent, mNativeMenuItem); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -nsresult nsMenuX::AddMenuItem(nsMenuItemX* aMenuItem) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (!aMenuItem) - return NS_ERROR_INVALID_ARG; - - mMenuObjectsArray.AppendElement(aMenuItem); - if (nsMenuUtilsX::NodeIsHiddenOrCollapsed(aMenuItem->Content())) - return NS_OK; - ++mVisibleItemsCount; - - NSMenuItem* newNativeMenuItem = (NSMenuItem*)aMenuItem->NativeData(); - - // add the menu item to this menu - [mNativeMenu addItem:newNativeMenuItem]; - - // set up target/action - [newNativeMenuItem setTarget:nsMenuBarX::sNativeEventTarget]; - [newNativeMenuItem setAction:@selector(menuItemHit:)]; - - // set its command. we get the unique command id from the menubar - [newNativeMenuItem setTag:mMenuGroupOwner->RegisterForCommand(aMenuItem)]; - MenuItemInfo * info = [[MenuItemInfo alloc] initWithMenuGroupOwner:mMenuGroupOwner]; - [newNativeMenuItem setRepresentedObject:info]; - [info release]; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -nsMenuX* nsMenuX::AddMenu(UniquePtr<nsMenuX> aMenu) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - // aMenu transfers ownership to mMenuObjectsArray and becomes nullptr, so - // we need to keep a raw pointer to access it conveniently. - nsMenuX* menu = aMenu.get(); - mMenuObjectsArray.AppendElement(Move(aMenu)); - - if (nsMenuUtilsX::NodeIsHiddenOrCollapsed(menu->Content())) { - return menu; - } - - ++mVisibleItemsCount; - - // We have to add a menu item and then associate the menu with it - NSMenuItem* newNativeMenuItem = menu->NativeMenuItem(); - if (newNativeMenuItem) { - [mNativeMenu addItem:newNativeMenuItem]; - [newNativeMenuItem setSubmenu:(NSMenu*)menu->NativeData()]; - } - - return menu; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nullptr); -} - -// Includes all items, including hidden/collapsed ones -uint32_t nsMenuX::GetItemCount() -{ - return mMenuObjectsArray.Length(); -} - -// Includes all items, including hidden/collapsed ones -nsMenuObjectX* nsMenuX::GetItemAt(uint32_t aPos) -{ - if (aPos >= (uint32_t)mMenuObjectsArray.Length()) - return NULL; - - return mMenuObjectsArray[aPos].get(); -} - -// Only includes visible items -nsresult nsMenuX::GetVisibleItemCount(uint32_t &aCount) -{ - aCount = mVisibleItemsCount; - return NS_OK; -} - -// Only includes visible items. Note that this is provides O(N) access -// If you need to iterate or search, consider using GetItemAt and doing your own filtering -nsMenuObjectX* nsMenuX::GetVisibleItemAt(uint32_t aPos) -{ - - uint32_t count = mMenuObjectsArray.Length(); - if (aPos >= mVisibleItemsCount || aPos >= count) - return NULL; - - // If there are no invisible items, can provide direct access - if (mVisibleItemsCount == count) - return mMenuObjectsArray[aPos].get(); - - // Otherwise, traverse the array until we find the the item we're looking for. - nsMenuObjectX* item; - uint32_t visibleNodeIndex = 0; - for (uint32_t i = 0; i < count; i++) { - item = mMenuObjectsArray[i].get(); - if (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(item->Content())) { - if (aPos == visibleNodeIndex) { - // we found the visible node we're looking for, return it - return item; - } - visibleNodeIndex++; - } - } - - return NULL; -} - -nsresult nsMenuX::RemoveAll() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (mNativeMenu) { - // clear command id's - int itemCount = [mNativeMenu numberOfItems]; - for (int i = 0; i < itemCount; i++) - mMenuGroupOwner->UnregisterCommand((uint32_t)[[mNativeMenu itemAtIndex:i] tag]); - // get rid of Cocoa menu items - for (int i = [mNativeMenu numberOfItems] - 1; i >= 0; i--) - [mNativeMenu removeItemAtIndex:i]; - } - - mMenuObjectsArray.Clear(); - mVisibleItemsCount = 0; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -nsEventStatus nsMenuX::MenuOpened() -{ - // Open the node. - mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, NS_LITERAL_STRING("true"), true); - - // Fire a handler. If we're told to stop, don't build the menu at all - bool keepProcessing = OnOpen(); - - if (!mNeedsRebuild || !keepProcessing) - return nsEventStatus_eConsumeNoDefault; - - if (!mConstructed || mNeedsRebuild) { - if (mNeedsRebuild) - RemoveAll(); - - MenuConstruct(); - mConstructed = true; - } - - nsEventStatus status = nsEventStatus_eIgnore; - WidgetMouseEvent event(true, eXULPopupShown, nullptr, - WidgetMouseEvent::eReal); - - nsCOMPtr<nsIContent> popupContent; - GetMenuPopupContent(getter_AddRefs(popupContent)); - nsIContent* dispatchTo = popupContent ? popupContent : mContent; - dispatchTo->DispatchDOMEvent(&event, nullptr, nullptr, &status); - - return nsEventStatus_eConsumeNoDefault; -} - -void nsMenuX::MenuClosed() -{ - if (mConstructed) { - // Don't close if a handler tells us to stop. - if (!OnClose()) - return; - - if (mNeedsRebuild) - mConstructed = false; - - mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::open, true); - - nsEventStatus status = nsEventStatus_eIgnore; - WidgetMouseEvent event(true, eXULPopupHidden, nullptr, - WidgetMouseEvent::eReal); - - nsCOMPtr<nsIContent> popupContent; - GetMenuPopupContent(getter_AddRefs(popupContent)); - nsIContent* dispatchTo = popupContent ? popupContent : mContent; - dispatchTo->DispatchDOMEvent(&event, nullptr, nullptr, &status); - - mDestroyHandlerCalled = true; - mConstructed = false; - } -} - -void nsMenuX::MenuConstruct() -{ - mConstructed = false; - gConstructingMenu = true; - - // reset destroy handler flag so that we'll know to fire it next time this menu goes away. - mDestroyHandlerCalled = false; - - //printf("nsMenuX::MenuConstruct called for %s = %d \n", NS_LossyConvertUTF16toASCII(mLabel).get(), mNativeMenu); - - // Retrieve our menupopup. - nsCOMPtr<nsIContent> menuPopup; - GetMenuPopupContent(getter_AddRefs(menuPopup)); - if (!menuPopup) { - gConstructingMenu = false; - return; - } - - // bug 365405: Manually wrap the menupopup node to make sure it's bounded - if (!mXBLAttached) { - nsresult rv; - nsCOMPtr<nsIXPConnect> xpconnect = - do_GetService(nsIXPConnect::GetCID(), &rv); - if (NS_SUCCEEDED(rv)) { - nsIDocument* ownerDoc = menuPopup->OwnerDoc(); - dom::AutoJSAPI jsapi; - if (ownerDoc && jsapi.Init(ownerDoc->GetInnerWindow())) { - JSContext* cx = jsapi.cx(); - JS::RootedObject ignoredObj(cx); - xpconnect->WrapNative(cx, JS::CurrentGlobalOrNull(cx), menuPopup, - NS_GET_IID(nsISupports), ignoredObj.address()); - mXBLAttached = true; - } - } - } - - // Iterate over the kids - uint32_t count = menuPopup->GetChildCount(); - for (uint32_t i = 0; i < count; i++) { - nsIContent *child = menuPopup->GetChildAt(i); - if (child) { - // depending on the type, create a menu item, separator, or submenu - if (child->IsAnyOfXULElements(nsGkAtoms::menuitem, - nsGkAtoms::menuseparator)) { - LoadMenuItem(child); - } else if (child->IsXULElement(nsGkAtoms::menu)) { - LoadSubMenu(child); - } - } - } // for each menu item - - gConstructingMenu = false; - mNeedsRebuild = false; - // printf("Done building, mMenuObjectsArray.Count() = %d \n", mMenuObjectsArray.Count()); -} - -void nsMenuX::SetRebuild(bool aNeedsRebuild) -{ - if (!gConstructingMenu) - mNeedsRebuild = aNeedsRebuild; -} - -nsresult nsMenuX::SetEnabled(bool aIsEnabled) -{ - if (aIsEnabled != mIsEnabled) { - // we always want to rebuild when this changes - mIsEnabled = aIsEnabled; - [mNativeMenuItem setEnabled:(BOOL)mIsEnabled]; - } - return NS_OK; -} - -nsresult nsMenuX::GetEnabled(bool* aIsEnabled) -{ - NS_ENSURE_ARG_POINTER(aIsEnabled); - *aIsEnabled = mIsEnabled; - return NS_OK; -} - -GeckoNSMenu* nsMenuX::CreateMenuWithGeckoString(nsString& menuTitle) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - NSString* title = [NSString stringWithCharacters:(UniChar*)menuTitle.get() length:menuTitle.Length()]; - GeckoNSMenu* myMenu = [[GeckoNSMenu alloc] initWithTitle:title]; - [myMenu setDelegate:mMenuDelegate]; - - // We don't want this menu to auto-enable menu items because then Cocoa - // overrides our decisions and things get incorrectly enabled/disabled. - [myMenu setAutoenablesItems:NO]; - - // we used to install Carbon event handlers here, but since NSMenu* doesn't - // create its underlying MenuRef until just before display, we delay until - // that happens. Now we install the event handlers when Cocoa notifies - // us that a menu is about to display - see the Cocoa MenuDelegate class. - - return myMenu; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -void nsMenuX::LoadMenuItem(nsIContent* inMenuItemContent) -{ - if (!inMenuItemContent) - return; - - nsAutoString menuitemName; - inMenuItemContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, menuitemName); - - // printf("menuitem %s \n", NS_LossyConvertUTF16toASCII(menuitemName).get()); - - EMenuItemType itemType = eRegularMenuItemType; - if (inMenuItemContent->IsXULElement(nsGkAtoms::menuseparator)) { - itemType = eSeparatorMenuItemType; - } - else { - static nsIContent::AttrValuesArray strings[] = - {&nsGkAtoms::checkbox, &nsGkAtoms::radio, nullptr}; - switch (inMenuItemContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type, - strings, eCaseMatters)) { - case 0: itemType = eCheckboxMenuItemType; break; - case 1: itemType = eRadioMenuItemType; break; - } - } - - // Create the item. - nsMenuItemX* menuItem = new nsMenuItemX(); - if (!menuItem) - return; - - nsresult rv = menuItem->Create(this, menuitemName, itemType, mMenuGroupOwner, inMenuItemContent); - if (NS_FAILED(rv)) { - delete menuItem; - return; - } - - AddMenuItem(menuItem); - - // This needs to happen after the nsIMenuItem object is inserted into - // our item array in AddMenuItem() - menuItem->SetupIcon(); -} - -void nsMenuX::LoadSubMenu(nsIContent* inMenuContent) -{ - auto menu = MakeUnique<nsMenuX>(); - if (!menu) - return; - - nsresult rv = menu->Create(this, mMenuGroupOwner, inMenuContent); - if (NS_FAILED(rv)) - return; - - // |menu|'s ownership is transfer to AddMenu but, if it is successfully - // added, we can access it via the returned raw pointer. - nsMenuX* menu_ptr = AddMenu(Move(menu)); - - // This needs to happen after the nsIMenu object is inserted into - // our item array in AddMenu() - if (menu_ptr) { - menu_ptr->SetupIcon(); - } -} - -// This menu is about to open. Returns TRUE if we should keep processing the event, -// FALSE if the handler wants to stop the opening of the menu. -bool nsMenuX::OnOpen() -{ - nsEventStatus status = nsEventStatus_eIgnore; - WidgetMouseEvent event(true, eXULPopupShowing, nullptr, - WidgetMouseEvent::eReal); - - nsCOMPtr<nsIContent> popupContent; - GetMenuPopupContent(getter_AddRefs(popupContent)); - - nsresult rv = NS_OK; - nsIContent* dispatchTo = popupContent ? popupContent : mContent; - rv = dispatchTo->DispatchDOMEvent(&event, nullptr, nullptr, &status); - if (NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault) - return false; - - // If the open is going to succeed we need to walk our menu items, checking to - // see if any of them have a command attribute. If so, several attributes - // must potentially be updated. - - // Get new popup content first since it might have changed as a result of the - // eXULPopupShowing event above. - GetMenuPopupContent(getter_AddRefs(popupContent)); - if (!popupContent) - return true; - - nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); - if (pm) { - pm->UpdateMenuItems(popupContent); - } - - return true; -} - -// Returns TRUE if we should keep processing the event, FALSE if the handler -// wants to stop the closing of the menu. -bool nsMenuX::OnClose() -{ - if (mDestroyHandlerCalled) - return true; - - nsEventStatus status = nsEventStatus_eIgnore; - WidgetMouseEvent event(true, eXULPopupHiding, nullptr, - WidgetMouseEvent::eReal); - - nsCOMPtr<nsIContent> popupContent; - GetMenuPopupContent(getter_AddRefs(popupContent)); - - nsresult rv = NS_OK; - nsIContent* dispatchTo = popupContent ? popupContent : mContent; - rv = dispatchTo->DispatchDOMEvent(&event, nullptr, nullptr, &status); - - mDestroyHandlerCalled = true; - - if (NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault) - return false; - - return true; -} - -// Find the |menupopup| child in the |popup| representing this menu. It should be one -// of a very few children so we won't be iterating over a bazillion menu items to find -// it (so the strcmp won't kill us). -void nsMenuX::GetMenuPopupContent(nsIContent** aResult) -{ - if (!aResult) - return; - *aResult = nullptr; - - // Check to see if we are a "menupopup" node (if we are a native menu). - { - int32_t dummy; - nsCOMPtr<nsIAtom> tag = mContent->OwnerDoc()->BindingManager()->ResolveTag(mContent, &dummy); - if (tag == nsGkAtoms::menupopup) { - *aResult = mContent; - NS_ADDREF(*aResult); - return; - } - } - - // Otherwise check our child nodes. - - uint32_t count = mContent->GetChildCount(); - - for (uint32_t i = 0; i < count; i++) { - int32_t dummy; - nsIContent *child = mContent->GetChildAt(i); - nsCOMPtr<nsIAtom> tag = child->OwnerDoc()->BindingManager()->ResolveTag(child, &dummy); - if (tag == nsGkAtoms::menupopup) { - *aResult = child; - NS_ADDREF(*aResult); - return; - } - } -} - -NSMenuItem* nsMenuX::NativeMenuItem() -{ - return mNativeMenuItem; -} - -bool nsMenuX::IsXULHelpMenu(nsIContent* aMenuContent) -{ - bool retval = false; - if (aMenuContent) { - nsAutoString id; - aMenuContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id); - if (id.Equals(NS_LITERAL_STRING("helpMenu"))) - retval = true; - } - return retval; -} - -// -// nsChangeObserver -// - -void nsMenuX::ObserveAttributeChanged(nsIDocument *aDocument, nsIContent *aContent, - nsIAtom *aAttribute) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // ignore the |open| attribute, which is by far the most common - if (gConstructingMenu || (aAttribute == nsGkAtoms::open)) - return; - - nsMenuObjectTypeX parentType = mParent->MenuObjectType(); - - if (aAttribute == nsGkAtoms::disabled) { - SetEnabled(!mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, - nsGkAtoms::_true, eCaseMatters)); - } - else if (aAttribute == nsGkAtoms::label) { - mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, mLabel); - - // invalidate my parent. If we're a submenu parent, we have to rebuild - // the parent menu in order for the changes to be picked up. If we're - // a regular menu, just change the title and redraw the menubar. - if (parentType == eMenuBarObjectType) { - // reuse the existing menu, to avoid rebuilding the root menu bar. - NS_ASSERTION(mNativeMenu, "nsMenuX::AttributeChanged: invalid menu handle."); - NSString *newCocoaLabelString = nsMenuUtilsX::GetTruncatedCocoaLabel(mLabel); - [mNativeMenu setTitle:newCocoaLabelString]; - } - else if (parentType == eSubmenuObjectType) { - static_cast<nsMenuX*>(mParent)->SetRebuild(true); - } - else if (parentType == eStandaloneNativeMenuObjectType) { - static_cast<nsStandaloneNativeMenu*>(mParent)->GetMenuXObject()->SetRebuild(true); - } - } - else if (aAttribute == nsGkAtoms::hidden || aAttribute == nsGkAtoms::collapsed) { - SetRebuild(true); - - bool contentIsHiddenOrCollapsed = nsMenuUtilsX::NodeIsHiddenOrCollapsed(mContent); - - // don't do anything if the state is correct already - if (contentIsHiddenOrCollapsed != mVisible) - return; - - if (contentIsHiddenOrCollapsed) { - if (parentType == eMenuBarObjectType || - parentType == eSubmenuObjectType || - parentType == eStandaloneNativeMenuObjectType) { - NSMenu* parentMenu = (NSMenu*)mParent->NativeData(); - // An exception will get thrown if we try to remove an item that isn't - // in the menu. - if ([parentMenu indexOfItem:mNativeMenuItem] != -1) - [parentMenu removeItem:mNativeMenuItem]; - mVisible = false; - } - } - else { - if (parentType == eMenuBarObjectType || - parentType == eSubmenuObjectType || - parentType == eStandaloneNativeMenuObjectType) { - int insertionIndex = nsMenuUtilsX::CalculateNativeInsertionPoint(mParent, this); - if (parentType == eMenuBarObjectType) { - // Before inserting we need to figure out if we should take the native - // application menu into account. - nsMenuBarX* mb = static_cast<nsMenuBarX*>(mParent); - if (mb->MenuContainsAppMenu()) - insertionIndex++; - } - NSMenu* parentMenu = (NSMenu*)mParent->NativeData(); - [parentMenu insertItem:mNativeMenuItem atIndex:insertionIndex]; - [mNativeMenuItem setSubmenu:mNativeMenu]; - mVisible = true; - } - } - } - else if (aAttribute == nsGkAtoms::image) { - SetupIcon(); - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void nsMenuX::ObserveContentRemoved(nsIDocument *aDocument, nsIContent *aChild, - int32_t aIndexInContainer) -{ - if (gConstructingMenu) - return; - - SetRebuild(true); - mMenuGroupOwner->UnregisterForContentChanges(aChild); -} - -void nsMenuX::ObserveContentInserted(nsIDocument *aDocument, nsIContent* aContainer, - nsIContent *aChild) -{ - if (gConstructingMenu) - return; - - SetRebuild(true); -} - -nsresult nsMenuX::SetupIcon() -{ - // In addition to out-of-memory, menus that are children of the menu bar - // will not have mIcon set. - if (!mIcon) - return NS_ERROR_OUT_OF_MEMORY; - - return mIcon->SetupIcon(); -} - -// -// MenuDelegate Objective-C class, used to set up Carbon events -// - -@implementation MenuDelegate - -- (id)initWithGeckoMenu:(nsMenuX*)geckoMenu -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - if ((self = [super init])) { - NS_ASSERTION(geckoMenu, "Cannot initialize native menu delegate with NULL gecko menu! Will crash!"); - mGeckoMenu = geckoMenu; - } - return self; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item -{ - if (!menu || !item || !mGeckoMenu) - return; - - nsMenuObjectX* target = mGeckoMenu->GetVisibleItemAt((uint32_t)[menu indexOfItem:item]); - if (target && (target->MenuObjectType() == eMenuItemObjectType)) { - nsMenuItemX* targetMenuItem = static_cast<nsMenuItemX*>(target); - bool handlerCalledPreventDefault; // but we don't actually care - targetMenuItem->DispatchDOMEvent(NS_LITERAL_STRING("DOMMenuItemActive"), &handlerCalledPreventDefault); - } -} - -- (void)menuWillOpen:(NSMenu *)menu -{ - if (!mGeckoMenu) - return; - - // Don't do anything while the OS is (re)indexing our menus (on Leopard and - // higher). This stops the Help menu from being able to search in our - // menus, but it also resolves many other problems. - if (nsMenuX::sIndexingMenuLevel > 0) - return; - - nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); - if (rollupListener) { - nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget(); - if (rollupWidget) { - rollupListener->Rollup(0, true, nullptr, nullptr); - [menu cancelTracking]; - return; - } - } - mGeckoMenu->MenuOpened(); -} - -- (void)menuDidClose:(NSMenu *)menu -{ - if (!mGeckoMenu) - return; - - // Don't do anything while the OS is (re)indexing our menus (on Leopard and - // higher). This stops the Help menu from being able to search in our - // menus, but it also resolves many other problems. - if (nsMenuX::sIndexingMenuLevel > 0) - return; - - mGeckoMenu->MenuClosed(); -} - -@end - -// OS X Leopard (at least as of 10.5.2) has an obscure bug triggered by some -// behavior that's present in Mozilla.org browsers but not (as best I can -// tell) in Apple products like Safari. (It's not yet clear exactly what this -// behavior is.) -// -// The bug is that sometimes you crash on quit in nsMenuX::RemoveAll(), on a -// call to [NSMenu removeItemAtIndex:]. The crash is caused by trying to -// access a deleted NSMenuItem object (sometimes (perhaps always?) by trying -// to send it a _setChangedFlags: message). Though this object was deleted -// some time ago, it remains registered as a potential target for a particular -// key equivalent. So when [NSMenu removeItemAtIndex:] removes the current -// target for that same key equivalent, the OS tries to "activate" the -// previous target. -// -// The underlying reason appears to be that NSMenu's _addItem:toTable: and -// _removeItem:fromTable: methods (which are used to keep a hashtable of -// registered key equivalents) don't properly "retain" and "release" -// NSMenuItem objects as they are added to and removed from the hashtable. -// -// Our (hackish) workaround is to shadow the OS's hashtable with another -// hastable of our own (gShadowKeyEquivDB), and use it to "retain" and -// "release" NSMenuItem objects as needed. This resolves bmo bugs 422287 and -// 423669. When (if) Apple fixes this bug, we can remove this workaround. - -static NSMutableDictionary *gShadowKeyEquivDB = nil; - -// Class for values in gShadowKeyEquivDB. - -@interface KeyEquivDBItem : NSObject -{ - NSMenuItem *mItem; - NSMutableSet *mTables; -} - -- (id)initWithItem:(NSMenuItem *)aItem table:(NSMapTable *)aTable; -- (BOOL)hasTable:(NSMapTable *)aTable; -- (int)addTable:(NSMapTable *)aTable; -- (int)removeTable:(NSMapTable *)aTable; - -@end - -@implementation KeyEquivDBItem - -- (id)initWithItem:(NSMenuItem *)aItem table:(NSMapTable *)aTable -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - if (!gShadowKeyEquivDB) - gShadowKeyEquivDB = [[NSMutableDictionary alloc] init]; - self = [super init]; - if (aItem && aTable) { - mTables = [[NSMutableSet alloc] init]; - mItem = [aItem retain]; - [mTables addObject:[NSValue valueWithPointer:aTable]]; - } else { - mTables = nil; - mItem = nil; - } - return self; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (void)dealloc -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (mTables) - [mTables release]; - if (mItem) - [mItem release]; - [super dealloc]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (BOOL)hasTable:(NSMapTable *)aTable -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - return [mTables member:[NSValue valueWithPointer:aTable]] ? YES : NO; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); -} - -// Does nothing if aTable (its index value) is already present in mTables. -- (int)addTable:(NSMapTable *)aTable -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - if (aTable) - [mTables addObject:[NSValue valueWithPointer:aTable]]; - return [mTables count]; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); -} - -- (int)removeTable:(NSMapTable *)aTable -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - if (aTable) { - NSValue *objectToRemove = - [mTables member:[NSValue valueWithPointer:aTable]]; - if (objectToRemove) - [mTables removeObject:objectToRemove]; - } - return [mTables count]; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); -} - -@end - -@interface NSMenu (MethodSwizzling) -+ (void)nsMenuX_NSMenu_addItem:(NSMenuItem *)aItem toTable:(NSMapTable *)aTable; -+ (void)nsMenuX_NSMenu_removeItem:(NSMenuItem *)aItem fromTable:(NSMapTable *)aTable; -@end - -@implementation NSMenu (MethodSwizzling) - -+ (void)nsMenuX_NSMenu_addItem:(NSMenuItem *)aItem toTable:(NSMapTable *)aTable -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (aItem && aTable) { - NSValue *key = [NSValue valueWithPointer:aItem]; - KeyEquivDBItem *shadowItem = [gShadowKeyEquivDB objectForKey:key]; - if (shadowItem) { - [shadowItem addTable:aTable]; - } else { - shadowItem = [[KeyEquivDBItem alloc] initWithItem:aItem table:aTable]; - [gShadowKeyEquivDB setObject:shadowItem forKey:key]; - // Release after [NSMutableDictionary setObject:forKey:] retains it (so - // that it will get dealloced when removeObjectForKey: is called). - [shadowItem release]; - } - } - - NS_OBJC_END_TRY_ABORT_BLOCK; - - [self nsMenuX_NSMenu_addItem:aItem toTable:aTable]; -} - -+ (void)nsMenuX_NSMenu_removeItem:(NSMenuItem *)aItem fromTable:(NSMapTable *)aTable -{ - [self nsMenuX_NSMenu_removeItem:aItem fromTable:aTable]; - - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (aItem && aTable) { - NSValue *key = [NSValue valueWithPointer:aItem]; - KeyEquivDBItem *shadowItem = [gShadowKeyEquivDB objectForKey:key]; - if (shadowItem && [shadowItem hasTable:aTable]) { - if (![shadowItem removeTable:aTable]) - [gShadowKeyEquivDB removeObjectForKey:key]; - } - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -@end - -// This class is needed to keep track of when the OS is (re)indexing all of -// our menus. This appears to only happen on Leopard and higher, and can -// be triggered by opening the Help menu. Some operations are unsafe while -// this is happening -- notably the calls to [[NSImage alloc] -// initWithSize:imageRect.size] and [newImage lockFocus] in nsMenuItemIconX:: -// OnStopFrame(). But we don't yet have a complete list, and Apple doesn't -// yet have any documentation on this subject. (Apple also doesn't yet have -// any documented way to find the information we seek here.) The "original" -// of this class (the one whose indexMenuBarDynamically method we hook) is -// defined in the Shortcut framework in /System/Library/PrivateFrameworks. -@interface NSObject (SCTGRLIndexMethodSwizzling) -- (void)nsMenuX_SCTGRLIndex_indexMenuBarDynamically; -@end - -@implementation NSObject (SCTGRLIndexMethodSwizzling) - -- (void)nsMenuX_SCTGRLIndex_indexMenuBarDynamically -{ - // This method appears to be called (once) whenever the OS (re)indexes our - // menus. sIndexingMenuLevel is a int32_t just in case it might be - // reentered. As it's running, it spawns calls to two undocumented - // HIToolbox methods (_SimulateMenuOpening() and _SimulateMenuClosed()), - // which "simulate" the opening and closing of our menus without actually - // displaying them. - ++nsMenuX::sIndexingMenuLevel; - [self nsMenuX_SCTGRLIndex_indexMenuBarDynamically]; - --nsMenuX::sIndexingMenuLevel; -} - -@end diff --git a/widget/cocoa/nsNativeThemeCocoa.h b/widget/cocoa/nsNativeThemeCocoa.h deleted file mode 100644 index 23f2bc4d32..0000000000 --- a/widget/cocoa/nsNativeThemeCocoa.h +++ /dev/null @@ -1,178 +0,0 @@ -/* -*- 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 nsNativeThemeCocoa_h_ -#define nsNativeThemeCocoa_h_ - -#import <Carbon/Carbon.h> -#import <Cocoa/Cocoa.h> - -#include "nsITheme.h" -#include "nsCOMPtr.h" -#include "nsIAtom.h" -#include "nsNativeTheme.h" - -@class CellDrawView; -@class NSProgressBarCell; -@class ContextAwareSearchFieldCell; -class nsDeviceContext; -struct SegmentedControlRenderSettings; - -namespace mozilla { -class EventStates; -} // namespace mozilla - -class nsNativeThemeCocoa : private nsNativeTheme, - public nsITheme -{ -public: - enum { - eThemeGeometryTypeTitlebar = eThemeGeometryTypeUnknown + 1, - eThemeGeometryTypeToolbar, - eThemeGeometryTypeToolbox, - eThemeGeometryTypeWindowButtons, - eThemeGeometryTypeFullscreenButton, - eThemeGeometryTypeMenu, - eThemeGeometryTypeHighlightedMenuItem, - eThemeGeometryTypeVibrancyLight, - eThemeGeometryTypeVibrancyDark, - eThemeGeometryTypeTooltip, - eThemeGeometryTypeSheet, - eThemeGeometryTypeSourceList, - eThemeGeometryTypeSourceListSelection, - eThemeGeometryTypeActiveSourceListSelection - }; - - nsNativeThemeCocoa(); - - NS_DECL_ISUPPORTS_INHERITED - - // The nsITheme interface. - NS_IMETHOD DrawWidgetBackground(nsRenderingContext* aContext, - nsIFrame* aFrame, - uint8_t aWidgetType, - const nsRect& aRect, - const nsRect& aDirtyRect) override; - NS_IMETHOD GetWidgetBorder(nsDeviceContext* aContext, - nsIFrame* aFrame, - uint8_t aWidgetType, - nsIntMargin* aResult) override; - - virtual bool GetWidgetPadding(nsDeviceContext* aContext, - nsIFrame* aFrame, - uint8_t aWidgetType, - nsIntMargin* aResult) override; - - virtual bool GetWidgetOverflow(nsDeviceContext* aContext, nsIFrame* aFrame, - uint8_t aWidgetType, nsRect* aOverflowRect) override; - - NS_IMETHOD GetMinimumWidgetSize(nsPresContext* aPresContext, nsIFrame* aFrame, - uint8_t aWidgetType, - mozilla::LayoutDeviceIntSize* aResult, bool* aIsOverridable) override; - NS_IMETHOD WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType, - nsIAtom* aAttribute, bool* aShouldRepaint, - const nsAttrValue* aOldValue) override; - NS_IMETHOD ThemeChanged() override; - bool ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* aFrame, uint8_t aWidgetType) override; - bool WidgetIsContainer(uint8_t aWidgetType) override; - bool ThemeDrawsFocusForWidget(uint8_t aWidgetType) override; - bool ThemeNeedsComboboxDropmarker() override; - virtual bool WidgetAppearanceDependsOnWindowFocus(uint8_t aWidgetType) override; - virtual bool NeedToClearBackgroundBehindWidget(nsIFrame* aFrame, - uint8_t aWidgetType) override; - virtual bool WidgetProvidesFontSmoothingBackgroundColor(nsIFrame* aFrame, uint8_t aWidgetType, - nscolor* aColor) override; - virtual ThemeGeometryType ThemeGeometryTypeForWidget(nsIFrame* aFrame, - uint8_t aWidgetType) override; - virtual Transparency GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType) override; - - void DrawProgress(CGContextRef context, const HIRect& inBoxRect, - bool inIsIndeterminate, bool inIsHorizontal, - double inValue, double inMaxValue, nsIFrame* aFrame); - - static void DrawNativeTitlebar(CGContextRef aContext, CGRect aTitlebarRect, - CGFloat aUnifiedHeight, BOOL aIsMain, BOOL aIsFlipped); - -protected: - virtual ~nsNativeThemeCocoa(); - - nsIntMargin DirectionAwareMargin(const nsIntMargin& aMargin, nsIFrame* aFrame); - nsIFrame* SeparatorResponsibility(nsIFrame* aBefore, nsIFrame* aAfter); - CGRect SeparatorAdjustedRect(CGRect aRect, nsIFrame* aLeft, - nsIFrame* aCurrent, nsIFrame* aRight); - bool IsWindowSheet(nsIFrame* aFrame); - - // HITheme drawing routines - void DrawFrame(CGContextRef context, HIThemeFrameKind inKind, - const HIRect& inBoxRect, bool inReadOnly, - mozilla::EventStates inState); - void DrawMeter(CGContextRef context, const HIRect& inBoxRect, - nsIFrame* aFrame); - void DrawSegment(CGContextRef cgContext, const HIRect& inBoxRect, - mozilla::EventStates inState, nsIFrame* aFrame, - const SegmentedControlRenderSettings& aSettings); - void DrawTabPanel(CGContextRef context, const HIRect& inBoxRect, nsIFrame* aFrame); - void DrawScale(CGContextRef context, const HIRect& inBoxRect, - mozilla::EventStates inState, bool inDirection, - bool inIsReverse, int32_t inCurrentValue, int32_t inMinValue, - int32_t inMaxValue, nsIFrame* aFrame); - void DrawCheckboxOrRadio(CGContextRef cgContext, bool inCheckbox, - const HIRect& inBoxRect, bool inSelected, - mozilla::EventStates inState, nsIFrame* aFrame); - void DrawSearchField(CGContextRef cgContext, const HIRect& inBoxRect, - nsIFrame* aFrame, mozilla::EventStates inState); - void DrawPushButton(CGContextRef cgContext, const HIRect& inBoxRect, - mozilla::EventStates inState, uint8_t aWidgetType, - nsIFrame* aFrame, float aOriginalHeight); - void DrawMenuIcon(CGContextRef cgContext, const CGRect& aRect, - mozilla::EventStates inState, nsIFrame* aFrame, - const NSSize& aIconSize, NSString* aImageName, - bool aCenterHorizontally); - void DrawButton(CGContextRef context, ThemeButtonKind inKind, - const HIRect& inBoxRect, bool inIsDefault, - ThemeButtonValue inValue, ThemeButtonAdornment inAdornment, - mozilla::EventStates inState, nsIFrame* aFrame); - void DrawFocusOutline(CGContextRef cgContext, const HIRect& inBoxRect, - mozilla::EventStates inState, uint8_t aWidgetType, - nsIFrame* aFrame); - void DrawDropdown(CGContextRef context, const HIRect& inBoxRect, - mozilla::EventStates inState, uint8_t aWidgetType, - nsIFrame* aFrame); - void DrawSpinButtons(CGContextRef context, ThemeButtonKind inKind, - const HIRect& inBoxRect, ThemeDrawState inDrawState, - ThemeButtonAdornment inAdornment, - mozilla::EventStates inState, nsIFrame* aFrame); - void DrawSpinButton(CGContextRef context, ThemeButtonKind inKind, - const HIRect& inBoxRect, ThemeDrawState inDrawState, - ThemeButtonAdornment inAdornment, - mozilla::EventStates inState, - nsIFrame* aFrame, uint8_t aWidgetType); - void DrawUnifiedToolbar(CGContextRef cgContext, const HIRect& inBoxRect, - NSWindow* aWindow); - void DrawStatusBar(CGContextRef cgContext, const HIRect& inBoxRect, - nsIFrame *aFrame); - void DrawResizer(CGContextRef cgContext, const HIRect& aRect, nsIFrame *aFrame); - - // Scrollbars - void GetScrollbarPressStates(nsIFrame *aFrame, - mozilla::EventStates aButtonStates[]); - nsIFrame* GetParentScrollbarFrame(nsIFrame *aFrame); - bool IsParentScrollbarRolledOver(nsIFrame* aFrame); - -private: - NSButtonCell* mDisclosureButtonCell; - NSButtonCell* mHelpButtonCell; - NSButtonCell* mPushButtonCell; - NSButtonCell* mRadioButtonCell; - NSButtonCell* mCheckboxCell; - ContextAwareSearchFieldCell* mSearchFieldCell; - NSPopUpButtonCell* mDropdownCell; - NSComboBoxCell* mComboBoxCell; - NSProgressBarCell* mProgressBarCell; - NSLevelIndicatorCell* mMeterBarCell; - CellDrawView* mCellDrawView; -}; - -#endif // nsNativeThemeCocoa_h_ diff --git a/widget/cocoa/nsNativeThemeCocoa.mm b/widget/cocoa/nsNativeThemeCocoa.mm deleted file mode 100644 index 056c453f2a..0000000000 --- a/widget/cocoa/nsNativeThemeCocoa.mm +++ /dev/null @@ -1,3961 +0,0 @@ -/* -*- 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/. */ - -#include "nsNativeThemeCocoa.h" - -#include "mozilla/gfx/2D.h" -#include "mozilla/gfx/Helpers.h" -#include "nsChildView.h" -#include "nsDeviceContext.h" -#include "nsLayoutUtils.h" -#include "nsObjCExceptions.h" -#include "nsNumberControlFrame.h" -#include "nsRangeFrame.h" -#include "nsRenderingContext.h" -#include "nsRect.h" -#include "nsSize.h" -#include "nsThemeConstants.h" -#include "nsIPresShell.h" -#include "nsPresContext.h" -#include "nsIContent.h" -#include "nsIDocument.h" -#include "nsIFrame.h" -#include "nsIAtom.h" -#include "nsNameSpaceManager.h" -#include "nsPresContext.h" -#include "nsGkAtoms.h" -#include "nsCocoaFeatures.h" -#include "nsCocoaWindow.h" -#include "nsNativeThemeColors.h" -#include "nsIScrollableFrame.h" -#include "mozilla/EventStates.h" -#include "mozilla/dom/Element.h" -#include "mozilla/dom/HTMLMeterElement.h" -#include "nsLookAndFeel.h" -#include "VibrancyManager.h" - -#include "gfxContext.h" -#include "gfxQuartzSurface.h" -#include "gfxQuartzNativeDrawing.h" -#include <algorithm> - -using namespace mozilla; -using namespace mozilla::gfx; -using mozilla::dom::HTMLMeterElement; - -#define DRAW_IN_FRAME_DEBUG 0 -#define SCROLLBARS_VISUAL_DEBUG 0 - -// private Quartz routines needed here -extern "C" { - CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform); - CG_EXTERN void CGContextSetBaseCTM(CGContextRef, CGAffineTransform); - typedef CFTypeRef CUIRendererRef; - void CUIDraw(CUIRendererRef r, CGRect rect, CGContextRef ctx, CFDictionaryRef options, CFDictionaryRef* result); -} - -// Workaround for NSCell control tint drawing -// Without this workaround, NSCells are always drawn with the clear control tint -// as long as they're not attached to an NSControl which is a subview of an active window. -// XXXmstange Why doesn't Webkit need this? -@implementation NSCell (ControlTintWorkaround) -- (int)_realControlTint { return [self controlTint]; } -@end - -// The purpose of this class is to provide objects that can be used when drawing -// NSCells using drawWithFrame:inView: without causing any harm. The only -// messages that will be sent to such an object are "isFlipped" and -// "currentEditor": isFlipped needs to return YES in order to avoid drawing bugs -// on 10.4 (see bug 465069); currentEditor (which isn't even a method of -// NSView) will be called when drawing search fields, and we only provide it in -// order to prevent "unrecognized selector" exceptions. -// There's no need to pass the actual NSView that we're drawing into to -// drawWithFrame:inView:. What's more, doing so even causes unnecessary -// invalidations as soon as we draw a focusring! -@interface CellDrawView : NSView - -@end; - -@implementation CellDrawView - -- (BOOL)isFlipped -{ - return YES; -} - -- (NSText*)currentEditor -{ - return nil; -} - -@end - -// These two classes don't actually add any behavior over NSButtonCell. Their -// purpose is to make it easy to distinguish NSCell objects that are used for -// drawing radio buttons / checkboxes from other cell types. -// The class names are made up, there are no classes with these names in AppKit. -// The reason we need them is that calling [cell setButtonType:NSRadioButton] -// doesn't leave an easy-to-check "marker" on the cell object - there is no -// -[NSButtonCell buttonType] method. -@interface RadioButtonCell : NSButtonCell -@end; -@implementation RadioButtonCell @end; -@interface CheckboxCell : NSButtonCell -@end; -@implementation CheckboxCell @end; - -static void -DrawFocusRingForCellIfNeeded(NSCell* aCell, NSRect aWithFrame, NSView* aInView) -{ - if ([aCell showsFirstResponder]) { - CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; - CGContextSaveGState(cgContext); - - // It's important to set the focus ring style before we enter the - // transparency layer so that the transparency layer only contains - // the normal button mask without the focus ring, and the conversion - // to the focus ring shape happens only when the transparency layer is - // ended. - NSSetFocusRingStyle(NSFocusRingOnly); - - // We need to draw the whole button into a transparency layer because - // many button types are composed of multiple parts, and if these parts - // were drawn while the focus ring style was active, each individual part - // would produce a focus ring for itself. But we only want one focus ring - // for the whole button. The transparency layer is a way to merge the - // individual button parts together before the focus ring shape is - // calculated. - CGContextBeginTransparencyLayerWithRect(cgContext, NSRectToCGRect(aWithFrame), 0); - [aCell drawFocusRingMaskWithFrame:aWithFrame inView:aInView]; - CGContextEndTransparencyLayer(cgContext); - - CGContextRestoreGState(cgContext); - } -} - -static bool -FocusIsDrawnByDrawWithFrame(NSCell* aCell) -{ -#if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 - // When building with the 10.8 SDK or higher, focus rings don't draw as part - // of -[NSCell drawWithFrame:inView:] and must be drawn by a separate call - // to -[NSCell drawFocusRingMaskWithFrame:inView:]; . - // See the NSButtonCell section under - // https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKitOlderNotes/#X10_8Notes - return false; -#else - if (!nsCocoaFeatures::OnYosemiteOrLater()) { - // When building with the 10.7 SDK or lower, focus rings always draw as - // part of -[NSCell drawWithFrame:inView:] if the build is run on 10.9 or - // lower. - return true; - } - - // On 10.10, whether the focus ring is drawn as part of - // -[NSCell drawWithFrame:inView:] depends on the cell type. - // Radio buttons and checkboxes draw their own focus rings, other cell - // types need -[NSCell drawFocusRingMaskWithFrame:inView:]. - return [aCell isKindOfClass:[RadioButtonCell class]] || - [aCell isKindOfClass:[CheckboxCell class]]; -#endif -} - -static void -DrawCellIncludingFocusRing(NSCell* aCell, NSRect aWithFrame, NSView* aInView) -{ - [aCell drawWithFrame:aWithFrame inView:aInView]; - - if (!FocusIsDrawnByDrawWithFrame(aCell)) { - DrawFocusRingForCellIfNeeded(aCell, aWithFrame, aInView); - } -} - -/** - * NSProgressBarCell is used to draw progress bars of any size. - */ -@interface NSProgressBarCell : NSCell -{ - /*All instance variables are private*/ - double mValue; - double mMax; - bool mIsIndeterminate; - bool mIsHorizontal; -} - -- (void)setValue:(double)value; -- (double)value; -- (void)setMax:(double)max; -- (double)max; -- (void)setIndeterminate:(bool)aIndeterminate; -- (bool)isIndeterminate; -- (void)setHorizontal:(bool)aIsHorizontal; -- (bool)isHorizontal; -- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView; -@end - -@implementation NSProgressBarCell - -- (void)setMax:(double)aMax -{ - mMax = aMax; -} - -- (double)max -{ - return mMax; -} - -- (void)setValue:(double)aValue -{ - mValue = aValue; -} - -- (double)value -{ - return mValue; -} - -- (void)setIndeterminate:(bool)aIndeterminate -{ - mIsIndeterminate = aIndeterminate; -} - -- (bool)isIndeterminate -{ - return mIsIndeterminate; -} - -- (void)setHorizontal:(bool)aIsHorizontal -{ - mIsHorizontal = aIsHorizontal; -} - -- (bool)isHorizontal -{ - return mIsHorizontal; -} - -- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView -{ - CGContext* cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; - - HIThemeTrackDrawInfo tdi; - - tdi.version = 0; - tdi.min = 0; - - tdi.value = INT32_MAX * (mValue / mMax); - tdi.max = INT32_MAX; - tdi.bounds = NSRectToCGRect(cellFrame); - tdi.attributes = mIsHorizontal ? kThemeTrackHorizontal : 0; - tdi.enableState = [self controlTint] == NSClearControlTint ? kThemeTrackInactive - : kThemeTrackActive; - - NSControlSize size = [self controlSize]; - if (size == NSRegularControlSize) { - tdi.kind = mIsIndeterminate ? kThemeLargeIndeterminateBar - : kThemeLargeProgressBar; - } else { - NS_ASSERTION(size == NSSmallControlSize, - "We shouldn't have another size than small and regular for the moment"); - tdi.kind = mIsIndeterminate ? kThemeMediumIndeterminateBar - : kThemeMediumProgressBar; - } - - int32_t stepsPerSecond = mIsIndeterminate ? 60 : 30; - int32_t milliSecondsPerStep = 1000 / stepsPerSecond; - tdi.trackInfo.progress.phase = uint8_t(PR_IntervalToMilliseconds(PR_IntervalNow()) / - milliSecondsPerStep); - - HIThemeDrawTrack(&tdi, NULL, cgContext, kHIThemeOrientationNormal); -} - -@end - -@interface ContextAwareSearchFieldCell : NSSearchFieldCell -{ - nsIFrame* mContext; -} - -// setContext: stores the searchfield nsIFrame so that it can be consulted -// during painting. Please reset this by calling setContext:nullptr as soon as -// you're done with painting because we don't want to keep a dangling pointer. -- (void)setContext:(nsIFrame*)aContext; -@end - -@implementation ContextAwareSearchFieldCell - -- (id)initTextCell:(NSString*)aString -{ - if ((self = [super initTextCell:aString])) { - mContext = nullptr; - } - return self; -} - -- (void)setContext:(nsIFrame*)aContext -{ - mContext = aContext; -} - -static BOOL IsToolbarStyleContainer(nsIFrame* aFrame) -{ - nsIContent* content = aFrame->GetContent(); - if (!content) - return NO; - - if (content->IsAnyOfXULElements(nsGkAtoms::toolbar, - nsGkAtoms::toolbox, - nsGkAtoms::statusbar)) - return YES; - - switch (aFrame->StyleDisplay()->mAppearance) { - case NS_THEME_TOOLBAR: - case NS_THEME_STATUSBAR: - return YES; - default: - return NO; - } -} - -- (BOOL)_isToolbarMode -{ - // On 10.7, searchfields have two different styles, depending on whether - // the searchfield is on top of of window chrome. This function is called on - // 10.7 during drawing in order to determine which style to use. - for (nsIFrame* frame = mContext; frame; frame = frame->GetParent()) { - if (IsToolbarStyleContainer(frame)) { - return YES; - } - } - return NO; -} - -@end - -// Workaround for Bug 542048 -// On 64-bit, NSSearchFieldCells don't draw focus rings. -#if defined(__x86_64__) - -@interface SearchFieldCellWithFocusRing : ContextAwareSearchFieldCell {} @end - -@implementation SearchFieldCellWithFocusRing - -- (void)drawWithFrame:(NSRect)rect inView:(NSView*)controlView -{ - [super drawWithFrame:rect inView:controlView]; - - if (FocusIsDrawnByDrawWithFrame(self)) { - // For some reason, -[NSSearchFieldCell drawWithFrame:inView] doesn't draw a - // focus ring in 64 bit mode, no matter what SDK is used or what OS X version - // we're running on. But if FocusIsDrawnByDrawWithFrame(self), then our - // caller expects us to draw a focus ring. So we just do that here. - DrawFocusRingForCellIfNeeded(self, rect, controlView); - } -} - -- (void)drawFocusRingMaskWithFrame:(NSRect)rect inView:(NSView*)controlView -{ - // By default this draws nothing. I don't know why. - // We just draw the search field again. It's a great mask shape for its own - // focus ring. - [super drawWithFrame:rect inView:controlView]; -} - -@end - -#endif - -#define HITHEME_ORIENTATION kHIThemeOrientationNormal - -static CGFloat kMaxFocusRingWidth = 0; // initialized by the nsNativeThemeCocoa constructor - -// These enums are for indexing into the margin array. -enum { - leopardOSorlater = 0, // 10.6 - 10.9 - yosemiteOSorlater = 1 // 10.10+ -}; - -enum { - miniControlSize, - smallControlSize, - regularControlSize -}; - -enum { - leftMargin, - topMargin, - rightMargin, - bottomMargin -}; - -static size_t EnumSizeForCocoaSize(NSControlSize cocoaControlSize) { - if (cocoaControlSize == NSMiniControlSize) - return miniControlSize; - else if (cocoaControlSize == NSSmallControlSize) - return smallControlSize; - else - return regularControlSize; -} - -static NSControlSize CocoaSizeForEnum(int32_t enumControlSize) { - if (enumControlSize == miniControlSize) - return NSMiniControlSize; - else if (enumControlSize == smallControlSize) - return NSSmallControlSize; - else - return NSRegularControlSize; -} - -static NSString* CUIControlSizeForCocoaSize(NSControlSize aControlSize) -{ - if (aControlSize == NSRegularControlSize) - return @"regular"; - else if (aControlSize == NSSmallControlSize) - return @"small"; - else - return @"mini"; -} - -static void InflateControlRect(NSRect* rect, NSControlSize cocoaControlSize, const float marginSet[][3][4]) -{ - if (!marginSet) - return; - - static int osIndex = nsCocoaFeatures::OnYosemiteOrLater() ? - yosemiteOSorlater : leopardOSorlater; - size_t controlSize = EnumSizeForCocoaSize(cocoaControlSize); - const float* buttonMargins = marginSet[osIndex][controlSize]; - rect->origin.x -= buttonMargins[leftMargin]; - rect->origin.y -= buttonMargins[bottomMargin]; - rect->size.width += buttonMargins[leftMargin] + buttonMargins[rightMargin]; - rect->size.height += buttonMargins[bottomMargin] + buttonMargins[topMargin]; -} - -static ChildView* ChildViewForFrame(nsIFrame* aFrame) -{ - if (!aFrame) - return nil; - - nsIWidget* widget = aFrame->GetNearestWidget(); - if (!widget) - return nil; - - NSWindow* window = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW); - return [window isKindOfClass:[BaseWindow class]] ? [(BaseWindow*)window mainChildView] : nil; -} - -static NSWindow* NativeWindowForFrame(nsIFrame* aFrame, - nsIWidget** aTopLevelWidget = NULL) -{ - if (!aFrame) - return nil; - - nsIWidget* widget = aFrame->GetNearestWidget(); - if (!widget) - return nil; - - nsIWidget* topLevelWidget = widget->GetTopLevelWidget(); - if (aTopLevelWidget) - *aTopLevelWidget = topLevelWidget; - - return (NSWindow*)topLevelWidget->GetNativeData(NS_NATIVE_WINDOW); -} - -static NSSize -WindowButtonsSize(nsIFrame* aFrame) -{ - NSWindow* window = NativeWindowForFrame(aFrame); - if (!window) { - // Return fallback values. - return NSMakeSize(54, 16); - } - - NSRect buttonBox = NSZeroRect; - NSButton* closeButton = [window standardWindowButton:NSWindowCloseButton]; - if (closeButton) { - buttonBox = NSUnionRect(buttonBox, [closeButton frame]); - } - NSButton* minimizeButton = [window standardWindowButton:NSWindowMiniaturizeButton]; - if (minimizeButton) { - buttonBox = NSUnionRect(buttonBox, [minimizeButton frame]); - } - NSButton* zoomButton = [window standardWindowButton:NSWindowZoomButton]; - if (zoomButton) { - buttonBox = NSUnionRect(buttonBox, [zoomButton frame]); - } - return buttonBox.size; -} - -static BOOL FrameIsInActiveWindow(nsIFrame* aFrame) -{ - nsIWidget* topLevelWidget = NULL; - NSWindow* win = NativeWindowForFrame(aFrame, &topLevelWidget); - if (!topLevelWidget || !win) - return YES; - - // XUL popups, e.g. the toolbar customization popup, can't become key windows, - // but controls in these windows should still get the active look. - if (topLevelWidget->WindowType() == eWindowType_popup) - return YES; - if ([win isSheet]) - return [win isKeyWindow]; - return [win isMainWindow] && ![win attachedSheet]; -} - -// Toolbar controls and content controls respond to different window -// activeness states. -static BOOL IsActive(nsIFrame* aFrame, BOOL aIsToolbarControl) -{ - if (aIsToolbarControl) - return [NativeWindowForFrame(aFrame) isMainWindow]; - return FrameIsInActiveWindow(aFrame); -} - -static bool IsInSourceList(nsIFrame* aFrame) { - for (nsIFrame* frame = aFrame->GetParent(); frame; frame = frame->GetParent()) { - if (frame->StyleDisplay()->mAppearance == NS_THEME_MAC_SOURCE_LIST) { - return true; - } - } - return false; -} - -NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeCocoa, nsNativeTheme, nsITheme) - -nsNativeThemeCocoa::nsNativeThemeCocoa() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - kMaxFocusRingWidth = nsCocoaFeatures::OnYosemiteOrLater() ? 7 : 4; - - // provide a local autorelease pool, as this is called during startup - // before the main event-loop pool is in place - nsAutoreleasePool pool; - - mDisclosureButtonCell = [[NSButtonCell alloc] initTextCell:@""]; - [mDisclosureButtonCell setBezelStyle:NSRoundedDisclosureBezelStyle]; - [mDisclosureButtonCell setButtonType:NSPushOnPushOffButton]; - [mDisclosureButtonCell setHighlightsBy:NSPushInCellMask]; - - mHelpButtonCell = [[NSButtonCell alloc] initTextCell:@""]; - [mHelpButtonCell setBezelStyle:NSHelpButtonBezelStyle]; - [mHelpButtonCell setButtonType:NSMomentaryPushInButton]; - [mHelpButtonCell setHighlightsBy:NSPushInCellMask]; - - mPushButtonCell = [[NSButtonCell alloc] initTextCell:@""]; - [mPushButtonCell setButtonType:NSMomentaryPushInButton]; - [mPushButtonCell setHighlightsBy:NSPushInCellMask]; - - mRadioButtonCell = [[RadioButtonCell alloc] initTextCell:@""]; - [mRadioButtonCell setButtonType:NSRadioButton]; - - mCheckboxCell = [[CheckboxCell alloc] initTextCell:@""]; - [mCheckboxCell setButtonType:NSSwitchButton]; - [mCheckboxCell setAllowsMixedState:YES]; - -#if defined(__x86_64__) - mSearchFieldCell = [[SearchFieldCellWithFocusRing alloc] initTextCell:@""]; -#else - mSearchFieldCell = [[ContextAwareSearchFieldCell alloc] initTextCell:@""]; -#endif - [mSearchFieldCell setBezelStyle:NSTextFieldRoundedBezel]; - [mSearchFieldCell setBezeled:YES]; - [mSearchFieldCell setEditable:YES]; - [mSearchFieldCell setFocusRingType:NSFocusRingTypeExterior]; - - mDropdownCell = [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]; - - mComboBoxCell = [[NSComboBoxCell alloc] initTextCell:@""]; - [mComboBoxCell setBezeled:YES]; - [mComboBoxCell setEditable:YES]; - [mComboBoxCell setFocusRingType:NSFocusRingTypeExterior]; - - mProgressBarCell = [[NSProgressBarCell alloc] init]; - - mMeterBarCell = [[NSLevelIndicatorCell alloc] - initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle]; - - mCellDrawView = [[CellDrawView alloc] init]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -nsNativeThemeCocoa::~nsNativeThemeCocoa() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [mMeterBarCell release]; - [mProgressBarCell release]; - [mDisclosureButtonCell release]; - [mHelpButtonCell release]; - [mPushButtonCell release]; - [mRadioButtonCell release]; - [mCheckboxCell release]; - [mSearchFieldCell release]; - [mDropdownCell release]; - [mComboBoxCell release]; - [mCellDrawView release]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// Limit on the area of the target rect (in pixels^2) in -// DrawCellWithScaling() and DrawButton() and above which we -// don't draw the object into a bitmap buffer. This is to avoid crashes in -// [NSGraphicsContext graphicsContextWithGraphicsPort:flipped:] and -// CGContextDrawImage(), and also to avoid very poor drawing performance in -// CGContextDrawImage() when it scales the bitmap (particularly if xscale or -// yscale is less than but near 1 -- e.g. 0.9). This value was determined -// by trial and error, on OS X 10.4.11 and 10.5.4, and on systems with -// different amounts of RAM. -#define BITMAP_MAX_AREA 500000 - -static int -GetBackingScaleFactorForRendering(CGContextRef cgContext) -{ - CGAffineTransform ctm = CGContextGetUserSpaceToDeviceSpaceTransform(cgContext); - CGRect transformedUserSpacePixel = CGRectApplyAffineTransform(CGRectMake(0, 0, 1, 1), ctm); - float maxScale = std::max(fabs(transformedUserSpacePixel.size.width), - fabs(transformedUserSpacePixel.size.height)); - return maxScale > 1.0 ? 2 : 1; -} - -/* - * Draw the given NSCell into the given cgContext. - * - * destRect - the size and position of the resulting control rectangle - * controlSize - the NSControlSize which will be given to the NSCell before - * asking it to render - * naturalSize - The natural dimensions of this control. - * If the control rect size is not equal to either of these, a scale - * will be applied to the context so that rendering the control at the - * natural size will result in it filling the destRect space. - * If a control has no natural dimensions in either/both axes, pass 0.0f. - * minimumSize - The minimum dimensions of this control. - * If the control rect size is less than the minimum for a given axis, - * a scale will be applied to the context so that the minimum is used - * for drawing. If a control has no minimum dimensions in either/both - * axes, pass 0.0f. - * marginSet - an array of margins; a multidimensional array of [2][3][4], - * with the first dimension being the OS version (Tiger or Leopard), - * the second being the control size (mini, small, regular), and the third - * being the 4 margin values (left, top, right, bottom). - * view - The NSView that we're drawing into. As far as I can tell, it doesn't - * matter if this is really the right view; it just has to return YES when - * asked for isFlipped. Otherwise we'll get drawing bugs on 10.4. - * mirrorHorizontal - whether to mirror the cell horizontally - */ -static void DrawCellWithScaling(NSCell *cell, - CGContextRef cgContext, - const HIRect& destRect, - NSControlSize controlSize, - NSSize naturalSize, - NSSize minimumSize, - const float marginSet[][3][4], - NSView* view, - BOOL mirrorHorizontal) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - NSRect drawRect = NSMakeRect(destRect.origin.x, destRect.origin.y, destRect.size.width, destRect.size.height); - - if (naturalSize.width != 0.0f) - drawRect.size.width = naturalSize.width; - if (naturalSize.height != 0.0f) - drawRect.size.height = naturalSize.height; - - // Keep aspect ratio when scaling if one dimension is free. - if (naturalSize.width == 0.0f && naturalSize.height != 0.0f) - drawRect.size.width = destRect.size.width * naturalSize.height / destRect.size.height; - if (naturalSize.height == 0.0f && naturalSize.width != 0.0f) - drawRect.size.height = destRect.size.height * naturalSize.width / destRect.size.width; - - // Honor minimum sizes. - if (drawRect.size.width < minimumSize.width) - drawRect.size.width = minimumSize.width; - if (drawRect.size.height < minimumSize.height) - drawRect.size.height = minimumSize.height; - - [NSGraphicsContext saveGraphicsState]; - - // Only skip the buffer if the area of our cell (in pixels^2) is too large. - if (drawRect.size.width * drawRect.size.height > BITMAP_MAX_AREA) { - // Inflate the rect Gecko gave us by the margin for the control. - InflateControlRect(&drawRect, controlSize, marginSet); - - NSGraphicsContext* savedContext = [NSGraphicsContext currentContext]; - [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]]; - - DrawCellIncludingFocusRing(cell, drawRect, view); - - [NSGraphicsContext setCurrentContext:savedContext]; - } - else { - float w = ceil(drawRect.size.width); - float h = ceil(drawRect.size.height); - NSRect tmpRect = NSMakeRect(kMaxFocusRingWidth, kMaxFocusRingWidth, w, h); - - // inflate to figure out the frame we need to tell NSCell to draw in, to get something that's 0,0,w,h - InflateControlRect(&tmpRect, controlSize, marginSet); - - // and then, expand by kMaxFocusRingWidth size to make sure we can capture any focus ring - w += kMaxFocusRingWidth * 2.0; - h += kMaxFocusRingWidth * 2.0; - - int backingScaleFactor = GetBackingScaleFactorForRendering(cgContext); - CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); - CGContextRef ctx = CGBitmapContextCreate(NULL, - (int) w * backingScaleFactor, (int) h * backingScaleFactor, - 8, (int) w * backingScaleFactor * 4, - rgb, kCGImageAlphaPremultipliedFirst); - CGColorSpaceRelease(rgb); - - // We need to flip the image twice in order to avoid drawing bugs on 10.4, see bug 465069. - // This is the first flip transform, applied to cgContext. - CGContextScaleCTM(cgContext, 1.0f, -1.0f); - CGContextTranslateCTM(cgContext, 0.0f, -(2.0 * destRect.origin.y + destRect.size.height)); - if (mirrorHorizontal) { - CGContextScaleCTM(cgContext, -1.0f, 1.0f); - CGContextTranslateCTM(cgContext, -(2.0 * destRect.origin.x + destRect.size.width), 0.0f); - } - - NSGraphicsContext* savedContext = [NSGraphicsContext currentContext]; - [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:YES]]; - - CGContextScaleCTM(ctx, backingScaleFactor, backingScaleFactor); - - // Set the context's "base transform" to in order to get correctly-sized focus rings. - CGContextSetBaseCTM(ctx, CGAffineTransformMakeScale(backingScaleFactor, backingScaleFactor)); - - // This is the second flip transform, applied to ctx. - CGContextScaleCTM(ctx, 1.0f, -1.0f); - CGContextTranslateCTM(ctx, 0.0f, -(2.0 * tmpRect.origin.y + tmpRect.size.height)); - - DrawCellIncludingFocusRing(cell, tmpRect, view); - - [NSGraphicsContext setCurrentContext:savedContext]; - - CGImageRef img = CGBitmapContextCreateImage(ctx); - - // Drop the image into the original destination rectangle, scaling to fit - // Only scale kMaxFocusRingWidth by xscale/yscale when the resulting rect - // doesn't extend beyond the overflow rect - float xscale = destRect.size.width / drawRect.size.width; - float yscale = destRect.size.height / drawRect.size.height; - float scaledFocusRingX = xscale < 1.0f ? kMaxFocusRingWidth * xscale : kMaxFocusRingWidth; - float scaledFocusRingY = yscale < 1.0f ? kMaxFocusRingWidth * yscale : kMaxFocusRingWidth; - CGContextDrawImage(cgContext, CGRectMake(destRect.origin.x - scaledFocusRingX, - destRect.origin.y - scaledFocusRingY, - destRect.size.width + scaledFocusRingX * 2, - destRect.size.height + scaledFocusRingY * 2), - img); - - CGImageRelease(img); - CGContextRelease(ctx); - } - - [NSGraphicsContext restoreGraphicsState]; - -#if DRAW_IN_FRAME_DEBUG - CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25); - CGContextFillRect(cgContext, destRect); -#endif - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -struct CellRenderSettings { - // The natural dimensions of the control. - // If a control has no natural dimensions in either/both axes, set to 0.0f. - NSSize naturalSizes[3]; - - // The minimum dimensions of the control. - // If a control has no minimum dimensions in either/both axes, set to 0.0f. - NSSize minimumSizes[3]; - - // A three-dimensional array, - // with the first dimension being the OS version ([0] 10.6-10.9, [1] 10.10 and above), - // the second being the control size (mini, small, regular), and the third - // being the 4 margin values (left, top, right, bottom). - float margins[2][3][4]; -}; - -/* - * This is a helper method that returns the required NSControlSize given a size - * and the size of the three controls plus a tolerance. - * size - The width or the height of the element to draw. - * sizes - An array with the all the width/height of the element for its - * different sizes. - * tolerance - The tolerance as passed to DrawCellWithSnapping. - * NOTE: returns NSRegularControlSize if all values in 'sizes' are zero. - */ -static NSControlSize FindControlSize(CGFloat size, const CGFloat* sizes, CGFloat tolerance) -{ - for (uint32_t i = miniControlSize; i <= regularControlSize; ++i) { - if (sizes[i] == 0) { - continue; - } - - CGFloat next = 0; - // Find next value. - for (uint32_t j = i+1; j <= regularControlSize; ++j) { - if (sizes[j] != 0) { - next = sizes[j]; - break; - } - } - - // If it's the latest value, we pick it. - if (next == 0) { - return CocoaSizeForEnum(i); - } - - if (size <= sizes[i] + tolerance && size < next) { - return CocoaSizeForEnum(i); - } - } - - // If we are here, that means sizes[] was an array with only empty values - // or the algorithm above is wrong. - // The former can happen but the later would be wrong. - NS_ASSERTION(sizes[0] == 0 && sizes[1] == 0 && sizes[2] == 0, - "We found no control! We shouldn't be there!"); - return CocoaSizeForEnum(regularControlSize); -} - -/* - * Draw the given NSCell into the given cgContext with a nice control size. - * - * This function is similar to DrawCellWithScaling, but it decides what - * control size to use based on the destRect's size. - * Scaling is only applied when the difference between the destRect's size - * and the next smaller natural size is greater than snapTolerance. Otherwise - * it snaps to the next smaller control size without scaling because unscaled - * controls look nicer. - */ -static void DrawCellWithSnapping(NSCell *cell, - CGContextRef cgContext, - const HIRect& destRect, - const CellRenderSettings settings, - float verticalAlignFactor, - NSView* view, - BOOL mirrorHorizontal, - float snapTolerance = 2.0f) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - const float rectWidth = destRect.size.width, rectHeight = destRect.size.height; - const NSSize *sizes = settings.naturalSizes; - const NSSize miniSize = sizes[EnumSizeForCocoaSize(NSMiniControlSize)]; - const NSSize smallSize = sizes[EnumSizeForCocoaSize(NSSmallControlSize)]; - const NSSize regularSize = sizes[EnumSizeForCocoaSize(NSRegularControlSize)]; - - HIRect drawRect = destRect; - - CGFloat controlWidths[3] = { miniSize.width, smallSize.width, regularSize.width }; - NSControlSize controlSizeX = FindControlSize(rectWidth, controlWidths, snapTolerance); - CGFloat controlHeights[3] = { miniSize.height, smallSize.height, regularSize.height }; - NSControlSize controlSizeY = FindControlSize(rectHeight, controlHeights, snapTolerance); - - NSControlSize controlSize = NSRegularControlSize; - size_t sizeIndex = 0; - - // At some sizes, don't scale but snap. - const NSControlSize smallerControlSize = - EnumSizeForCocoaSize(controlSizeX) < EnumSizeForCocoaSize(controlSizeY) ? - controlSizeX : controlSizeY; - const size_t smallerControlSizeIndex = EnumSizeForCocoaSize(smallerControlSize); - const NSSize size = sizes[smallerControlSizeIndex]; - float diffWidth = size.width ? rectWidth - size.width : 0.0f; - float diffHeight = size.height ? rectHeight - size.height : 0.0f; - if (diffWidth >= 0.0f && diffHeight >= 0.0f && - diffWidth <= snapTolerance && diffHeight <= snapTolerance) { - // Snap to the smaller control size. - controlSize = smallerControlSize; - sizeIndex = smallerControlSizeIndex; - MOZ_ASSERT(sizeIndex < ArrayLength(settings.naturalSizes)); - - // Resize and center the drawRect. - if (sizes[sizeIndex].width) { - drawRect.origin.x += ceil((destRect.size.width - sizes[sizeIndex].width) / 2); - drawRect.size.width = sizes[sizeIndex].width; - } - if (sizes[sizeIndex].height) { - drawRect.origin.y += floor((destRect.size.height - sizes[sizeIndex].height) * verticalAlignFactor); - drawRect.size.height = sizes[sizeIndex].height; - } - } else { - // Use the larger control size. - controlSize = EnumSizeForCocoaSize(controlSizeX) > EnumSizeForCocoaSize(controlSizeY) ? - controlSizeX : controlSizeY; - sizeIndex = EnumSizeForCocoaSize(controlSize); - } - - [cell setControlSize:controlSize]; - - MOZ_ASSERT(sizeIndex < ArrayLength(settings.minimumSizes)); - const NSSize minimumSize = settings.minimumSizes[sizeIndex]; - DrawCellWithScaling(cell, cgContext, drawRect, controlSize, sizes[sizeIndex], - minimumSize, settings.margins, view, mirrorHorizontal); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -@interface NSWindow(CoreUIRendererPrivate) -+ (CUIRendererRef)coreUIRenderer; -@end - -static id -GetAquaAppearance() -{ - // We only need NSAppearance on 10.10 and up. - if (nsCocoaFeatures::OnYosemiteOrLater()) { - Class NSAppearanceClass = NSClassFromString(@"NSAppearance"); - if (NSAppearanceClass && - [NSAppearanceClass respondsToSelector:@selector(appearanceNamed:)]) { - return [NSAppearanceClass performSelector:@selector(appearanceNamed:) - withObject:@"NSAppearanceNameAqua"]; - } - } - return nil; -} - -@interface NSObject(NSAppearanceCoreUIRendering) -- (void)_drawInRect:(CGRect)rect context:(CGContextRef)cgContext options:(id)options; -@end - -static void -RenderWithCoreUI(CGRect aRect, CGContextRef cgContext, NSDictionary* aOptions, bool aSkipAreaCheck = false) -{ - id appearance = GetAquaAppearance(); - - if (!aSkipAreaCheck && aRect.size.width * aRect.size.height > BITMAP_MAX_AREA) { - return; - } - - if (appearance && [appearance respondsToSelector:@selector(_drawInRect:context:options:)]) { - // Render through NSAppearance on Mac OS 10.10 and up. This will call - // CUIDraw with a CoreUI renderer that will give us the correct 10.10 - // style. Calling CUIDraw directly with [NSWindow coreUIRenderer] still - // renders 10.9-style widgets on 10.10. - [appearance _drawInRect:aRect context:cgContext options:aOptions]; - } else { - // 10.9 and below - CUIRendererRef renderer = [NSWindow respondsToSelector:@selector(coreUIRenderer)] - ? [NSWindow coreUIRenderer] : nil; - CUIDraw(renderer, aRect, cgContext, (CFDictionaryRef)aOptions, NULL); - } -} - -static float VerticalAlignFactor(nsIFrame *aFrame) -{ - if (!aFrame) - return 0.5f; // default: center - - const nsStyleCoord& va = aFrame->StyleDisplay()->mVerticalAlign; - uint8_t intval = (va.GetUnit() == eStyleUnit_Enumerated) - ? va.GetIntValue() - : NS_STYLE_VERTICAL_ALIGN_MIDDLE; - switch (intval) { - case NS_STYLE_VERTICAL_ALIGN_TOP: - case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP: - return 0.0f; - - case NS_STYLE_VERTICAL_ALIGN_SUB: - case NS_STYLE_VERTICAL_ALIGN_SUPER: - case NS_STYLE_VERTICAL_ALIGN_MIDDLE: - case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE: - return 0.5f; - - case NS_STYLE_VERTICAL_ALIGN_BASELINE: - case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM: - case NS_STYLE_VERTICAL_ALIGN_BOTTOM: - return 1.0f; - - default: - NS_NOTREACHED("invalid vertical-align"); - return 0.5f; - } -} - -// These are the sizes that Gecko needs to request to draw if it wants -// to get a standard-sized Aqua radio button drawn. Note that the rects -// that draw these are actually a little bigger. -static const CellRenderSettings radioSettings = { - { - NSMakeSize(11, 11), // mini - NSMakeSize(13, 13), // small - NSMakeSize(16, 16) // regular - }, - { - NSZeroSize, NSZeroSize, NSZeroSize - }, - { - { // Leopard - {0, 0, 0, 0}, // mini - {0, 1, 1, 1}, // small - {0, 0, 0, 0} // regular - }, - { // Yosemite - {0, 0, 0, 0}, // mini - {1, 1, 1, 2}, // small - {0, 0, 0, 0} // regular - } - } -}; - -static const CellRenderSettings checkboxSettings = { - { - NSMakeSize(11, 11), // mini - NSMakeSize(13, 13), // small - NSMakeSize(16, 16) // regular - }, - { - NSZeroSize, NSZeroSize, NSZeroSize - }, - { - { // Leopard - {0, 1, 0, 0}, // mini - {0, 1, 0, 1}, // small - {0, 1, 0, 1} // regular - }, - { // Yosemite - {0, 1, 0, 0}, // mini - {0, 1, 0, 1}, // small - {0, 1, 0, 1} // regular - } - } -}; - -void -nsNativeThemeCocoa::DrawCheckboxOrRadio(CGContextRef cgContext, bool inCheckbox, - const HIRect& inBoxRect, bool inSelected, - EventStates inState, nsIFrame* aFrame) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - NSButtonCell *cell = inCheckbox ? mCheckboxCell : mRadioButtonCell; - NSCellStateValue state = inSelected ? NSOnState : NSOffState; - - // Check if we have an indeterminate checkbox - if (inCheckbox && GetIndeterminate(aFrame)) - state = NSMixedState; - - [cell setEnabled:!IsDisabled(aFrame, inState)]; - [cell setShowsFirstResponder:inState.HasState(NS_EVENT_STATE_FOCUS)]; - [cell setState:state]; - [cell setHighlighted:inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)]; - [cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint] : NSClearControlTint)]; - - // Ensure that the control is square. - float length = std::min(inBoxRect.size.width, inBoxRect.size.height); - HIRect drawRect = CGRectMake(inBoxRect.origin.x + (int)((inBoxRect.size.width - length) / 2.0f), - inBoxRect.origin.y + (int)((inBoxRect.size.height - length) / 2.0f), - length, length); - - DrawCellWithSnapping(cell, cgContext, drawRect, - inCheckbox ? checkboxSettings : radioSettings, - VerticalAlignFactor(aFrame), mCellDrawView, NO); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -static const CellRenderSettings searchFieldSettings = { - { - NSMakeSize(0, 16), // mini - NSMakeSize(0, 19), // small - NSMakeSize(0, 22) // regular - }, - { - NSMakeSize(32, 0), // mini - NSMakeSize(38, 0), // small - NSMakeSize(44, 0) // regular - }, - { - { // Leopard - {0, 0, 0, 0}, // mini - {0, 0, 0, 0}, // small - {0, 0, 0, 0} // regular - }, - { // Yosemite - {0, 0, 0, 0}, // mini - {0, 0, 0, 0}, // small - {0, 0, 0, 0} // regular - } - } -}; - -void -nsNativeThemeCocoa::DrawSearchField(CGContextRef cgContext, const HIRect& inBoxRect, - nsIFrame* aFrame, EventStates inState) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - ContextAwareSearchFieldCell* cell = mSearchFieldCell; - [cell setContext:aFrame]; - [cell setEnabled:!IsDisabled(aFrame, inState)]; - // NOTE: this could probably use inState - [cell setShowsFirstResponder:IsFocused(aFrame)]; - - // When using the 10.11 SDK, the default string will be shown if we don't - // set the placeholder string. - [cell setPlaceholderString:@""]; - - DrawCellWithSnapping(cell, cgContext, inBoxRect, searchFieldSettings, - VerticalAlignFactor(aFrame), mCellDrawView, - IsFrameRTL(aFrame)); - - [cell setContext:nullptr]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -static const NSSize kCheckmarkSize = NSMakeSize(11, 11); -static const NSSize kMenuarrowSize = NSMakeSize(9, 10); -static const NSSize kMenuScrollArrowSize = NSMakeSize(10, 8); -static NSString* kCheckmarkImage = @"MenuOnState"; -static NSString* kMenuarrowRightImage = @"MenuSubmenu"; -static NSString* kMenuarrowLeftImage = @"MenuSubmenuLeft"; -static NSString* kMenuDownScrollArrowImage = @"MenuScrollDown"; -static NSString* kMenuUpScrollArrowImage = @"MenuScrollUp"; -static const CGFloat kMenuIconIndent = 6.0f; - -void -nsNativeThemeCocoa::DrawMenuIcon(CGContextRef cgContext, const CGRect& aRect, - EventStates inState, nsIFrame* aFrame, - const NSSize& aIconSize, NSString* aImageName, - bool aCenterHorizontally) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Adjust size and position of our drawRect. - CGFloat paddingX = std::max(CGFloat(0.0), aRect.size.width - aIconSize.width); - CGFloat paddingY = std::max(CGFloat(0.0), aRect.size.height - aIconSize.height); - CGFloat paddingStartX = std::min(paddingX, kMenuIconIndent); - CGFloat paddingEndX = std::max(CGFloat(0.0), paddingX - kMenuIconIndent); - CGRect drawRect = CGRectMake( - aRect.origin.x + (aCenterHorizontally ? ceil(paddingX / 2) : - IsFrameRTL(aFrame) ? paddingEndX : paddingStartX), - aRect.origin.y + ceil(paddingY / 2), - aIconSize.width, aIconSize.height); - - NSString* state = IsDisabled(aFrame, inState) ? @"disabled" : - (CheckBooleanAttr(aFrame, nsGkAtoms::menuactive) ? @"pressed" : @"normal"); - - NSString* imageName = aImageName; - if (!nsCocoaFeatures::OnElCapitanOrLater()) { - // Pre-10.11, image names are prefixed with "image." - imageName = [@"image." stringByAppendingString:aImageName]; - } - - RenderWithCoreUI(drawRect, cgContext, - [NSDictionary dictionaryWithObjectsAndKeys: - @"kCUIBackgroundTypeMenu", @"backgroundTypeKey", - imageName, @"imageNameKey", - state, @"state", - @"image", @"widget", - [NSNumber numberWithBool:YES], @"is.flipped", - nil]); - -#if DRAW_IN_FRAME_DEBUG - CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25); - CGContextFillRect(cgContext, drawRect); -#endif - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -static const NSSize kHelpButtonSize = NSMakeSize(20, 20); -static const NSSize kDisclosureButtonSize = NSMakeSize(21, 21); - -static const CellRenderSettings pushButtonSettings = { - { - NSMakeSize(0, 16), // mini - NSMakeSize(0, 19), // small - NSMakeSize(0, 22) // regular - }, - { - NSMakeSize(18, 0), // mini - NSMakeSize(26, 0), // small - NSMakeSize(30, 0) // regular - }, - { - { // Leopard - {0, 0, 0, 0}, // mini - {4, 0, 4, 1}, // small - {5, 0, 5, 2} // regular - }, - { // Yosemite - {0, 0, 0, 0}, // mini - {4, 0, 4, 1}, // small - {5, 0, 5, 2} // regular - } - } -}; - -// The height at which we start doing square buttons instead of rounded buttons -// Rounded buttons look bad if drawn at a height greater than 26, so at that point -// we switch over to doing square buttons which looks fine at any size. -#define DO_SQUARE_BUTTON_HEIGHT 26 - -void -nsNativeThemeCocoa::DrawPushButton(CGContextRef cgContext, const HIRect& inBoxRect, - EventStates inState, uint8_t aWidgetType, - nsIFrame* aFrame, float aOriginalHeight) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - BOOL isActive = FrameIsInActiveWindow(aFrame); - BOOL isDisabled = IsDisabled(aFrame, inState); - - NSButtonCell* cell = (aWidgetType == NS_THEME_BUTTON) ? mPushButtonCell : - (aWidgetType == NS_THEME_MAC_HELP_BUTTON) ? mHelpButtonCell : mDisclosureButtonCell; - [cell setEnabled:!isDisabled]; - [cell setHighlighted:isActive && - inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)]; - [cell setShowsFirstResponder:inState.HasState(NS_EVENT_STATE_FOCUS) && !isDisabled && isActive]; - - if (aWidgetType != NS_THEME_BUTTON) { // Help button or disclosure button. - NSSize buttonSize = NSMakeSize(0, 0); - if (aWidgetType == NS_THEME_MAC_HELP_BUTTON) { - buttonSize = kHelpButtonSize; - } else { // Disclosure button. - buttonSize = kDisclosureButtonSize; - [cell setState:(aWidgetType == NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED) ? NSOffState : NSOnState]; - } - - DrawCellWithScaling(cell, cgContext, inBoxRect, NSRegularControlSize, - NSZeroSize, buttonSize, NULL, mCellDrawView, - false); // Don't mirror icon in RTL. - } else { - // If the button is tall enough, draw the square button style so that - // buttons with non-standard content look good. Otherwise draw normal - // rounded aqua buttons. - // This comparison is done based on the height that is calculated without - // the top, because the snapped height can be affected by the top of the - // rect and that may result in different height depending on the top value. - if (aOriginalHeight > DO_SQUARE_BUTTON_HEIGHT) { - [cell setBezelStyle:NSShadowlessSquareBezelStyle]; - DrawCellWithScaling(cell, cgContext, inBoxRect, NSRegularControlSize, - NSZeroSize, NSMakeSize(14, 0), NULL, mCellDrawView, - IsFrameRTL(aFrame)); - } else { - [cell setBezelStyle:NSRoundedBezelStyle]; - DrawCellWithSnapping(cell, cgContext, inBoxRect, pushButtonSettings, 0.5f, - mCellDrawView, IsFrameRTL(aFrame), 1.0f); - } - } - -#if DRAW_IN_FRAME_DEBUG - CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25); - CGContextFillRect(cgContext, inBoxRect); -#endif - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -nsNativeThemeCocoa::DrawFocusOutline(CGContextRef cgContext, const HIRect& inBoxRect, - EventStates inState, uint8_t aWidgetType, - nsIFrame* aFrame) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - HIThemeFrameDrawInfo fdi; - fdi.version = 0; - fdi.kind = kHIThemeFrameTextFieldSquare; - fdi.state = kThemeStateActive; - fdi.isFocused = TRUE; - -#if DRAW_IN_FRAME_DEBUG - CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25); - CGContextFillRect(cgContext, inBoxRect); -#endif - - HIThemeDrawFrame(&inBoxRect, &fdi, cgContext, HITHEME_ORIENTATION); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -typedef void (*RenderHIThemeControlFunction)(CGContextRef cgContext, const HIRect& aRenderRect, void* aData); - -static void -RenderTransformedHIThemeControl(CGContextRef aCGContext, const HIRect& aRect, - RenderHIThemeControlFunction aFunc, void* aData, - BOOL mirrorHorizontally = NO) -{ - CGAffineTransform savedCTM = CGContextGetCTM(aCGContext); - CGContextTranslateCTM(aCGContext, aRect.origin.x, aRect.origin.y); - - bool drawDirect; - HIRect drawRect = aRect; - drawRect.origin = CGPointZero; - - if (!mirrorHorizontally && savedCTM.a == 1.0f && savedCTM.b == 0.0f && - savedCTM.c == 0.0f && (savedCTM.d == 1.0f || savedCTM.d == -1.0f)) { - drawDirect = TRUE; - } else { - drawDirect = FALSE; - } - - // Fall back to no bitmap buffer if the area of our control (in pixels^2) - // is too large. - if (drawDirect || (aRect.size.width * aRect.size.height > BITMAP_MAX_AREA)) { - aFunc(aCGContext, drawRect, aData); - } else { - // Inflate the buffer to capture focus rings. - int w = ceil(drawRect.size.width) + 2 * kMaxFocusRingWidth; - int h = ceil(drawRect.size.height) + 2 * kMaxFocusRingWidth; - - int backingScaleFactor = GetBackingScaleFactorForRendering(aCGContext); - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - CGContextRef bitmapctx = CGBitmapContextCreate(NULL, - w * backingScaleFactor, - h * backingScaleFactor, - 8, - w * backingScaleFactor * 4, - colorSpace, - kCGImageAlphaPremultipliedFirst); - CGColorSpaceRelease(colorSpace); - - CGContextScaleCTM(bitmapctx, backingScaleFactor, backingScaleFactor); - CGContextTranslateCTM(bitmapctx, kMaxFocusRingWidth, kMaxFocusRingWidth); - - // Set the context's "base transform" to in order to get correctly-sized focus rings. - CGContextSetBaseCTM(bitmapctx, CGAffineTransformMakeScale(backingScaleFactor, backingScaleFactor)); - - // HITheme always wants to draw into a flipped context, or things - // get confused. - CGContextTranslateCTM(bitmapctx, 0.0f, aRect.size.height); - CGContextScaleCTM(bitmapctx, 1.0f, -1.0f); - - aFunc(bitmapctx, drawRect, aData); - - CGImageRef bitmap = CGBitmapContextCreateImage(bitmapctx); - - CGAffineTransform ctm = CGContextGetCTM(aCGContext); - - // We need to unflip, so that we can do a DrawImage without getting a flipped image. - CGContextTranslateCTM(aCGContext, 0.0f, aRect.size.height); - CGContextScaleCTM(aCGContext, 1.0f, -1.0f); - - if (mirrorHorizontally) { - CGContextTranslateCTM(aCGContext, aRect.size.width, 0); - CGContextScaleCTM(aCGContext, -1.0f, 1.0f); - } - - HIRect inflatedDrawRect = CGRectMake(-kMaxFocusRingWidth, -kMaxFocusRingWidth, w, h); - CGContextDrawImage(aCGContext, inflatedDrawRect, bitmap); - - CGContextSetCTM(aCGContext, ctm); - - CGImageRelease(bitmap); - CGContextRelease(bitmapctx); - } - - CGContextSetCTM(aCGContext, savedCTM); -} - -static void -RenderButton(CGContextRef cgContext, const HIRect& aRenderRect, void* aData) -{ - HIThemeButtonDrawInfo* bdi = (HIThemeButtonDrawInfo*)aData; - HIThemeDrawButton(&aRenderRect, bdi, cgContext, kHIThemeOrientationNormal, NULL); -} - -void -nsNativeThemeCocoa::DrawButton(CGContextRef cgContext, ThemeButtonKind inKind, - const HIRect& inBoxRect, bool inIsDefault, - ThemeButtonValue inValue, ThemeButtonAdornment inAdornment, - EventStates inState, nsIFrame* aFrame) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - BOOL isActive = FrameIsInActiveWindow(aFrame); - BOOL isDisabled = IsDisabled(aFrame, inState); - - HIThemeButtonDrawInfo bdi; - bdi.version = 0; - bdi.kind = inKind; - bdi.value = inValue; - bdi.adornment = inAdornment; - - if (isDisabled) { - bdi.state = kThemeStateUnavailable; - } - else if (inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)) { - bdi.state = kThemeStatePressed; - } - else { - if (inKind == kThemeArrowButton) - bdi.state = kThemeStateUnavailable; // these are always drawn as unavailable - else if (!isActive && inKind == kThemeListHeaderButton) - bdi.state = kThemeStateInactive; - else - bdi.state = kThemeStateActive; - } - - if (inState.HasState(NS_EVENT_STATE_FOCUS) && isActive) - bdi.adornment |= kThemeAdornmentFocus; - - if (inIsDefault && !isDisabled && - !inState.HasState(NS_EVENT_STATE_ACTIVE)) { - bdi.adornment |= kThemeAdornmentDefault; - bdi.animation.time.start = 0; - bdi.animation.time.current = CFAbsoluteTimeGetCurrent(); - } - - HIRect drawFrame = inBoxRect; - - if (inKind == kThemePushButton) { - drawFrame.size.height -= 2; - if (inBoxRect.size.height < pushButtonSettings.naturalSizes[smallControlSize].height) { - bdi.kind = kThemePushButtonMini; - } - else if (inBoxRect.size.height < pushButtonSettings.naturalSizes[regularControlSize].height) { - bdi.kind = kThemePushButtonSmall; - drawFrame.origin.y -= 1; - drawFrame.origin.x += 1; - drawFrame.size.width -= 2; - } - } - else if (inKind == kThemeListHeaderButton) { - CGContextClipToRect(cgContext, inBoxRect); - // Always remove the top border. - drawFrame.origin.y -= 1; - drawFrame.size.height += 1; - // Remove the left border in LTR mode and the right border in RTL mode. - drawFrame.size.width += 1; - bool isLast = IsLastTreeHeaderCell(aFrame); - if (isLast) - drawFrame.size.width += 1; // Also remove the other border. - if (!IsFrameRTL(aFrame) || isLast) - drawFrame.origin.x -= 1; - } - - RenderTransformedHIThemeControl(cgContext, drawFrame, RenderButton, &bdi, - IsFrameRTL(aFrame)); - -#if DRAW_IN_FRAME_DEBUG - CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25); - CGContextFillRect(cgContext, inBoxRect); -#endif - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -static const CellRenderSettings dropdownSettings = { - { - NSMakeSize(0, 16), // mini - NSMakeSize(0, 19), // small - NSMakeSize(0, 22) // regular - }, - { - NSMakeSize(18, 0), // mini - NSMakeSize(38, 0), // small - NSMakeSize(44, 0) // regular - }, - { - { // Leopard - {1, 1, 2, 1}, // mini - {3, 0, 3, 1}, // small - {3, 0, 3, 0} // regular - }, - { // Yosemite - {1, 1, 2, 1}, // mini - {3, 0, 3, 1}, // small - {3, 0, 3, 0} // regular - } - } -}; - -static const CellRenderSettings editableMenulistSettings = { - { - NSMakeSize(0, 15), // mini - NSMakeSize(0, 18), // small - NSMakeSize(0, 21) // regular - }, - { - NSMakeSize(18, 0), // mini - NSMakeSize(38, 0), // small - NSMakeSize(44, 0) // regular - }, - { - { // Leopard - {0, 0, 2, 2}, // mini - {0, 0, 3, 2}, // small - {0, 1, 3, 3} // regular - }, - { // Yosemite - {0, 0, 2, 2}, // mini - {0, 0, 3, 2}, // small - {0, 1, 3, 3} // regular - } - } -}; - -void -nsNativeThemeCocoa::DrawDropdown(CGContextRef cgContext, const HIRect& inBoxRect, - EventStates inState, uint8_t aWidgetType, - nsIFrame* aFrame) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [mDropdownCell setPullsDown:(aWidgetType == NS_THEME_BUTTON)]; - - BOOL isEditable = (aWidgetType == NS_THEME_MENULIST_TEXTFIELD); - NSCell* cell = isEditable ? (NSCell*)mComboBoxCell : (NSCell*)mDropdownCell; - - [cell setEnabled:!IsDisabled(aFrame, inState)]; - [cell setShowsFirstResponder:(IsFocused(aFrame) || inState.HasState(NS_EVENT_STATE_FOCUS))]; - [cell setHighlighted:IsOpenButton(aFrame)]; - [cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint] : NSClearControlTint)]; - - const CellRenderSettings& settings = isEditable ? editableMenulistSettings : dropdownSettings; - DrawCellWithSnapping(cell, cgContext, inBoxRect, settings, - 0.5f, mCellDrawView, IsFrameRTL(aFrame)); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -static const CellRenderSettings spinnerSettings = { - { - NSMakeSize(11, 16), // mini (width trimmed by 2px to reduce blank border) - NSMakeSize(15, 22), // small - NSMakeSize(19, 27) // regular - }, - { - NSMakeSize(11, 16), // mini (width trimmed by 2px to reduce blank border) - NSMakeSize(15, 22), // small - NSMakeSize(19, 27) // regular - }, - { - { // Leopard - {0, 0, 0, 0}, // mini - {0, 0, 0, 0}, // small - {0, 0, 0, 0} // regular - }, - { // Yosemite - {0, 0, 0, 0}, // mini - {0, 0, 0, 0}, // small - {0, 0, 0, 0} // regular - } - } -}; - -void -nsNativeThemeCocoa::DrawSpinButtons(CGContextRef cgContext, ThemeButtonKind inKind, - const HIRect& inBoxRect, ThemeDrawState inDrawState, - ThemeButtonAdornment inAdornment, - EventStates inState, nsIFrame* aFrame) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - HIThemeButtonDrawInfo bdi; - bdi.version = 0; - bdi.kind = inKind; - bdi.value = kThemeButtonOff; - bdi.adornment = inAdornment; - - if (IsDisabled(aFrame, inState)) - bdi.state = kThemeStateUnavailable; - else - bdi.state = FrameIsInActiveWindow(aFrame) ? inDrawState : kThemeStateActive; - - HIThemeDrawButton(&inBoxRect, &bdi, cgContext, HITHEME_ORIENTATION, NULL); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -nsNativeThemeCocoa::DrawSpinButton(CGContextRef cgContext, - ThemeButtonKind inKind, - const HIRect& inBoxRect, - ThemeDrawState inDrawState, - ThemeButtonAdornment inAdornment, - EventStates inState, - nsIFrame* aFrame, - uint8_t aWidgetType) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - MOZ_ASSERT(aWidgetType == NS_THEME_SPINNER_UPBUTTON || - aWidgetType == NS_THEME_SPINNER_DOWNBUTTON); - - HIThemeButtonDrawInfo bdi; - bdi.version = 0; - bdi.kind = inKind; - bdi.value = kThemeButtonOff; - bdi.adornment = inAdornment; - - if (IsDisabled(aFrame, inState)) - bdi.state = kThemeStateUnavailable; - else - bdi.state = FrameIsInActiveWindow(aFrame) ? inDrawState : kThemeStateActive; - - // Cocoa only allows kThemeIncDecButton to paint the up and down spin buttons - // together as a single unit (presumably because when one button is active, - // the appearance of both changes (in different ways)). Here we have to paint - // both buttons, using clip to hide the one we don't want to paint. - HIRect drawRect = inBoxRect; - drawRect.size.height *= 2; - if (aWidgetType == NS_THEME_SPINNER_DOWNBUTTON) { - drawRect.origin.y -= inBoxRect.size.height; - } - - // Shift the drawing a little to the left, since cocoa paints with more - // blank space around the visual buttons than we'd like: - drawRect.origin.x -= 1; - - CGContextSaveGState(cgContext); - CGContextClipToRect(cgContext, inBoxRect); - - HIThemeDrawButton(&drawRect, &bdi, cgContext, HITHEME_ORIENTATION, NULL); - - CGContextRestoreGState(cgContext); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -nsNativeThemeCocoa::DrawFrame(CGContextRef cgContext, HIThemeFrameKind inKind, - const HIRect& inBoxRect, bool inDisabled, - EventStates inState) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - HIThemeFrameDrawInfo fdi; - fdi.version = 0; - fdi.kind = inKind; - - // We don't ever set an inactive state for this because it doesn't - // look right (see other apps). - fdi.state = inDisabled ? kThemeStateUnavailable : kThemeStateActive; - - // for some reason focus rings on listboxes draw incorrectly - if (inKind == kHIThemeFrameListBox) - fdi.isFocused = 0; - else - fdi.isFocused = inState.HasState(NS_EVENT_STATE_FOCUS); - - // HIThemeDrawFrame takes the rect for the content area of the frame, not - // the bounding rect for the frame. Here we reduce the size of the rect we - // will pass to make it the size of the content. - HIRect drawRect = inBoxRect; - if (inKind == kHIThemeFrameTextFieldSquare) { - SInt32 frameOutset = 0; - ::GetThemeMetric(kThemeMetricEditTextFrameOutset, &frameOutset); - drawRect.origin.x += frameOutset; - drawRect.origin.y += frameOutset; - drawRect.size.width -= frameOutset * 2; - drawRect.size.height -= frameOutset * 2; - } - else if (inKind == kHIThemeFrameListBox) { - SInt32 frameOutset = 0; - ::GetThemeMetric(kThemeMetricListBoxFrameOutset, &frameOutset); - drawRect.origin.x += frameOutset; - drawRect.origin.y += frameOutset; - drawRect.size.width -= frameOutset * 2; - drawRect.size.height -= frameOutset * 2; - } - -#if DRAW_IN_FRAME_DEBUG - CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25); - CGContextFillRect(cgContext, inBoxRect); -#endif - - HIThemeDrawFrame(&drawRect, &fdi, cgContext, HITHEME_ORIENTATION); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -static const CellRenderSettings progressSettings[2][2] = { - // Vertical progress bar. - { - // Determined settings. - { - { - NSZeroSize, // mini - NSMakeSize(10, 0), // small - NSMakeSize(16, 0) // regular - }, - { - NSZeroSize, NSZeroSize, NSZeroSize - }, - { - { // Leopard - {0, 0, 0, 0}, // mini - {1, 1, 1, 1}, // small - {1, 1, 1, 1} // regular - } - } - }, - // There is no horizontal margin in regular undetermined size. - { - { - NSZeroSize, // mini - NSMakeSize(10, 0), // small - NSMakeSize(16, 0) // regular - }, - { - NSZeroSize, NSZeroSize, NSZeroSize - }, - { - { // Leopard - {0, 0, 0, 0}, // mini - {1, 1, 1, 1}, // small - {1, 0, 1, 0} // regular - }, - { // Yosemite - {0, 0, 0, 0}, // mini - {1, 1, 1, 1}, // small - {1, 0, 1, 0} // regular - } - } - } - }, - // Horizontal progress bar. - { - // Determined settings. - { - { - NSZeroSize, // mini - NSMakeSize(0, 10), // small - NSMakeSize(0, 16) // regular - }, - { - NSZeroSize, NSZeroSize, NSZeroSize - }, - { - { // Leopard - {0, 0, 0, 0}, // mini - {1, 1, 1, 1}, // small - {1, 1, 1, 1} // regular - }, - { // Yosemite - {0, 0, 0, 0}, // mini - {1, 1, 1, 1}, // small - {1, 1, 1, 1} // regular - } - } - }, - // There is no horizontal margin in regular undetermined size. - { - { - NSZeroSize, // mini - NSMakeSize(0, 10), // small - NSMakeSize(0, 16) // regular - }, - { - NSZeroSize, NSZeroSize, NSZeroSize - }, - { - { // Leopard - {0, 0, 0, 0}, // mini - {1, 1, 1, 1}, // small - {0, 1, 0, 1} // regular - }, - { // Yosemite - {0, 0, 0, 0}, // mini - {1, 1, 1, 1}, // small - {0, 1, 0, 1} // regular - } - } - } - } -}; - -void -nsNativeThemeCocoa::DrawProgress(CGContextRef cgContext, const HIRect& inBoxRect, - bool inIsIndeterminate, bool inIsHorizontal, - double inValue, double inMaxValue, - nsIFrame* aFrame) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - NSProgressBarCell* cell = mProgressBarCell; - - [cell setValue:inValue]; - [cell setMax:inMaxValue]; - [cell setIndeterminate:inIsIndeterminate]; - [cell setHorizontal:inIsHorizontal]; - [cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint] - : NSClearControlTint)]; - - DrawCellWithSnapping(cell, cgContext, inBoxRect, - progressSettings[inIsHorizontal][inIsIndeterminate], - VerticalAlignFactor(aFrame), mCellDrawView, - IsFrameRTL(aFrame)); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -static const CellRenderSettings meterSetting = { - { - NSMakeSize(0, 16), // mini - NSMakeSize(0, 16), // small - NSMakeSize(0, 16) // regular - }, - { - NSZeroSize, NSZeroSize, NSZeroSize - }, - { - { // Leopard - {1, 1, 1, 1}, // mini - {1, 1, 1, 1}, // small - {1, 1, 1, 1} // regular - }, - { // Yosemite - {1, 1, 1, 1}, // mini - {1, 1, 1, 1}, // small - {1, 1, 1, 1} // regular - } - } -}; - -void -nsNativeThemeCocoa::DrawMeter(CGContextRef cgContext, const HIRect& inBoxRect, - nsIFrame* aFrame) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK - - NS_PRECONDITION(aFrame, "aFrame should not be null here!"); - - // When using -moz-meterbar on an non meter element, we will not be able to - // get all the needed information so we just draw an empty meter. - nsIContent* content = aFrame->GetContent(); - if (!(content && content->IsHTMLElement(nsGkAtoms::meter))) { - DrawCellWithSnapping(mMeterBarCell, cgContext, inBoxRect, - meterSetting, VerticalAlignFactor(aFrame), - mCellDrawView, IsFrameRTL(aFrame)); - return; - } - - HTMLMeterElement* meterElement = static_cast<HTMLMeterElement*>(content); - double value = meterElement->Value(); - double min = meterElement->Min(); - double max = meterElement->Max(); - - NSLevelIndicatorCell* cell = mMeterBarCell; - - [cell setMinValue:min]; - [cell setMaxValue:max]; - [cell setDoubleValue:value]; - - /** - * The way HTML and Cocoa defines the meter/indicator widget are different. - * So, we are going to use a trick to get the Cocoa widget showing what we - * are expecting: we set the warningValue or criticalValue to the current - * value when we want to have the widget to be in the warning or critical - * state. - */ - EventStates states = aFrame->GetContent()->AsElement()->State(); - - // Reset previously set warning and critical values. - [cell setWarningValue:max+1]; - [cell setCriticalValue:max+1]; - - if (states.HasState(NS_EVENT_STATE_SUB_OPTIMUM)) { - [cell setWarningValue:value]; - } else if (states.HasState(NS_EVENT_STATE_SUB_SUB_OPTIMUM)) { - [cell setCriticalValue:value]; - } - - HIRect rect = CGRectStandardize(inBoxRect); - BOOL vertical = IsVerticalMeter(aFrame); - - CGContextSaveGState(cgContext); - - if (vertical) { - /** - * Cocoa doesn't provide a vertical meter bar so to show one, we have to - * show a rotated horizontal meter bar. - * Given that we want to show a vertical meter bar, we assume that the rect - * has vertical dimensions but we can't correctly draw a meter widget inside - * such a rectangle so we need to inverse width and height (and re-position) - * to get a rectangle with horizontal dimensions. - * Finally, we want to show a vertical meter so we want to rotate the result - * so it is vertical. We do that by changing the context. - */ - CGFloat tmp = rect.size.width; - rect.size.width = rect.size.height; - rect.size.height = tmp; - rect.origin.x += rect.size.height / 2.f - rect.size.width / 2.f; - rect.origin.y += rect.size.width / 2.f - rect.size.height / 2.f; - - CGContextTranslateCTM(cgContext, CGRectGetMidX(rect), CGRectGetMidY(rect)); - CGContextRotateCTM(cgContext, -M_PI / 2.f); - CGContextTranslateCTM(cgContext, -CGRectGetMidX(rect), -CGRectGetMidY(rect)); - } - - DrawCellWithSnapping(cell, cgContext, rect, - meterSetting, VerticalAlignFactor(aFrame), - mCellDrawView, !vertical && IsFrameRTL(aFrame)); - - CGContextRestoreGState(cgContext); - - NS_OBJC_END_TRY_ABORT_BLOCK -} - -void -nsNativeThemeCocoa::DrawTabPanel(CGContextRef cgContext, const HIRect& inBoxRect, - nsIFrame* aFrame) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - HIThemeTabPaneDrawInfo tpdi; - - tpdi.version = 1; - tpdi.state = FrameIsInActiveWindow(aFrame) ? kThemeStateActive : kThemeStateInactive; - tpdi.direction = kThemeTabNorth; - tpdi.size = kHIThemeTabSizeNormal; - tpdi.kind = kHIThemeTabKindNormal; - - HIThemeDrawTabPane(&inBoxRect, &tpdi, cgContext, HITHEME_ORIENTATION); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -nsNativeThemeCocoa::DrawScale(CGContextRef cgContext, const HIRect& inBoxRect, - EventStates inState, bool inIsVertical, - bool inIsReverse, int32_t inCurrentValue, - int32_t inMinValue, int32_t inMaxValue, - nsIFrame* aFrame) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - HIThemeTrackDrawInfo tdi; - - tdi.version = 0; - tdi.kind = kThemeMediumSlider; - tdi.bounds = inBoxRect; - tdi.min = inMinValue; - tdi.max = inMaxValue; - tdi.value = inCurrentValue; - tdi.attributes = kThemeTrackShowThumb; - if (!inIsVertical) - tdi.attributes |= kThemeTrackHorizontal; - if (inIsReverse) - tdi.attributes |= kThemeTrackRightToLeft; - if (inState.HasState(NS_EVENT_STATE_FOCUS)) - tdi.attributes |= kThemeTrackHasFocus; - if (IsDisabled(aFrame, inState)) - tdi.enableState = kThemeTrackDisabled; - else - tdi.enableState = FrameIsInActiveWindow(aFrame) ? kThemeTrackActive : kThemeTrackInactive; - tdi.trackInfo.slider.thumbDir = kThemeThumbPlain; - tdi.trackInfo.slider.pressState = 0; - - HIThemeDrawTrack(&tdi, NULL, cgContext, HITHEME_ORIENTATION); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -nsIFrame* -nsNativeThemeCocoa::SeparatorResponsibility(nsIFrame* aBefore, nsIFrame* aAfter) -{ - // Usually a separator is drawn by the segment to the right of the - // separator, but pressed and selected segments have higher priority. - if (!aBefore || !aAfter) - return nullptr; - if (IsSelectedButton(aAfter)) - return aAfter; - if (IsSelectedButton(aBefore) || IsPressedButton(aBefore)) - return aBefore; - return aAfter; -} - -CGRect -nsNativeThemeCocoa::SeparatorAdjustedRect(CGRect aRect, nsIFrame* aLeft, - nsIFrame* aCurrent, nsIFrame* aRight) -{ - // A separator between two segments should always be located in the leftmost - // pixel column of the segment to the right of the separator, regardless of - // who ends up drawing it. - // CoreUI draws the separators inside the drawing rect. - if (aLeft && SeparatorResponsibility(aLeft, aCurrent) == aLeft) { - // The left button draws the separator, so we need to make room for it. - aRect.origin.x += 1; - aRect.size.width -= 1; - } - if (SeparatorResponsibility(aCurrent, aRight) == aCurrent) { - // We draw the right separator, so we need to extend the draw rect into the - // segment to our right. - aRect.size.width += 1; - } - return aRect; -} - -static NSString* ToolbarButtonPosition(BOOL aIsFirst, BOOL aIsLast) -{ - if (aIsFirst) { - if (aIsLast) - return @"kCUISegmentPositionOnly"; - return @"kCUISegmentPositionFirst"; - } - if (aIsLast) - return @"kCUISegmentPositionLast"; - return @"kCUISegmentPositionMiddle"; -} - -struct SegmentedControlRenderSettings { - const CGFloat* heights; - const NSString* widgetName; - const BOOL ignoresPressedWhenSelected; - const BOOL isToolbarControl; -}; - -static const CGFloat tabHeights[3] = { 17, 20, 23 }; - -static const SegmentedControlRenderSettings tabRenderSettings = { - tabHeights, @"tab", YES, NO -}; - -static const CGFloat toolbarButtonHeights[3] = { 15, 18, 22 }; - -static const SegmentedControlRenderSettings toolbarButtonRenderSettings = { - toolbarButtonHeights, @"kCUIWidgetButtonSegmentedSCurve", NO, YES -}; - -void -nsNativeThemeCocoa::DrawSegment(CGContextRef cgContext, const HIRect& inBoxRect, - EventStates inState, nsIFrame* aFrame, - const SegmentedControlRenderSettings& aSettings) -{ - BOOL isActive = IsActive(aFrame, aSettings.isToolbarControl); - BOOL isFocused = inState.HasState(NS_EVENT_STATE_FOCUS); - BOOL isSelected = IsSelectedButton(aFrame); - BOOL isPressed = IsPressedButton(aFrame); - if (isSelected && aSettings.ignoresPressedWhenSelected) { - isPressed = NO; - } - - BOOL isRTL = IsFrameRTL(aFrame); - nsIFrame* left = GetAdjacentSiblingFrameWithSameAppearance(aFrame, isRTL); - nsIFrame* right = GetAdjacentSiblingFrameWithSameAppearance(aFrame, !isRTL); - CGRect drawRect = SeparatorAdjustedRect(inBoxRect, left, aFrame, right); - BOOL drawLeftSeparator = SeparatorResponsibility(left, aFrame) == aFrame; - BOOL drawRightSeparator = SeparatorResponsibility(aFrame, right) == aFrame; - NSControlSize controlSize = FindControlSize(drawRect.size.height, aSettings.heights, 4.0f); - - RenderWithCoreUI(drawRect, cgContext, [NSDictionary dictionaryWithObjectsAndKeys: - aSettings.widgetName, @"widget", - (isActive ? @"kCUIPresentationStateActiveKey" : @"kCUIPresentationStateInactive"), @"kCUIPresentationStateKey", - ToolbarButtonPosition(!left, !right), @"kCUIPositionKey", - [NSNumber numberWithBool:drawLeftSeparator], @"kCUISegmentLeadingSeparatorKey", - [NSNumber numberWithBool:drawRightSeparator], @"kCUISegmentTrailingSeparatorKey", - [NSNumber numberWithBool:isSelected], @"value", - (isPressed ? @"pressed" : (isActive ? @"normal" : @"inactive")), @"state", - [NSNumber numberWithBool:isFocused], @"focus", - CUIControlSizeForCocoaSize(controlSize), @"size", - [NSNumber numberWithBool:YES], @"is.flipped", - @"up", @"direction", - nil]); -} - -void -nsNativeThemeCocoa::GetScrollbarPressStates(nsIFrame* aFrame, - EventStates aButtonStates[]) -{ - static nsIContent::AttrValuesArray attributeValues[] = { - &nsGkAtoms::scrollbarUpTop, - &nsGkAtoms::scrollbarDownTop, - &nsGkAtoms::scrollbarUpBottom, - &nsGkAtoms::scrollbarDownBottom, - nullptr - }; - - // Get the state of any scrollbar buttons in our child frames - for (nsIFrame *childFrame : aFrame->PrincipalChildList()) { - nsIContent *childContent = childFrame->GetContent(); - if (!childContent) continue; - int32_t attrIndex = childContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::sbattr, - attributeValues, eCaseMatters); - if (attrIndex < 0) continue; - - aButtonStates[attrIndex] = GetContentState(childFrame, NS_THEME_BUTTON); - } -} - -nsIFrame* -nsNativeThemeCocoa::GetParentScrollbarFrame(nsIFrame *aFrame) -{ - // Walk our parents to find a scrollbar frame - nsIFrame *scrollbarFrame = aFrame; - do { - if (scrollbarFrame->GetType() == nsGkAtoms::scrollbarFrame) break; - } while ((scrollbarFrame = scrollbarFrame->GetParent())); - - // We return null if we can't find a parent scrollbar frame - return scrollbarFrame; -} - -static bool -ToolbarCanBeUnified(CGContextRef cgContext, const HIRect& inBoxRect, NSWindow* aWindow) -{ - if (![aWindow isKindOfClass:[ToolbarWindow class]]) - return false; - - ToolbarWindow* win = (ToolbarWindow*)aWindow; - float unifiedToolbarHeight = [win unifiedToolbarHeight]; - return inBoxRect.origin.x == 0 && - inBoxRect.size.width >= [win frame].size.width && - CGRectGetMaxY(inBoxRect) <= unifiedToolbarHeight; -} - -// By default, kCUIWidgetWindowFrame drawing draws rounded corners in the -// upper corners. Depending on the context type, it fills the background in -// the corners with black or leaves it transparent. Unfortunately, this corner -// rounding interacts poorly with the window corner masking we apply during -// titlebar drawing and results in small remnants of the corner background -// appearing at the rounded edge. -// So we draw square corners. -static void -DrawNativeTitlebarToolbarWithSquareCorners(CGContextRef aContext, const CGRect& aRect, - CGFloat aUnifiedHeight, BOOL aIsMain, BOOL aIsFlipped) -{ - // We extend the draw rect horizontally and clip away the rounded corners. - const CGFloat extendHorizontal = 10; - CGRect drawRect = CGRectInset(aRect, -extendHorizontal, 0); - CGContextSaveGState(aContext); - CGContextClipToRect(aContext, aRect); - - RenderWithCoreUI(drawRect, aContext, - [NSDictionary dictionaryWithObjectsAndKeys: - @"kCUIWidgetWindowFrame", @"widget", - @"regularwin", @"windowtype", - (aIsMain ? @"normal" : @"inactive"), @"state", - [NSNumber numberWithDouble:aUnifiedHeight], @"kCUIWindowFrameUnifiedTitleBarHeightKey", - [NSNumber numberWithBool:YES], @"kCUIWindowFrameDrawTitleSeparatorKey", - [NSNumber numberWithBool:aIsFlipped], @"is.flipped", - nil]); - - CGContextRestoreGState(aContext); -} - -void -nsNativeThemeCocoa::DrawUnifiedToolbar(CGContextRef cgContext, const HIRect& inBoxRect, - NSWindow* aWindow) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - CGContextSaveGState(cgContext); - CGContextClipToRect(cgContext, inBoxRect); - - CGFloat unifiedHeight = std::max([(ToolbarWindow*)aWindow unifiedToolbarHeight], - inBoxRect.size.height); - BOOL isMain = [aWindow isMainWindow]; - CGFloat titlebarHeight = unifiedHeight - inBoxRect.size.height; - CGRect drawRect = CGRectMake(inBoxRect.origin.x, inBoxRect.origin.y - titlebarHeight, - inBoxRect.size.width, inBoxRect.size.height + titlebarHeight); - DrawNativeTitlebarToolbarWithSquareCorners(cgContext, drawRect, unifiedHeight, isMain, YES); - - CGContextRestoreGState(cgContext); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -nsNativeThemeCocoa::DrawStatusBar(CGContextRef cgContext, const HIRect& inBoxRect, - nsIFrame *aFrame) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (inBoxRect.size.height < 2.0f) - return; - - CGContextSaveGState(cgContext); - CGContextClipToRect(cgContext, inBoxRect); - - // kCUIWidgetWindowFrame draws a complete window frame with both title bar - // and bottom bar. We only want the bottom bar, so we extend the draw rect - // upwards to make space for the title bar, and then we clip it away. - CGRect drawRect = inBoxRect; - const int extendUpwards = 40; - drawRect.origin.y -= extendUpwards; - drawRect.size.height += extendUpwards; - RenderWithCoreUI(drawRect, cgContext, - [NSDictionary dictionaryWithObjectsAndKeys: - @"kCUIWidgetWindowFrame", @"widget", - @"regularwin", @"windowtype", - (IsActive(aFrame, YES) ? @"normal" : @"inactive"), @"state", - [NSNumber numberWithInt:inBoxRect.size.height], @"kCUIWindowFrameBottomBarHeightKey", - [NSNumber numberWithBool:YES], @"kCUIWindowFrameDrawBottomBarSeparatorKey", - [NSNumber numberWithBool:YES], @"is.flipped", - nil]); - - CGContextRestoreGState(cgContext); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -nsNativeThemeCocoa::DrawNativeTitlebar(CGContextRef aContext, CGRect aTitlebarRect, - CGFloat aUnifiedHeight, BOOL aIsMain, BOOL aIsFlipped) -{ - CGFloat unifiedHeight = std::max(aUnifiedHeight, aTitlebarRect.size.height); - DrawNativeTitlebarToolbarWithSquareCorners(aContext, aTitlebarRect, unifiedHeight, aIsMain, aIsFlipped); -} - -static void -RenderResizer(CGContextRef cgContext, const HIRect& aRenderRect, void* aData) -{ - HIThemeGrowBoxDrawInfo* drawInfo = (HIThemeGrowBoxDrawInfo*)aData; - HIThemeDrawGrowBox(&CGPointZero, drawInfo, cgContext, kHIThemeOrientationNormal); -} - -void -nsNativeThemeCocoa::DrawResizer(CGContextRef cgContext, const HIRect& aRect, - nsIFrame *aFrame) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - HIThemeGrowBoxDrawInfo drawInfo; - drawInfo.version = 0; - drawInfo.state = kThemeStateActive; - drawInfo.kind = kHIThemeGrowBoxKindNormal; - drawInfo.direction = kThemeGrowRight | kThemeGrowDown; - drawInfo.size = kHIThemeGrowBoxSizeNormal; - - RenderTransformedHIThemeControl(cgContext, aRect, RenderResizer, &drawInfo, - IsFrameRTL(aFrame)); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -static void -DrawVibrancyBackground(CGContextRef cgContext, CGRect inBoxRect, - nsIFrame* aFrame, nsITheme::ThemeGeometryType aThemeGeometryType, - int aCornerRadiusIfOpaque = 0) -{ - ChildView* childView = ChildViewForFrame(aFrame); - if (childView) { - NSRect rect = NSRectFromCGRect(inBoxRect); - NSGraphicsContext* savedContext = [NSGraphicsContext currentContext]; - [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]]; - [NSGraphicsContext saveGraphicsState]; - - NSColor* fillColor = [childView vibrancyFillColorForThemeGeometryType:aThemeGeometryType]; - if ([fillColor alphaComponent] == 1.0 && aCornerRadiusIfOpaque > 0) { - // The fillColor being opaque means that the system-wide pref "reduce - // transparency" is set. In that scenario, we still go through all the - // vibrancy rendering paths (VibrancyManager::SystemSupportsVibrancy() - // will still return true), but the result just won't look "vibrant". - // However, there's one unfortunate change of behavior that this pref - // has: It stops the window server from applying window masks. We use - // a window mask to get rounded corners on menus. So since the mask - // doesn't work in "reduce vibrancy" mode, we need to do our own rounded - // corner clipping here. - [[NSBezierPath bezierPathWithRoundedRect:rect - xRadius:aCornerRadiusIfOpaque - yRadius:aCornerRadiusIfOpaque] addClip]; - } - - [fillColor set]; - NSRectFill(rect); - - [NSGraphicsContext restoreGraphicsState]; - [NSGraphicsContext setCurrentContext:savedContext]; - } -} - -bool -nsNativeThemeCocoa::IsParentScrollbarRolledOver(nsIFrame* aFrame) -{ - nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame); - return nsLookAndFeel::UseOverlayScrollbars() - ? CheckBooleanAttr(scrollbarFrame, nsGkAtoms::hover) - : GetContentState(scrollbarFrame, NS_THEME_NONE).HasState(NS_EVENT_STATE_HOVER); -} - -static bool -IsHiDPIContext(nsPresContext* aContext) -{ - return nsPresContext::AppUnitsPerCSSPixel() >= - 2 * aContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom(); -} - -static bool -IsScrollbarWidthThin(nsIFrame* aFrame) -{ - return aFrame->StyleUserInterface()->mScrollbarWidth == StyleScrollbarWidth::Thin; -} - -NS_IMETHODIMP -nsNativeThemeCocoa::DrawWidgetBackground(nsRenderingContext* aContext, - nsIFrame* aFrame, - uint8_t aWidgetType, - const nsRect& aRect, - const nsRect& aDirtyRect) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - DrawTarget& aDrawTarget = *aContext->GetDrawTarget(); - - // setup to draw into the correct port - int32_t p2a = aFrame->PresContext()->AppUnitsPerDevPixel(); - - gfx::Rect nativeDirtyRect = NSRectToRect(aDirtyRect, p2a); - gfxRect nativeWidgetRect(aRect.x, aRect.y, aRect.width, aRect.height); - nativeWidgetRect.ScaleInverse(gfxFloat(p2a)); - float nativeWidgetHeight = round(nativeWidgetRect.Height()); - nativeWidgetRect.Round(); - if (nativeWidgetRect.IsEmpty()) - return NS_OK; // Don't attempt to draw invisible widgets. - - AutoRestoreTransform autoRestoreTransform(&aDrawTarget); - - bool hidpi = IsHiDPIContext(aFrame->PresContext()); - if (hidpi) { - // Use high-resolution drawing. - nativeWidgetRect.Scale(0.5f); - nativeWidgetHeight *= 0.5f; - nativeDirtyRect.Scale(0.5f); - aDrawTarget.SetTransform(aDrawTarget.GetTransform().PreScale(2.0f, 2.0f)); - } - - gfxQuartzNativeDrawing nativeDrawing(aDrawTarget, nativeDirtyRect); - - CGContextRef cgContext = nativeDrawing.BeginNativeDrawing(); - if (cgContext == nullptr) { - // The Quartz surface handles 0x0 surfaces by internally - // making all operations no-ops; there's no cgcontext created for them. - // Unfortunately, this means that callers that want to render - // directly to the CGContext need to be aware of this quirk. - return NS_OK; - } - - if (hidpi) { - // Set the context's "base transform" to in order to get correctly-sized focus rings. - CGContextSetBaseCTM(cgContext, CGAffineTransformMakeScale(2, 2)); - } - -#if 0 - if (1 /*aWidgetType == NS_THEME_TEXTFIELD*/) { - fprintf(stderr, "Native theme drawing widget %d [%p] dis:%d in rect [%d %d %d %d]\n", - aWidgetType, aFrame, IsDisabled(aFrame), aRect.x, aRect.y, aRect.width, aRect.height); - fprintf(stderr, "Cairo matrix: [%f %f %f %f %f %f]\n", - mat._11, mat._12, mat._21, mat._22, mat._31, mat._32); - fprintf(stderr, "Native theme xform[0]: [%f %f %f %f %f %f]\n", - mm0.a, mm0.b, mm0.c, mm0.d, mm0.tx, mm0.ty); - CGAffineTransform mm = CGContextGetCTM(cgContext); - fprintf(stderr, "Native theme xform[1]: [%f %f %f %f %f %f]\n", - mm.a, mm.b, mm.c, mm.d, mm.tx, mm.ty); - } -#endif - - CGRect macRect = CGRectMake(nativeWidgetRect.X(), nativeWidgetRect.Y(), - nativeWidgetRect.Width(), nativeWidgetRect.Height()); - -#if 0 - fprintf(stderr, " --> macRect %f %f %f %f\n", - macRect.origin.x, macRect.origin.y, macRect.size.width, macRect.size.height); - CGRect bounds = CGContextGetClipBoundingBox(cgContext); - fprintf(stderr, " --> clip bounds: %f %f %f %f\n", - bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height); - - //CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 1.0, 0.1); - //CGContextFillRect(cgContext, bounds); -#endif - - EventStates eventState = GetContentState(aFrame, aWidgetType); - - switch (aWidgetType) { - case NS_THEME_DIALOG: { - if (IsWindowSheet(aFrame)) { - if (VibrancyManager::SystemSupportsVibrancy()) { - ThemeGeometryType type = ThemeGeometryTypeForWidget(aFrame, aWidgetType); - DrawVibrancyBackground(cgContext, macRect, aFrame, type); - } else { - HIThemeSetFill(kThemeBrushSheetBackgroundTransparent, NULL, cgContext, HITHEME_ORIENTATION); - CGContextFillRect(cgContext, macRect); - } - } else { - HIThemeSetFill(kThemeBrushDialogBackgroundActive, NULL, cgContext, HITHEME_ORIENTATION); - CGContextFillRect(cgContext, macRect); - } - - } - break; - - case NS_THEME_MENUPOPUP: - if (VibrancyManager::SystemSupportsVibrancy()) { - DrawVibrancyBackground(cgContext, macRect, aFrame, eThemeGeometryTypeMenu, 4); - } else { - HIThemeMenuDrawInfo mdi; - memset(&mdi, 0, sizeof(mdi)); - mdi.version = 0; - mdi.menuType = IsDisabled(aFrame, eventState) ? - static_cast<ThemeMenuType>(kThemeMenuTypeInactive) : - static_cast<ThemeMenuType>(kThemeMenuTypePopUp); - - bool isLeftOfParent = false; - if (IsSubmenu(aFrame, &isLeftOfParent) && !isLeftOfParent) { - mdi.menuType = kThemeMenuTypeHierarchical; - } - - // The rounded corners draw outside the frame. - CGRect deflatedRect = CGRectMake(macRect.origin.x, macRect.origin.y + 4, - macRect.size.width, macRect.size.height - 8); - HIThemeDrawMenuBackground(&deflatedRect, &mdi, cgContext, HITHEME_ORIENTATION); - } - break; - - case NS_THEME_MENUARROW: { - bool isRTL = IsFrameRTL(aFrame); - DrawMenuIcon(cgContext, macRect, eventState, aFrame, kMenuarrowSize, - isRTL ? kMenuarrowLeftImage : kMenuarrowRightImage, true); - } - break; - - case NS_THEME_MENUITEM: - case NS_THEME_CHECKMENUITEM: { - if (VibrancyManager::SystemSupportsVibrancy()) { - ThemeGeometryType type = ThemeGeometryTypeForWidget(aFrame, aWidgetType); - DrawVibrancyBackground(cgContext, macRect, aFrame, type); - } else { - bool isDisabled = IsDisabled(aFrame, eventState); - bool isSelected = !isDisabled && CheckBooleanAttr(aFrame, nsGkAtoms::menuactive); - // maybe use kThemeMenuItemHierBackground or PopUpBackground instead of just Plain? - HIThemeMenuItemDrawInfo drawInfo; - memset(&drawInfo, 0, sizeof(drawInfo)); - drawInfo.version = 0; - drawInfo.itemType = kThemeMenuItemPlain; - drawInfo.state = (isDisabled ? - static_cast<ThemeMenuState>(kThemeMenuDisabled) : - isSelected ? - static_cast<ThemeMenuState>(kThemeMenuSelected) : - static_cast<ThemeMenuState>(kThemeMenuActive)); - - // XXX pass in the menu rect instead of always using the item rect - HIRect ignored; - HIThemeDrawMenuItem(&macRect, &macRect, &drawInfo, cgContext, HITHEME_ORIENTATION, &ignored); - } - - if (aWidgetType == NS_THEME_CHECKMENUITEM) { - DrawMenuIcon(cgContext, macRect, eventState, aFrame, kCheckmarkSize, kCheckmarkImage, false); - } - } - break; - - case NS_THEME_MENUSEPARATOR: { - // Workaround for visual artifacts issues with - // HIThemeDrawMenuSeparator on macOS Big Sur. - if (nsCocoaFeatures::OnBigSurOrLater()) { - CGRect separatorRect = macRect; - separatorRect.size.height = 1; - separatorRect.size.width -= 42; - separatorRect.origin.x += 21; - // Use a gray color similar to the native separator - CGContextSetRGBFillColor(cgContext, 0.816, 0.816, 0.816, 1.0); - CGContextFillRect(cgContext, separatorRect); - } - else - { - ThemeMenuState menuState; - if (IsDisabled(aFrame, eventState)) { - menuState = kThemeMenuDisabled; - } - else { - menuState = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive) ? - kThemeMenuSelected : kThemeMenuActive; - } - HIThemeMenuItemDrawInfo midi = { 0, kThemeMenuItemPlain, menuState }; - HIThemeDrawMenuSeparator(&macRect, &macRect, &midi, cgContext, HITHEME_ORIENTATION); - } - } - break; - - case NS_THEME_BUTTON_ARROW_UP: - case NS_THEME_BUTTON_ARROW_DOWN: - DrawMenuIcon(cgContext, macRect, eventState, aFrame, kMenuScrollArrowSize, - aWidgetType == NS_THEME_BUTTON_ARROW_UP ? - kMenuUpScrollArrowImage : kMenuDownScrollArrowImage, true); - break; - - case NS_THEME_TOOLTIP: - if (VibrancyManager::SystemSupportsVibrancy()) { - DrawVibrancyBackground(cgContext, macRect, aFrame, ThemeGeometryTypeForWidget(aFrame, aWidgetType)); - } else { - CGContextSetRGBFillColor(cgContext, 0.996, 1.000, 0.792, 0.950); - CGContextFillRect(cgContext, macRect); - } - break; - - case NS_THEME_CHECKBOX: - case NS_THEME_RADIO: { - bool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX); - DrawCheckboxOrRadio(cgContext, isCheckbox, macRect, GetCheckedOrSelected(aFrame, !isCheckbox), - eventState, aFrame); - } - break; - - case NS_THEME_BUTTON: - if (IsDefaultButton(aFrame)) { - // Check whether the default button is in a document that does not - // match the :-moz-window-inactive pseudoclass. This activeness check - // is different from the other "active window" checks in this file - // because we absolutely need the button's default button appearance to - // be in sync with its text color, and the text color is changed by - // such a :-moz-window-inactive rule. (That's because on 10.10 and up, - // default buttons in active windows have blue background and white - // text, and default buttons in inactive windows have white background - // and black text.) - EventStates docState = aFrame->GetContent()->OwnerDoc()->GetDocumentState(); - bool isInActiveWindow = !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE); - if (!IsDisabled(aFrame, eventState) && isInActiveWindow && - !QueueAnimatedContentForRefresh(aFrame->GetContent(), 10)) { - NS_WARNING("Unable to animate button!"); - } - DrawButton(cgContext, kThemePushButton, macRect, isInActiveWindow, - kThemeButtonOff, kThemeAdornmentNone, eventState, aFrame); - } else if (IsButtonTypeMenu(aFrame)) { - DrawDropdown(cgContext, macRect, eventState, aWidgetType, aFrame); - } else { - DrawPushButton(cgContext, macRect, eventState, aWidgetType, aFrame, - nativeWidgetHeight); - } - break; - - case NS_THEME_FOCUS_OUTLINE: - DrawFocusOutline(cgContext, macRect, eventState, aWidgetType, aFrame); - break; - - case NS_THEME_MAC_HELP_BUTTON: - case NS_THEME_MAC_DISCLOSURE_BUTTON_OPEN: - case NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED: - DrawPushButton(cgContext, macRect, eventState, aWidgetType, aFrame, - nativeWidgetHeight); - break; - - case NS_THEME_BUTTON_BEVEL: - DrawButton(cgContext, kThemeMediumBevelButton, macRect, - IsDefaultButton(aFrame), kThemeButtonOff, kThemeAdornmentNone, - eventState, aFrame); - break; - - case NS_THEME_SPINNER: { - nsIContent* content = aFrame->GetContent(); - if (content->IsHTMLElement()) { - // In HTML the theming for the spin buttons is drawn individually into - // their own backgrounds instead of being drawn into the background of - // their spinner parent as it is for XUL. - break; - } - ThemeDrawState state = kThemeStateActive; - if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::state, - NS_LITERAL_STRING("up"), eCaseMatters)) { - state = kThemeStatePressedUp; - } - else if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::state, - NS_LITERAL_STRING("down"), eCaseMatters)) { - state = kThemeStatePressedDown; - } - - DrawSpinButtons(cgContext, kThemeIncDecButton, macRect, state, - kThemeAdornmentNone, eventState, aFrame); - } - break; - - case NS_THEME_SPINNER_UPBUTTON: - case NS_THEME_SPINNER_DOWNBUTTON: { - nsNumberControlFrame* numberControlFrame = - nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame); - if (numberControlFrame) { - ThemeDrawState state = kThemeStateActive; - if (numberControlFrame->SpinnerUpButtonIsDepressed()) { - state = kThemeStatePressedUp; - } else if (numberControlFrame->SpinnerDownButtonIsDepressed()) { - state = kThemeStatePressedDown; - } - DrawSpinButton(cgContext, kThemeIncDecButtonMini, macRect, state, - kThemeAdornmentNone, eventState, aFrame, aWidgetType); - } - } - break; - - case NS_THEME_TOOLBARBUTTON: - DrawSegment(cgContext, macRect, eventState, aFrame, toolbarButtonRenderSettings); - break; - - case NS_THEME_SEPARATOR: { - HIThemeSeparatorDrawInfo sdi = { 0, kThemeStateActive }; - HIThemeDrawSeparator(&macRect, &sdi, cgContext, HITHEME_ORIENTATION); - } - break; - - case NS_THEME_TOOLBAR: { - NSWindow* win = NativeWindowForFrame(aFrame); - if (ToolbarCanBeUnified(cgContext, macRect, win)) { - DrawUnifiedToolbar(cgContext, macRect, win); - break; - } - BOOL isMain = [win isMainWindow]; - CGRect drawRect = macRect; - - // top border - drawRect.size.height = 1.0f; - DrawNativeGreyColorInRect(cgContext, toolbarTopBorderGrey, drawRect, isMain); - - // background - drawRect.origin.y += drawRect.size.height; - drawRect.size.height = macRect.size.height - 2.0f; - DrawNativeGreyColorInRect(cgContext, toolbarFillGrey, drawRect, isMain); - - // bottom border - drawRect.origin.y += drawRect.size.height; - drawRect.size.height = 1.0f; - DrawNativeGreyColorInRect(cgContext, toolbarBottomBorderGrey, drawRect, isMain); - } - break; - - case NS_THEME_WINDOW_TITLEBAR: { - NSWindow* win = NativeWindowForFrame(aFrame); - BOOL isMain = [win isMainWindow]; - float unifiedToolbarHeight = [win isKindOfClass:[ToolbarWindow class]] ? - [(ToolbarWindow*)win unifiedToolbarHeight] : macRect.size.height; - DrawNativeTitlebar(cgContext, macRect, unifiedToolbarHeight, isMain, YES); - } - break; - - case NS_THEME_STATUSBAR: - DrawStatusBar(cgContext, macRect, aFrame); - break; - - case NS_THEME_MENULIST: - case NS_THEME_MENULIST_TEXTFIELD: - DrawDropdown(cgContext, macRect, eventState, aWidgetType, aFrame); - break; - - case NS_THEME_MENULIST_BUTTON: - DrawButton(cgContext, kThemeArrowButton, macRect, false, kThemeButtonOn, - kThemeAdornmentArrowDownArrow, eventState, aFrame); - break; - - case NS_THEME_GROUPBOX: { - HIThemeGroupBoxDrawInfo gdi = { 0, kThemeStateActive, kHIThemeGroupBoxKindPrimary }; - HIThemeDrawGroupBox(&macRect, &gdi, cgContext, HITHEME_ORIENTATION); - break; - } - - case NS_THEME_TEXTFIELD: - case NS_THEME_NUMBER_INPUT: - // HIThemeSetFill is not available on 10.3 - CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0); - CGContextFillRect(cgContext, macRect); - - // XUL textboxes set the native appearance on the containing box, while - // concrete focus is set on the html:input element within it. We can - // though, check the focused attribute of xul textboxes in this case. - // On Mac, focus rings are always shown for textboxes, so we do not need - // to check the window's focus ring state here - if (aFrame->GetContent()->IsXULElement() && IsFocused(aFrame)) { - eventState |= NS_EVENT_STATE_FOCUS; - } - - DrawFrame(cgContext, kHIThemeFrameTextFieldSquare, macRect, - IsDisabled(aFrame, eventState) || IsReadOnly(aFrame), eventState); - break; - - case NS_THEME_SEARCHFIELD: - DrawSearchField(cgContext, macRect, aFrame, eventState); - break; - - case NS_THEME_PROGRESSBAR: - { - double value = GetProgressValue(aFrame); - double maxValue = GetProgressMaxValue(aFrame); - // Don't request repaints for scrollbars at 100% because those don't animate. - if (value < maxValue) { - if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) { - NS_WARNING("Unable to animate progressbar!"); - } - } - DrawProgress(cgContext, macRect, IsIndeterminateProgress(aFrame, eventState), - !IsVerticalProgress(aFrame), - value, maxValue, aFrame); - break; - } - - case NS_THEME_PROGRESSBAR_VERTICAL: - DrawProgress(cgContext, macRect, IsIndeterminateProgress(aFrame, eventState), - false, GetProgressValue(aFrame), - GetProgressMaxValue(aFrame), aFrame); - break; - - case NS_THEME_METERBAR: - DrawMeter(cgContext, macRect, aFrame); - break; - - case NS_THEME_PROGRESSCHUNK: - case NS_THEME_PROGRESSCHUNK_VERTICAL: - case NS_THEME_METERCHUNK: - // Do nothing: progress and meter bars cases will draw chunks. - break; - - case NS_THEME_TREETWISTY: - DrawButton(cgContext, kThemeDisclosureButton, macRect, false, - kThemeDisclosureRight, kThemeAdornmentNone, eventState, aFrame); - break; - - case NS_THEME_TREETWISTYOPEN: - DrawButton(cgContext, kThemeDisclosureButton, macRect, false, - kThemeDisclosureDown, kThemeAdornmentNone, eventState, aFrame); - break; - - case NS_THEME_TREEHEADERCELL: { - TreeSortDirection sortDirection = GetTreeSortDirection(aFrame); - DrawButton(cgContext, kThemeListHeaderButton, macRect, false, - sortDirection == eTreeSortDirection_Natural ? kThemeButtonOff : kThemeButtonOn, - sortDirection == eTreeSortDirection_Ascending ? - kThemeAdornmentHeaderButtonSortUp : kThemeAdornmentNone, eventState, aFrame); - } - break; - - case NS_THEME_TREEITEM: - case NS_THEME_TREEVIEW: - // HIThemeSetFill is not available on 10.3 - // HIThemeSetFill(kThemeBrushWhite, NULL, cgContext, HITHEME_ORIENTATION); - CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0); - CGContextFillRect(cgContext, macRect); - break; - - case NS_THEME_TREEHEADER: - // do nothing, taken care of by individual header cells - case NS_THEME_TREEHEADERSORTARROW: - // do nothing, taken care of by treeview header - case NS_THEME_TREELINE: - // do nothing, these lines don't exist on macos - break; - - case NS_THEME_SCALE_HORIZONTAL: - case NS_THEME_SCALE_VERTICAL: { - int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0); - int32_t minpos = CheckIntAttr(aFrame, nsGkAtoms::minpos, 0); - int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100); - if (!maxpos) - maxpos = 100; - - bool reverse = aFrame->GetContent()-> - AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir, - NS_LITERAL_STRING("reverse"), eCaseMatters); - DrawScale(cgContext, macRect, eventState, - (aWidgetType == NS_THEME_SCALE_VERTICAL), reverse, - curpos, minpos, maxpos, aFrame); - } - break; - - case NS_THEME_SCALETHUMB_HORIZONTAL: - case NS_THEME_SCALETHUMB_VERTICAL: - // do nothing, drawn by scale - break; - - case NS_THEME_RANGE: { - nsRangeFrame *rangeFrame = do_QueryFrame(aFrame); - if (!rangeFrame) { - break; - } - // DrawScale requires integer min, max and value. This is purely for - // drawing, so we normalize to a range 0-1000 here. - int32_t value = int32_t(rangeFrame->GetValueAsFractionOfRange() * 1000); - int32_t min = 0; - int32_t max = 1000; - bool isVertical = !IsRangeHorizontal(aFrame); - bool reverseDir = isVertical || rangeFrame->IsRightToLeft(); - DrawScale(cgContext, macRect, eventState, isVertical, reverseDir, - value, min, max, aFrame); - break; - } - - case NS_THEME_SCROLLBAR_SMALL: - case NS_THEME_SCROLLBAR: - break; - case NS_THEME_SCROLLBARTHUMB_VERTICAL: - case NS_THEME_SCROLLBARTHUMB_HORIZONTAL: { - BOOL isOverlay = nsLookAndFeel::UseOverlayScrollbars(); - BOOL isHorizontal = (aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL); - BOOL isRolledOver = IsParentScrollbarRolledOver(aFrame); - nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame); - bool isSmall = (scrollbarFrame && scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL); - if (isOverlay && !isRolledOver) { - if (isHorizontal) { - macRect.origin.y += 4; - macRect.size.height -= 4; - } else { - if (aFrame->StyleVisibility()->mDirection != - NS_STYLE_DIRECTION_RTL) { - macRect.origin.x += 4; - } - macRect.size.width -= 4; - } - } - const BOOL isOnTopOfDarkBackground = IsDarkBackground(aFrame); - NSMutableDictionary* options = [NSMutableDictionary dictionaryWithObjectsAndKeys: - (isOverlay ? @"kCUIWidgetOverlayScrollBar" : @"scrollbar"), @"widget", - (isSmall ? @"small" : @"regular"), @"size", - (isHorizontal ? @"kCUIOrientHorizontal" : @"kCUIOrientVertical"), @"kCUIOrientationKey", - (isOverlay && isOnTopOfDarkBackground ? @"kCUIVariantWhite" : @""), @"kCUIVariantKey", - [NSNumber numberWithBool:YES], @"indiconly", - [NSNumber numberWithBool:YES], @"kCUIThumbProportionKey", - [NSNumber numberWithBool:YES], @"is.flipped", - nil]; - if (isRolledOver) { - [options setObject:@"rollover" forKey:@"state"]; - } - RenderWithCoreUI(macRect, cgContext, options, true); - } - break; - - case NS_THEME_SCROLLBARBUTTON_UP: - case NS_THEME_SCROLLBARBUTTON_LEFT: -#if SCROLLBARS_VISUAL_DEBUG - CGContextSetRGBFillColor(cgContext, 1.0, 0, 0, 0.6); - CGContextFillRect(cgContext, macRect); -#endif - break; - case NS_THEME_SCROLLBARBUTTON_DOWN: - case NS_THEME_SCROLLBARBUTTON_RIGHT: -#if SCROLLBARS_VISUAL_DEBUG - CGContextSetRGBFillColor(cgContext, 0, 1.0, 0, 0.6); - CGContextFillRect(cgContext, macRect); -#endif - break; - case NS_THEME_SCROLLBARTRACK_HORIZONTAL: - case NS_THEME_SCROLLBARTRACK_VERTICAL: { - BOOL isOverlay = nsLookAndFeel::UseOverlayScrollbars(); - if (!isOverlay || IsParentScrollbarRolledOver(aFrame)) { - BOOL isHorizontal = (aWidgetType == NS_THEME_SCROLLBARTRACK_HORIZONTAL); - nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame); - bool isSmall = (scrollbarFrame && scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL); - const BOOL isOnTopOfDarkBackground = IsDarkBackground(aFrame); - RenderWithCoreUI(macRect, cgContext, - [NSDictionary dictionaryWithObjectsAndKeys: - (isOverlay ? @"kCUIWidgetOverlayScrollBar" : @"scrollbar"), @"widget", - (isSmall ? @"small" : @"regular"), @"size", - (isHorizontal ? @"kCUIOrientHorizontal" : @"kCUIOrientVertical"), @"kCUIOrientationKey", - (isOnTopOfDarkBackground ? @"kCUIVariantWhite" : @""), @"kCUIVariantKey", - [NSNumber numberWithBool:YES], @"noindicator", - [NSNumber numberWithBool:YES], @"kCUIThumbProportionKey", - [NSNumber numberWithBool:YES], @"is.flipped", - nil], - true); - } - } - break; - - case NS_THEME_TEXTFIELD_MULTILINE: { - // we have to draw this by hand because there is no HITheme value for it - CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0); - - CGContextFillRect(cgContext, macRect); - - // #737373 for the top border, #999999 for the rest. - float x = macRect.origin.x, y = macRect.origin.y; - float w = macRect.size.width, h = macRect.size.height; - CGContextSetRGBFillColor(cgContext, 0.4510, 0.4510, 0.4510, 1.0); - CGContextFillRect(cgContext, CGRectMake(x, y, w, 1)); - CGContextSetRGBFillColor(cgContext, 0.6, 0.6, 0.6, 1.0); - CGContextFillRect(cgContext, CGRectMake(x, y + 1, 1, h - 1)); - CGContextFillRect(cgContext, CGRectMake(x + w - 1, y + 1, 1, h - 1)); - CGContextFillRect(cgContext, CGRectMake(x + 1, y + h - 1, w - 2, 1)); - - // draw a focus ring - if (eventState.HasState(NS_EVENT_STATE_FOCUS)) { - NSGraphicsContext* savedContext = [NSGraphicsContext currentContext]; - [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]]; - CGContextSaveGState(cgContext); - NSSetFocusRingStyle(NSFocusRingOnly); - NSRectFill(NSRectFromCGRect(macRect)); - CGContextRestoreGState(cgContext); - [NSGraphicsContext setCurrentContext:savedContext]; - } - } - break; - - case NS_THEME_LISTBOX: { - // We have to draw this by hand because kHIThemeFrameListBox drawing - // is buggy on 10.5, see bug 579259. - CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0); - CGContextFillRect(cgContext, macRect); - - // #8E8E8E for the top border, #BEBEBE for the rest. - float x = macRect.origin.x, y = macRect.origin.y; - float w = macRect.size.width, h = macRect.size.height; - CGContextSetRGBFillColor(cgContext, 0.557, 0.557, 0.557, 1.0); - CGContextFillRect(cgContext, CGRectMake(x, y, w, 1)); - CGContextSetRGBFillColor(cgContext, 0.745, 0.745, 0.745, 1.0); - CGContextFillRect(cgContext, CGRectMake(x, y + 1, 1, h - 1)); - CGContextFillRect(cgContext, CGRectMake(x + w - 1, y + 1, 1, h - 1)); - CGContextFillRect(cgContext, CGRectMake(x + 1, y + h - 1, w - 2, 1)); - } - break; - - case NS_THEME_MAC_SOURCE_LIST: { - if (VibrancyManager::SystemSupportsVibrancy()) { - ThemeGeometryType type = ThemeGeometryTypeForWidget(aFrame, aWidgetType); - DrawVibrancyBackground(cgContext, macRect, aFrame, type); - } else { - CGGradientRef backgroundGradient; - CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB(); - CGFloat activeGradientColors[8] = { 0.9137, 0.9294, 0.9490, 1.0, - 0.8196, 0.8471, 0.8784, 1.0 }; - CGFloat inactiveGradientColors[8] = { 0.9686, 0.9686, 0.9686, 1.0, - 0.9216, 0.9216, 0.9216, 1.0 }; - CGPoint start = macRect.origin; - CGPoint end = CGPointMake(macRect.origin.x, - macRect.origin.y + macRect.size.height); - BOOL isActive = FrameIsInActiveWindow(aFrame); - backgroundGradient = - CGGradientCreateWithColorComponents(rgb, isActive ? activeGradientColors - : inactiveGradientColors, NULL, 2); - CGContextDrawLinearGradient(cgContext, backgroundGradient, start, end, 0); - CGGradientRelease(backgroundGradient); - CGColorSpaceRelease(rgb); - } - } - break; - - case NS_THEME_MAC_SOURCE_LIST_SELECTION: - case NS_THEME_MAC_ACTIVE_SOURCE_LIST_SELECTION: { - // If we're in XUL tree, we need to rely on the source list's clear - // background display item. If we cleared the background behind the - // selections, the source list would not pick up the right font - // smoothing background. So, to simplify a bit, we only support vibrancy - // if we're in a source list. - if (VibrancyManager::SystemSupportsVibrancy() && IsInSourceList(aFrame)) { - ThemeGeometryType type = ThemeGeometryTypeForWidget(aFrame, aWidgetType); - DrawVibrancyBackground(cgContext, macRect, aFrame, type); - } else { - BOOL isActiveSelection = - aWidgetType == NS_THEME_MAC_ACTIVE_SOURCE_LIST_SELECTION; - RenderWithCoreUI(macRect, cgContext, - [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:isActiveSelection], @"focus", - [NSNumber numberWithBool:YES], @"is.flipped", - @"kCUIVariantGradientSideBarSelection", @"kCUIVariantKey", - (FrameIsInActiveWindow(aFrame) ? @"normal" : @"inactive"), @"state", - @"gradient", @"widget", - nil]); - } - } - break; - - case NS_THEME_TAB: - DrawSegment(cgContext, macRect, eventState, aFrame, tabRenderSettings); - break; - - case NS_THEME_TABPANELS: - DrawTabPanel(cgContext, macRect, aFrame); - break; - - case NS_THEME_RESIZER: - DrawResizer(cgContext, macRect, aFrame); - break; - - case NS_THEME_MAC_VIBRANCY_LIGHT: - case NS_THEME_MAC_VIBRANCY_DARK: { - ThemeGeometryType type = ThemeGeometryTypeForWidget(aFrame, aWidgetType); - DrawVibrancyBackground(cgContext, macRect, aFrame, type); - break; - } - } - - if (hidpi) { - // Reset the base CTM. - CGContextSetBaseCTM(cgContext, CGAffineTransformIdentity); - } - - nativeDrawing.EndNativeDrawing(); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -nsIntMargin -nsNativeThemeCocoa::DirectionAwareMargin(const nsIntMargin& aMargin, - nsIFrame* aFrame) -{ - // Assuming aMargin was originally specified for a horizontal LTR context, - // reinterpret the values as logical, and then map to physical coords - // according to aFrame's actual writing mode. - WritingMode wm = aFrame->GetWritingMode(); - nsMargin m = LogicalMargin(wm, aMargin.top, aMargin.right, aMargin.bottom, - aMargin.left).GetPhysicalMargin(wm); - return nsIntMargin(m.top, m.right, m.bottom, m.left); -} - -static const nsIntMargin kAquaDropdownBorder(1, 22, 2, 5); -static const nsIntMargin kAquaComboboxBorder(3, 20, 3, 4); -static const nsIntMargin kAquaSearchfieldBorder(3, 5, 2, 19); - -NS_IMETHODIMP -nsNativeThemeCocoa::GetWidgetBorder(nsDeviceContext* aContext, - nsIFrame* aFrame, - uint8_t aWidgetType, - nsIntMargin* aResult) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - aResult->SizeTo(0, 0, 0, 0); - - switch (aWidgetType) { - case NS_THEME_BUTTON: - { - if (IsButtonTypeMenu(aFrame)) { - *aResult = DirectionAwareMargin(kAquaDropdownBorder, aFrame); - } else { - *aResult = DirectionAwareMargin(nsIntMargin(1, 7, 3, 7), aFrame); - } - break; - } - - case NS_THEME_TOOLBARBUTTON: - { - *aResult = DirectionAwareMargin(nsIntMargin(1, 4, 1, 4), aFrame); - break; - } - - case NS_THEME_CHECKBOX: - case NS_THEME_RADIO: - { - // nsFormControlFrame::GetIntrinsicWidth and nsFormControlFrame::GetIntrinsicHeight - // assume a border width of 2px. - aResult->SizeTo(2, 2, 2, 2); - break; - } - - case NS_THEME_MENULIST: - case NS_THEME_MENULIST_BUTTON: - *aResult = DirectionAwareMargin(kAquaDropdownBorder, aFrame); - break; - - case NS_THEME_MENULIST_TEXTFIELD: - *aResult = DirectionAwareMargin(kAquaComboboxBorder, aFrame); - break; - - case NS_THEME_NUMBER_INPUT: - case NS_THEME_TEXTFIELD: - { - SInt32 frameOutset = 0; - ::GetThemeMetric(kThemeMetricEditTextFrameOutset, &frameOutset); - - SInt32 textPadding = 0; - ::GetThemeMetric(kThemeMetricEditTextWhitespace, &textPadding); - - frameOutset += textPadding; - - aResult->SizeTo(frameOutset, frameOutset, frameOutset, frameOutset); - break; - } - - case NS_THEME_TEXTFIELD_MULTILINE: - aResult->SizeTo(1, 1, 1, 1); - break; - - case NS_THEME_SEARCHFIELD: - *aResult = DirectionAwareMargin(kAquaSearchfieldBorder, aFrame); - break; - - case NS_THEME_LISTBOX: - { - SInt32 frameOutset = 0; - ::GetThemeMetric(kThemeMetricListBoxFrameOutset, &frameOutset); - aResult->SizeTo(frameOutset, frameOutset, frameOutset, frameOutset); - break; - } - - case NS_THEME_SCROLLBARTRACK_HORIZONTAL: - case NS_THEME_SCROLLBARTRACK_VERTICAL: - { - bool isHorizontal = (aWidgetType == NS_THEME_SCROLLBARTRACK_HORIZONTAL); - if (nsLookAndFeel::UseOverlayScrollbars()) { - if (!nsCocoaFeatures::OnYosemiteOrLater()) { - // Pre-10.10, we have to center the thumb rect in the middle of the - // scrollbar. Starting with 10.10, the expected rect for thumb - // rendering is the full width of the scrollbar. - if (isHorizontal) { - aResult->top = 2; - aResult->bottom = 1; - } else { - aResult->left = 2; - aResult->right = 1; - } - } - // Leave a bit of space at the start and the end on all OS X versions. - if (isHorizontal) { - aResult->left = 1; - aResult->right = 1; - } else { - aResult->top = 1; - aResult->bottom = 1; - } - } - - break; - } - - case NS_THEME_STATUSBAR: - aResult->SizeTo(1, 0, 0, 0); - break; - } - - if (IsHiDPIContext(aFrame->PresContext())) { - *aResult = *aResult + *aResult; // doubled - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// Return false here to indicate that CSS padding values should be used. There is -// no reason to make a distinction between padding and border values, just specify -// whatever values you want in GetWidgetBorder and only use this to return true -// if you want to override CSS padding values. -bool -nsNativeThemeCocoa::GetWidgetPadding(nsDeviceContext* aContext, - nsIFrame* aFrame, - uint8_t aWidgetType, - nsIntMargin* aResult) -{ - // We don't want CSS padding being used for certain widgets. - // See bug 381639 for an example of why. - switch (aWidgetType) { - // Radios and checkboxes return a fixed size in GetMinimumWidgetSize - // and have a meaningful baseline, so they can't have - // author-specified padding. - case NS_THEME_CHECKBOX: - case NS_THEME_RADIO: - aResult->SizeTo(0, 0, 0, 0); - return true; - } - return false; -} - -bool -nsNativeThemeCocoa::GetWidgetOverflow(nsDeviceContext* aContext, nsIFrame* aFrame, - uint8_t aWidgetType, nsRect* aOverflowRect) -{ - int32_t p2a = aFrame->PresContext()->AppUnitsPerDevPixel(); - switch (aWidgetType) { - case NS_THEME_BUTTON: - case NS_THEME_MAC_DISCLOSURE_BUTTON_OPEN: - case NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED: - case NS_THEME_MAC_HELP_BUTTON: - case NS_THEME_TOOLBARBUTTON: - case NS_THEME_NUMBER_INPUT: - case NS_THEME_TEXTFIELD: - case NS_THEME_TEXTFIELD_MULTILINE: - case NS_THEME_SEARCHFIELD: - case NS_THEME_LISTBOX: - case NS_THEME_MENULIST: - case NS_THEME_MENULIST_BUTTON: - case NS_THEME_MENULIST_TEXTFIELD: - case NS_THEME_CHECKBOX: - case NS_THEME_RADIO: - case NS_THEME_TAB: - { - // We assume that the above widgets can draw a focus ring that will be less than - // or equal to 4 pixels thick. - nsIntMargin extraSize = nsIntMargin(kMaxFocusRingWidth, - kMaxFocusRingWidth, - kMaxFocusRingWidth, - kMaxFocusRingWidth); - nsMargin m(NSIntPixelsToAppUnits(extraSize.top, p2a), - NSIntPixelsToAppUnits(extraSize.right, p2a), - NSIntPixelsToAppUnits(extraSize.bottom, p2a), - NSIntPixelsToAppUnits(extraSize.left, p2a)); - aOverflowRect->Inflate(m); - return true; - } - case NS_THEME_PROGRESSBAR: - { - // Progress bars draw a 2 pixel white shadow under their progress indicators - nsMargin m(0, 0, NSIntPixelsToAppUnits(2, p2a), 0); - aOverflowRect->Inflate(m); - return true; - } - case NS_THEME_FOCUS_OUTLINE: - { - aOverflowRect->Inflate(NSIntPixelsToAppUnits(2, p2a)); - return true; - } - } - - return false; -} - -static const int32_t kRegularScrollbarThumbMinSize = 26; -static const int32_t kSmallScrollbarThumbMinSize = 26; - -NS_IMETHODIMP -nsNativeThemeCocoa::GetMinimumWidgetSize(nsPresContext* aPresContext, - nsIFrame* aFrame, - uint8_t aWidgetType, - LayoutDeviceIntSize* aResult, - bool* aIsOverridable) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - aResult->SizeTo(0,0); - *aIsOverridable = true; - - switch (aWidgetType) { - case NS_THEME_BUTTON: - { - aResult->SizeTo(pushButtonSettings.minimumSizes[miniControlSize].width, - pushButtonSettings.naturalSizes[miniControlSize].height); - break; - } - - case NS_THEME_BUTTON_ARROW_UP: - case NS_THEME_BUTTON_ARROW_DOWN: - { - aResult->SizeTo(kMenuScrollArrowSize.width, kMenuScrollArrowSize.height); - *aIsOverridable = false; - break; - } - - case NS_THEME_MENUARROW: - { - aResult->SizeTo(kMenuarrowSize.width, kMenuarrowSize.height); - *aIsOverridable = false; - break; - } - - case NS_THEME_MAC_DISCLOSURE_BUTTON_OPEN: - case NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED: - { - aResult->SizeTo(kDisclosureButtonSize.width, kDisclosureButtonSize.height); - *aIsOverridable = false; - break; - } - - case NS_THEME_MAC_HELP_BUTTON: - { - aResult->SizeTo(kHelpButtonSize.width, kHelpButtonSize.height); - *aIsOverridable = false; - break; - } - - case NS_THEME_TOOLBARBUTTON: - { - aResult->SizeTo(0, toolbarButtonHeights[miniControlSize]); - break; - } - - case NS_THEME_SPINNER: - case NS_THEME_SPINNER_UPBUTTON: - case NS_THEME_SPINNER_DOWNBUTTON: - { - SInt32 buttonHeight = 0, buttonWidth = 0; - if (aFrame->GetContent()->IsXULElement()) { - ::GetThemeMetric(kThemeMetricLittleArrowsWidth, &buttonWidth); - ::GetThemeMetric(kThemeMetricLittleArrowsHeight, &buttonHeight); - } else { - NSSize size = - spinnerSettings.minimumSizes[EnumSizeForCocoaSize(NSMiniControlSize)]; - buttonWidth = size.width; - buttonHeight = size.height; - if (aWidgetType != NS_THEME_SPINNER) { - // the buttons are half the height of the spinner - buttonHeight /= 2; - } - } - aResult->SizeTo(buttonWidth, buttonHeight); - *aIsOverridable = true; - break; - } - - case NS_THEME_MENULIST: - case NS_THEME_MENULIST_BUTTON: - { - SInt32 popupHeight = 0; - ::GetThemeMetric(kThemeMetricPopupButtonHeight, &popupHeight); - aResult->SizeTo(0, popupHeight); - break; - } - - case NS_THEME_NUMBER_INPUT: - case NS_THEME_TEXTFIELD: - case NS_THEME_TEXTFIELD_MULTILINE: - case NS_THEME_SEARCHFIELD: - { - // at minimum, we should be tall enough for 9pt text. - // I'm using hardcoded values here because the appearance manager - // values for the frame size are incorrect. - aResult->SizeTo(0, (2 + 2) /* top */ + 9 + (1 + 1) /* bottom */); - break; - } - - case NS_THEME_WINDOW_BUTTON_BOX: { - NSSize size = WindowButtonsSize(aFrame); - aResult->SizeTo(size.width, size.height); - *aIsOverridable = false; - break; - } - - case NS_THEME_MAC_FULLSCREEN_BUTTON: { - if ([NativeWindowForFrame(aFrame) respondsToSelector:@selector(toggleFullScreen:)] && - !nsCocoaFeatures::OnYosemiteOrLater()) { - // This value is hardcoded because it's needed before we can measure the - // position and size of the fullscreen button. - aResult->SizeTo(16, 17); - } - *aIsOverridable = false; - break; - } - - case NS_THEME_PROGRESSBAR: - { - SInt32 barHeight = 0; - ::GetThemeMetric(kThemeMetricNormalProgressBarThickness, &barHeight); - aResult->SizeTo(0, barHeight); - break; - } - - case NS_THEME_TREETWISTY: - case NS_THEME_TREETWISTYOPEN: - { - SInt32 twistyHeight = 0, twistyWidth = 0; - ::GetThemeMetric(kThemeMetricDisclosureButtonWidth, &twistyWidth); - ::GetThemeMetric(kThemeMetricDisclosureButtonHeight, &twistyHeight); - aResult->SizeTo(twistyWidth, twistyHeight); - *aIsOverridable = false; - break; - } - - case NS_THEME_TREEHEADER: - case NS_THEME_TREEHEADERCELL: - { - SInt32 headerHeight = 0; - ::GetThemeMetric(kThemeMetricListHeaderHeight, &headerHeight); - aResult->SizeTo(0, headerHeight - 1); // We don't need the top border. - break; - } - - case NS_THEME_TAB: - { - aResult->SizeTo(0, tabHeights[miniControlSize]); - break; - } - - case NS_THEME_RANGE: - { - // The Mac Appearance Manager API (the old API we're currently using) - // doesn't define constants to obtain a minimum size for sliders. We use - // the "thickness" of a slider that has default dimensions for both the - // minimum width and height to get something sane and so that paint - // invalidation works. - SInt32 size = 0; - if (IsRangeHorizontal(aFrame)) { - ::GetThemeMetric(kThemeMetricHSliderHeight, &size); - } else { - ::GetThemeMetric(kThemeMetricVSliderWidth, &size); - } - aResult->SizeTo(size, size); - *aIsOverridable = true; - break; - } - - case NS_THEME_RANGE_THUMB: - { - SInt32 width = 0; - SInt32 height = 0; - ::GetThemeMetric(kThemeMetricSliderMinThumbWidth, &width); - ::GetThemeMetric(kThemeMetricSliderMinThumbHeight, &height); - aResult->SizeTo(width, height); - *aIsOverridable = false; - break; - } - - case NS_THEME_SCALE_HORIZONTAL: - { - SInt32 scaleHeight = 0; - ::GetThemeMetric(kThemeMetricHSliderHeight, &scaleHeight); - aResult->SizeTo(scaleHeight, scaleHeight); - *aIsOverridable = false; - break; - } - - case NS_THEME_SCALE_VERTICAL: - { - SInt32 scaleWidth = 0; - ::GetThemeMetric(kThemeMetricVSliderWidth, &scaleWidth); - aResult->SizeTo(scaleWidth, scaleWidth); - *aIsOverridable = false; - break; - } - - case NS_THEME_SCROLLBARTHUMB_HORIZONTAL: - case NS_THEME_SCROLLBARTHUMB_VERTICAL: - { - // Find our parent scrollbar frame in order to find out whether we're in - // a small or a large scrollbar. - nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame); - if (!scrollbarFrame) { - return NS_ERROR_FAILURE; - } - - bool isSmall = (scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL); - bool isHorizontal = (aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL); - int32_t& minSize = isHorizontal ? aResult->width : aResult->height; - minSize = isSmall ? kSmallScrollbarThumbMinSize : kRegularScrollbarThumbMinSize; - break; - } - - case NS_THEME_SCROLLBAR: - case NS_THEME_SCROLLBAR_SMALL: - case NS_THEME_SCROLLBARTRACK_VERTICAL: - case NS_THEME_SCROLLBARTRACK_HORIZONTAL: - { - *aIsOverridable = false; - - if (nsLookAndFeel::UseOverlayScrollbars()) { - nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame); - if (scrollbarFrame && - scrollbarFrame->StyleDisplay()->mAppearance == - NS_THEME_SCROLLBAR_SMALL) { - aResult->SizeTo(14, 14); - } - else { - aResult->SizeTo(16, 16); - } - if (IsScrollbarWidthThin(aFrame)) { - aResult->SizeTo(8, 8); - } - break; - } - - // yeah, i know i'm cheating a little here, but i figure that it - // really doesn't matter if the scrollbar is vertical or horizontal - // and the width metric is a really good metric for every piece - // of the scrollbar. - - nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame); - if (!scrollbarFrame) return NS_ERROR_FAILURE; - - int32_t themeMetric = (scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL) ? - kThemeMetricSmallScrollBarWidth : - kThemeMetricScrollBarWidth; - SInt32 scrollbarWidth = 0; - ::GetThemeMetric(themeMetric, &scrollbarWidth); - if (IsScrollbarWidthThin(aFrame)) { - scrollbarWidth /= 2; - } - aResult->SizeTo(scrollbarWidth, scrollbarWidth); - break; - } - - case NS_THEME_SCROLLBAR_NON_DISAPPEARING: - { - int32_t themeMetric = kThemeMetricScrollBarWidth; - - if (aFrame) { - nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame); - if (scrollbarFrame && - scrollbarFrame->StyleDisplay()->mAppearance == - NS_THEME_SCROLLBAR_SMALL) { - // XXX We're interested in the width of non-disappearing scrollbars - // to leave enough space for a dropmarker in non-native styled - // comboboxes (bug 869314). It isn't clear to me if comboboxes can - // ever have small scrollbars. - themeMetric = kThemeMetricSmallScrollBarWidth; - } - } - - SInt32 scrollbarWidth = 0; - ::GetThemeMetric(themeMetric, &scrollbarWidth); - aResult->SizeTo(scrollbarWidth, scrollbarWidth); - break; - } - - case NS_THEME_SCROLLBARBUTTON_UP: - case NS_THEME_SCROLLBARBUTTON_DOWN: - case NS_THEME_SCROLLBARBUTTON_LEFT: - case NS_THEME_SCROLLBARBUTTON_RIGHT: - { - if (!IsScrollbarWidthThin(aFrame)) { - // Get scrollbar button metrics from the system, except in the case of - // thin scrollbars, where we leave them at 0 (collapse) - - nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame); - if (!scrollbarFrame) return NS_ERROR_FAILURE; - - // Since there is no NS_THEME_SCROLLBARBUTTON_UP_SMALL we need to ask the parent what appearance style it has. - int32_t themeMetric = (scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL) ? - kThemeMetricSmallScrollBarWidth : - kThemeMetricScrollBarWidth; - SInt32 scrollbarWidth = 0; - ::GetThemeMetric(themeMetric, &scrollbarWidth); - - // It seems that for both sizes of scrollbar, the buttons are one pixel "longer". - if (aWidgetType == NS_THEME_SCROLLBARBUTTON_LEFT || aWidgetType == NS_THEME_SCROLLBARBUTTON_RIGHT) - aResult->SizeTo(scrollbarWidth+1, scrollbarWidth); - else - aResult->SizeTo(scrollbarWidth, scrollbarWidth+1); - } - *aIsOverridable = false; - break; - } - case NS_THEME_RESIZER: - { - HIThemeGrowBoxDrawInfo drawInfo; - drawInfo.version = 0; - drawInfo.state = kThemeStateActive; - drawInfo.kind = kHIThemeGrowBoxKindNormal; - drawInfo.direction = kThemeGrowRight | kThemeGrowDown; - drawInfo.size = kHIThemeGrowBoxSizeNormal; - HIPoint pnt = { 0, 0 }; - HIRect bounds; - HIThemeGetGrowBoxBounds(&pnt, &drawInfo, &bounds); - aResult->SizeTo(bounds.size.width, bounds.size.height); - *aIsOverridable = false; - } - } - - if (IsHiDPIContext(aPresContext)) { - *aResult = *aResult * 2; - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsNativeThemeCocoa::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType, - nsIAtom* aAttribute, bool* aShouldRepaint, - const nsAttrValue* aOldValue) -{ - // Some widget types just never change state. - switch (aWidgetType) { - case NS_THEME_WINDOW_TITLEBAR: - case NS_THEME_TOOLBOX: - case NS_THEME_TOOLBAR: - case NS_THEME_STATUSBAR: - case NS_THEME_STATUSBARPANEL: - case NS_THEME_RESIZERPANEL: - case NS_THEME_TOOLTIP: - case NS_THEME_TABPANELS: - case NS_THEME_TABPANEL: - case NS_THEME_DIALOG: - case NS_THEME_MENUPOPUP: - case NS_THEME_GROUPBOX: - case NS_THEME_PROGRESSCHUNK: - case NS_THEME_PROGRESSCHUNK_VERTICAL: - case NS_THEME_PROGRESSBAR: - case NS_THEME_PROGRESSBAR_VERTICAL: - case NS_THEME_METERBAR: - case NS_THEME_METERCHUNK: - case NS_THEME_MAC_VIBRANCY_LIGHT: - case NS_THEME_MAC_VIBRANCY_DARK: - *aShouldRepaint = false; - return NS_OK; - } - - // XXXdwh Not sure what can really be done here. Can at least guess for - // specific widgets that they're highly unlikely to have certain states. - // For example, a toolbar doesn't care about any states. - if (!aAttribute) { - // Hover/focus/active changed. Always repaint. - *aShouldRepaint = true; - } else { - // Check the attribute to see if it's relevant. - // disabled, checked, dlgtype, default, etc. - *aShouldRepaint = false; - if (aAttribute == nsGkAtoms::disabled || - aAttribute == nsGkAtoms::checked || - aAttribute == nsGkAtoms::selected || - aAttribute == nsGkAtoms::visuallyselected || - aAttribute == nsGkAtoms::menuactive || - aAttribute == nsGkAtoms::sortDirection || - aAttribute == nsGkAtoms::focused || - aAttribute == nsGkAtoms::_default || - aAttribute == nsGkAtoms::open || - aAttribute == nsGkAtoms::hover) - *aShouldRepaint = true; - } - - return NS_OK; -} - -NS_IMETHODIMP -nsNativeThemeCocoa::ThemeChanged() -{ - // This is unimplemented because we don't care if gecko changes its theme - // and Mac OS X doesn't have themes. - return NS_OK; -} - -bool -nsNativeThemeCocoa::ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* aFrame, - uint8_t aWidgetType) -{ - // We don't have CSS set up to render non-native scrollbars on Mac OS X so we - // render natively even if native theme support is disabled. - if (aWidgetType != NS_THEME_SCROLLBAR && - aPresContext && !aPresContext->PresShell()->IsThemeSupportEnabled()) - return false; - - // if this is a dropdown button in a combobox the answer is always no - if (aWidgetType == NS_THEME_MENULIST_BUTTON) { - nsIFrame* parentFrame = aFrame->GetParent(); - if (parentFrame && (parentFrame->GetType() == nsGkAtoms::comboboxControlFrame)) - return false; - } - - switch (aWidgetType) { - // Combobox dropdowns don't support native theming in vertical mode. - case NS_THEME_MENULIST: - case NS_THEME_MENULIST_BUTTON: - case NS_THEME_MENULIST_TEXT: - case NS_THEME_MENULIST_TEXTFIELD: - if (aFrame && aFrame->GetWritingMode().IsVertical()) { - return false; - } - MOZ_FALLTHROUGH; - - case NS_THEME_LISTBOX: - - case NS_THEME_DIALOG: - case NS_THEME_WINDOW: - case NS_THEME_WINDOW_BUTTON_BOX: - case NS_THEME_WINDOW_TITLEBAR: - case NS_THEME_CHECKMENUITEM: - case NS_THEME_MENUPOPUP: - case NS_THEME_MENUARROW: - case NS_THEME_MENUITEM: - case NS_THEME_MENUSEPARATOR: - case NS_THEME_MAC_FULLSCREEN_BUTTON: - case NS_THEME_TOOLTIP: - - case NS_THEME_CHECKBOX: - case NS_THEME_CHECKBOX_CONTAINER: - case NS_THEME_RADIO: - case NS_THEME_RADIO_CONTAINER: - case NS_THEME_GROUPBOX: - case NS_THEME_MAC_HELP_BUTTON: - case NS_THEME_MAC_DISCLOSURE_BUTTON_OPEN: - case NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED: - case NS_THEME_BUTTON: - case NS_THEME_BUTTON_ARROW_UP: - case NS_THEME_BUTTON_ARROW_DOWN: - case NS_THEME_BUTTON_BEVEL: - case NS_THEME_TOOLBARBUTTON: - case NS_THEME_SPINNER: - case NS_THEME_SPINNER_UPBUTTON: - case NS_THEME_SPINNER_DOWNBUTTON: - case NS_THEME_TOOLBAR: - case NS_THEME_STATUSBAR: - case NS_THEME_NUMBER_INPUT: - case NS_THEME_TEXTFIELD: - case NS_THEME_TEXTFIELD_MULTILINE: - case NS_THEME_SEARCHFIELD: - case NS_THEME_TOOLBOX: - //case NS_THEME_TOOLBARBUTTON: - case NS_THEME_PROGRESSBAR: - case NS_THEME_PROGRESSBAR_VERTICAL: - case NS_THEME_PROGRESSCHUNK: - case NS_THEME_PROGRESSCHUNK_VERTICAL: - case NS_THEME_METERBAR: - case NS_THEME_METERCHUNK: - case NS_THEME_SEPARATOR: - - case NS_THEME_TABPANELS: - case NS_THEME_TAB: - - case NS_THEME_TREETWISTY: - case NS_THEME_TREETWISTYOPEN: - case NS_THEME_TREEVIEW: - case NS_THEME_TREEHEADER: - case NS_THEME_TREEHEADERCELL: - case NS_THEME_TREEHEADERSORTARROW: - case NS_THEME_TREEITEM: - case NS_THEME_TREELINE: - case NS_THEME_MAC_SOURCE_LIST: - case NS_THEME_MAC_SOURCE_LIST_SELECTION: - case NS_THEME_MAC_ACTIVE_SOURCE_LIST_SELECTION: - - case NS_THEME_RANGE: - - case NS_THEME_SCALE_HORIZONTAL: - case NS_THEME_SCALETHUMB_HORIZONTAL: - case NS_THEME_SCALE_VERTICAL: - case NS_THEME_SCALETHUMB_VERTICAL: - - case NS_THEME_SCROLLBAR: - case NS_THEME_SCROLLBAR_SMALL: - case NS_THEME_SCROLLBARBUTTON_UP: - case NS_THEME_SCROLLBARBUTTON_DOWN: - case NS_THEME_SCROLLBARBUTTON_LEFT: - case NS_THEME_SCROLLBARBUTTON_RIGHT: - case NS_THEME_SCROLLBARTHUMB_HORIZONTAL: - case NS_THEME_SCROLLBARTHUMB_VERTICAL: - case NS_THEME_SCROLLBARTRACK_VERTICAL: - case NS_THEME_SCROLLBARTRACK_HORIZONTAL: - case NS_THEME_SCROLLBAR_NON_DISAPPEARING: - return !IsWidgetStyled(aPresContext, aFrame, aWidgetType); - - case NS_THEME_RESIZER: - { - nsIFrame* parentFrame = aFrame->GetParent(); - if (!parentFrame || parentFrame->GetType() != nsGkAtoms::scrollFrame) - return true; - - // Note that IsWidgetStyled is not called for resizers on Mac. This is - // because for scrollable containers, the native resizer looks better - // when (non-overlay) scrollbars are present even when the style is - // overriden, and the custom transparent resizer looks better when - // scrollbars are not present. - nsIScrollableFrame* scrollFrame = do_QueryFrame(parentFrame); - return (!nsLookAndFeel::UseOverlayScrollbars() && - scrollFrame && scrollFrame->GetScrollbarVisibility()); - } - - case NS_THEME_FOCUS_OUTLINE: - return true; - - case NS_THEME_MAC_VIBRANCY_LIGHT: - case NS_THEME_MAC_VIBRANCY_DARK: - return VibrancyManager::SystemSupportsVibrancy(); - } - - return false; -} - -bool -nsNativeThemeCocoa::WidgetIsContainer(uint8_t aWidgetType) -{ - // flesh this out at some point - switch (aWidgetType) { - case NS_THEME_MENULIST_BUTTON: - case NS_THEME_RADIO: - case NS_THEME_CHECKBOX: - case NS_THEME_PROGRESSBAR: - case NS_THEME_METERBAR: - case NS_THEME_RANGE: - case NS_THEME_MAC_HELP_BUTTON: - case NS_THEME_MAC_DISCLOSURE_BUTTON_OPEN: - case NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED: - return false; - } - return true; -} - -bool -nsNativeThemeCocoa::ThemeDrawsFocusForWidget(uint8_t aWidgetType) -{ - if (aWidgetType == NS_THEME_MENULIST || - aWidgetType == NS_THEME_MENULIST_TEXTFIELD || - aWidgetType == NS_THEME_BUTTON || - aWidgetType == NS_THEME_MAC_HELP_BUTTON || - aWidgetType == NS_THEME_MAC_DISCLOSURE_BUTTON_OPEN || - aWidgetType == NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED || - aWidgetType == NS_THEME_RADIO || - aWidgetType == NS_THEME_RANGE || - aWidgetType == NS_THEME_CHECKBOX) - return true; - - return false; -} - -bool -nsNativeThemeCocoa::ThemeNeedsComboboxDropmarker() -{ - return false; -} - -bool -nsNativeThemeCocoa::WidgetAppearanceDependsOnWindowFocus(uint8_t aWidgetType) -{ - switch (aWidgetType) { - case NS_THEME_DIALOG: - case NS_THEME_GROUPBOX: - case NS_THEME_TABPANELS: - case NS_THEME_BUTTON_ARROW_UP: - case NS_THEME_BUTTON_ARROW_DOWN: - case NS_THEME_CHECKMENUITEM: - case NS_THEME_MENUPOPUP: - case NS_THEME_MENUARROW: - case NS_THEME_MENUITEM: - case NS_THEME_MENUSEPARATOR: - case NS_THEME_TOOLTIP: - case NS_THEME_SPINNER: - case NS_THEME_SPINNER_UPBUTTON: - case NS_THEME_SPINNER_DOWNBUTTON: - case NS_THEME_SEPARATOR: - case NS_THEME_TOOLBOX: - case NS_THEME_NUMBER_INPUT: - case NS_THEME_TEXTFIELD: - case NS_THEME_TREEVIEW: - case NS_THEME_TREELINE: - case NS_THEME_TEXTFIELD_MULTILINE: - case NS_THEME_LISTBOX: - case NS_THEME_RESIZER: - return false; - default: - return true; - } -} - -bool -nsNativeThemeCocoa::IsWindowSheet(nsIFrame* aFrame) -{ - NSWindow* win = NativeWindowForFrame(aFrame); - id winDelegate = [win delegate]; - nsIWidget* widget = [(WindowDelegate *)winDelegate geckoWidget]; - if (!widget) { - return false; - } - return (widget->WindowType() == eWindowType_sheet); -} - -bool -nsNativeThemeCocoa::NeedToClearBackgroundBehindWidget(nsIFrame* aFrame, - uint8_t aWidgetType) -{ - switch (aWidgetType) { - case NS_THEME_MAC_SOURCE_LIST: - // If we're in a XUL tree, we don't want to clear the background behind the - // selections below, since that would make our source list to not pick up - // the right font smoothing background. But since we don't call this method - // in nsTreeBodyFrame::BuildDisplayList, we never get here. - case NS_THEME_MAC_SOURCE_LIST_SELECTION: - case NS_THEME_MAC_ACTIVE_SOURCE_LIST_SELECTION: - case NS_THEME_MAC_VIBRANCY_LIGHT: - case NS_THEME_MAC_VIBRANCY_DARK: - case NS_THEME_TOOLTIP: - case NS_THEME_MENUPOPUP: - case NS_THEME_MENUITEM: - case NS_THEME_CHECKMENUITEM: - return true; - case NS_THEME_DIALOG: - return IsWindowSheet(aFrame); - default: - return false; - } -} - -static nscolor ConvertNSColor(NSColor* aColor) -{ - NSColor* deviceColor = [aColor colorUsingColorSpaceName:NSDeviceRGBColorSpace]; - return NS_RGBA((unsigned int)([deviceColor redComponent] * 255.0), - (unsigned int)([deviceColor greenComponent] * 255.0), - (unsigned int)([deviceColor blueComponent] * 255.0), - (unsigned int)([deviceColor alphaComponent] * 255.0)); -} - -bool -nsNativeThemeCocoa::WidgetProvidesFontSmoothingBackgroundColor(nsIFrame* aFrame, - uint8_t aWidgetType, - nscolor* aColor) -{ - switch (aWidgetType) { - case NS_THEME_MAC_SOURCE_LIST: - case NS_THEME_MAC_SOURCE_LIST_SELECTION: - case NS_THEME_MAC_ACTIVE_SOURCE_LIST_SELECTION: - case NS_THEME_MAC_VIBRANCY_LIGHT: - case NS_THEME_MAC_VIBRANCY_DARK: - case NS_THEME_TOOLTIP: - case NS_THEME_MENUPOPUP: - case NS_THEME_MENUITEM: - case NS_THEME_CHECKMENUITEM: - case NS_THEME_DIALOG: - { - if ((aWidgetType == NS_THEME_DIALOG && !IsWindowSheet(aFrame)) || - ((aWidgetType == NS_THEME_MAC_SOURCE_LIST_SELECTION || - aWidgetType == NS_THEME_MAC_ACTIVE_SOURCE_LIST_SELECTION) && - !IsInSourceList(aFrame))) { - return false; - } - ChildView* childView = ChildViewForFrame(aFrame); - if (childView) { - ThemeGeometryType type = ThemeGeometryTypeForWidget(aFrame, aWidgetType); - NSColor* color = [childView vibrancyFontSmoothingBackgroundColorForThemeGeometryType:type]; - *aColor = ConvertNSColor(color); - return true; - } - return false; - } - default: - return false; - } -} - -nsITheme::ThemeGeometryType -nsNativeThemeCocoa::ThemeGeometryTypeForWidget(nsIFrame* aFrame, uint8_t aWidgetType) -{ - switch (aWidgetType) { - case NS_THEME_WINDOW_TITLEBAR: - return eThemeGeometryTypeTitlebar; - case NS_THEME_TOOLBAR: - return eThemeGeometryTypeToolbar; - case NS_THEME_TOOLBOX: - return eThemeGeometryTypeToolbox; - case NS_THEME_WINDOW_BUTTON_BOX: - return eThemeGeometryTypeWindowButtons; - case NS_THEME_MAC_FULLSCREEN_BUTTON: - return eThemeGeometryTypeFullscreenButton; - case NS_THEME_MAC_VIBRANCY_LIGHT: - return eThemeGeometryTypeVibrancyLight; - case NS_THEME_MAC_VIBRANCY_DARK: - return eThemeGeometryTypeVibrancyDark; - case NS_THEME_TOOLTIP: - return eThemeGeometryTypeTooltip; - case NS_THEME_MENUPOPUP: - return eThemeGeometryTypeMenu; - case NS_THEME_MENUITEM: - case NS_THEME_CHECKMENUITEM: { - EventStates eventState = GetContentState(aFrame, aWidgetType); - bool isDisabled = IsDisabled(aFrame, eventState); - bool isSelected = !isDisabled && CheckBooleanAttr(aFrame, nsGkAtoms::menuactive); - return isSelected ? eThemeGeometryTypeHighlightedMenuItem : eThemeGeometryTypeMenu; - } - case NS_THEME_DIALOG: - return IsWindowSheet(aFrame) ? eThemeGeometryTypeSheet : eThemeGeometryTypeUnknown; - case NS_THEME_MAC_SOURCE_LIST: - return eThemeGeometryTypeSourceList; - case NS_THEME_MAC_SOURCE_LIST_SELECTION: - return IsInSourceList(aFrame) ? eThemeGeometryTypeSourceListSelection - : eThemeGeometryTypeUnknown; - case NS_THEME_MAC_ACTIVE_SOURCE_LIST_SELECTION: - return IsInSourceList(aFrame) ? eThemeGeometryTypeActiveSourceListSelection - : eThemeGeometryTypeUnknown; - default: - return eThemeGeometryTypeUnknown; - } -} - -nsITheme::Transparency -nsNativeThemeCocoa::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType) -{ - switch (aWidgetType) { - case NS_THEME_MENUPOPUP: - case NS_THEME_TOOLTIP: - return eTransparent; - - case NS_THEME_DIALOG: - return IsWindowSheet(aFrame) ? eTransparent : eOpaque; - - case NS_THEME_SCROLLBAR_SMALL: - case NS_THEME_SCROLLBAR: - return nsLookAndFeel::UseOverlayScrollbars() ? eTransparent : eOpaque; - - case NS_THEME_STATUSBAR: - // Knowing that scrollbars and statusbars are opaque improves - // performance, because we create layers for them. - return eOpaque; - - case NS_THEME_TOOLBAR: - return eOpaque; - - default: - return eUnknownTransparency; - } -} diff --git a/widget/cocoa/nsNativeThemeColors.h b/widget/cocoa/nsNativeThemeColors.h deleted file mode 100644 index b1691b5163..0000000000 --- a/widget/cocoa/nsNativeThemeColors.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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 nsNativeThemeColors_h_ -#define nsNativeThemeColors_h_ - -#include "nsCocoaFeatures.h" -#import <Cocoa/Cocoa.h> - -enum ColorName { - toolbarTopBorderGrey, - toolbarFillGrey, - toolbarBottomBorderGrey, -}; - -static const int sLionThemeColors[][2] = { - /* { active window, inactive window } */ - // toolbar: - { 0xD0, 0xF0 }, // top separator line - { 0xB2, 0xE1 }, // fill color - { 0x59, 0x87 }, // bottom separator line -}; - -static const int sYosemiteThemeColors[][2] = { - /* { active window, inactive window } */ - // toolbar: - { 0xBD, 0xDF }, // top separator line - { 0xD3, 0xF6 }, // fill color - { 0xB3, 0xD1 }, // bottom separator line -}; - -__attribute__((unused)) -static int NativeGreyColorAsInt(ColorName name, BOOL isMain) -{ - if (nsCocoaFeatures::OnYosemiteOrLater()) - return sYosemiteThemeColors[name][isMain ? 0 : 1]; - return sLionThemeColors[name][isMain ? 0 : 1]; -} - -__attribute__((unused)) -static float NativeGreyColorAsFloat(ColorName name, BOOL isMain) -{ - return NativeGreyColorAsInt(name, isMain) / 255.0f; -} - -__attribute__((unused)) -static void DrawNativeGreyColorInRect(CGContextRef context, ColorName name, - CGRect rect, BOOL isMain) -{ - float grey = NativeGreyColorAsFloat(name, isMain); - CGContextSetRGBFillColor(context, grey, grey, grey, 1.0f); - CGContextFillRect(context, rect); -} - -#endif // nsNativeThemeColors_h_ diff --git a/widget/cocoa/nsPIWidgetCocoa.idl b/widget/cocoa/nsPIWidgetCocoa.idl deleted file mode 100644 index a8fd8149ce..0000000000 --- a/widget/cocoa/nsPIWidgetCocoa.idl +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- 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/. */ - -#include "nsISupports.idl" - -interface nsIWidget; - -[ptr] native NSWindowPtr(NSWindow); - -// -// nsPIWidgetCocoa -// -// A private interface (unfrozen, private to the widget implementation) that -// gives us access to some extra features on a widget/window. -// -[uuid(f75ff69e-3a51-419e-bd29-042f804bc2ed)] -interface nsPIWidgetCocoa : nsISupports -{ - void SendSetZLevelEvent(); - - // Find the displayed child sheet (if aShown) or a child sheet that - // wants to be displayed (if !aShown) - nsIWidget GetChildSheet(in boolean aShown); - - // Get the parent widget (if any) StandardCreate() was called with. - nsIWidget GetRealParent(); - - // If the object implementing this interface is a sheet, this will return the - // native NSWindow it is attached to - readonly attribute NSWindowPtr sheetWindowParent; - - // True if window is a sheet - readonly attribute boolean isSheet; - -}; // nsPIWidgetCocoa diff --git a/widget/cocoa/nsPrintDialogX.h b/widget/cocoa/nsPrintDialogX.h deleted file mode 100644 index 470f17d99d..0000000000 --- a/widget/cocoa/nsPrintDialogX.h +++ /dev/null @@ -1,68 +0,0 @@ -/* -*- 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 nsPrintDialog_h_ -#define nsPrintDialog_h_ - -#include "nsIPrintDialogService.h" -#include "nsCOMPtr.h" -#include "nsCocoaUtils.h" - -#import <Cocoa/Cocoa.h> - -class nsIPrintSettings; -class nsIStringBundle; - -class nsPrintDialogServiceX : public nsIPrintDialogService -{ -public: - nsPrintDialogServiceX(); - - NS_DECL_ISUPPORTS - - NS_IMETHOD Init() override; - NS_IMETHOD Show(nsPIDOMWindowOuter *aParent, nsIPrintSettings *aSettings, - nsIWebBrowserPrint *aWebBrowserPrint) override; - NS_IMETHOD ShowPageSetup(nsPIDOMWindowOuter *aParent, - nsIPrintSettings *aSettings) override; - -protected: - virtual ~nsPrintDialogServiceX(); -}; - -@interface PrintPanelAccessoryView : NSView -{ - nsIPrintSettings* mSettings; - nsIStringBundle* mPrintBundle; - NSButton* mPrintSelectionOnlyCheckbox; - NSButton* mShrinkToFitCheckbox; - NSButton* mPrintBGColorsCheckbox; - NSButton* mPrintBGImagesCheckbox; - NSButtonCell* mAsLaidOutRadio; - NSButtonCell* mSelectedFrameRadio; - NSButtonCell* mSeparateFramesRadio; - NSPopUpButton* mHeaderLeftList; - NSPopUpButton* mHeaderCenterList; - NSPopUpButton* mHeaderRightList; - NSPopUpButton* mFooterLeftList; - NSPopUpButton* mFooterCenterList; - NSPopUpButton* mFooterRightList; -} - -- (id)initWithSettings:(nsIPrintSettings*)aSettings; - -- (void)exportSettings; - -@end - -@interface PrintPanelAccessoryController : NSViewController <NSPrintPanelAccessorizing> - -- (id)initWithSettings:(nsIPrintSettings*)aSettings; - -- (void)exportSettings; - -@end - -#endif // nsPrintDialog_h_ diff --git a/widget/cocoa/nsPrintDialogX.mm b/widget/cocoa/nsPrintDialogX.mm deleted file mode 100644 index a6d58d5bfb..0000000000 --- a/widget/cocoa/nsPrintDialogX.mm +++ /dev/null @@ -1,682 +0,0 @@ -/* -*- 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/. */ - -#include "mozilla/ArrayUtils.h" - -#include "nsPrintDialogX.h" -#include "nsIPrintSettings.h" -#include "nsIPrintSettingsService.h" -#include "nsPrintSettingsX.h" -#include "nsCOMPtr.h" -#include "nsQueryObject.h" -#include "nsServiceManagerUtils.h" -#include "nsIWebProgressListener.h" -#include "nsIStringBundle.h" -#include "nsIWebBrowserPrint.h" -#include "nsCRT.h" - -#import <Cocoa/Cocoa.h> -#include "nsObjCExceptions.h" - -using namespace mozilla; - -NS_IMPL_ISUPPORTS(nsPrintDialogServiceX, nsIPrintDialogService) - -nsPrintDialogServiceX::nsPrintDialogServiceX() -{ -} - -nsPrintDialogServiceX::~nsPrintDialogServiceX() -{ -} - -NS_IMETHODIMP -nsPrintDialogServiceX::Init() -{ - return NS_OK; -} - -NS_IMETHODIMP -nsPrintDialogServiceX::Show(nsPIDOMWindowOuter *aParent, nsIPrintSettings *aSettings, - nsIWebBrowserPrint *aWebBrowserPrint) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NS_PRECONDITION(aSettings, "aSettings must not be null"); - - RefPtr<nsPrintSettingsX> settingsX(do_QueryObject(aSettings)); - if (!settingsX) - return NS_ERROR_FAILURE; - - nsCOMPtr<nsIPrintSettingsService> printSettingsSvc - = do_GetService("@mozilla.org/gfx/printsettings-service;1"); - - // Set the print job title - char16_t** docTitles; - uint32_t titleCount; - nsresult rv = aWebBrowserPrint->EnumerateDocumentNames(&titleCount, &docTitles); - if (NS_SUCCEEDED(rv) && titleCount > 0) { - CFStringRef cfTitleString = CFStringCreateWithCharacters(NULL, reinterpret_cast<const UniChar*>(docTitles[0]), - NS_strlen(docTitles[0])); - if (cfTitleString) { - ::PMPrintSettingsSetJobName(settingsX->GetPMPrintSettings(), cfTitleString); - CFRelease(cfTitleString); - } - for (int32_t i = titleCount - 1; i >= 0; i--) { - free(docTitles[i]); - } - free(docTitles); - docTitles = NULL; - titleCount = 0; - } - - // Read default print settings from prefs - printSettingsSvc->InitPrintSettingsFromPrefs(settingsX, true, - nsIPrintSettings::kInitSaveNativeData); - NSPrintInfo* printInfo = settingsX->GetCocoaPrintInfo(); - - // Put the print info into the current print operation, since that's where - // [panel runModal] will look for it. We create the view because otherwise - // we'll get unrelated warnings printed to the console. - NSView* tmpView = [[NSView alloc] init]; - NSPrintOperation* printOperation = [NSPrintOperation printOperationWithView:tmpView printInfo:printInfo]; - [NSPrintOperation setCurrentOperation:printOperation]; - - NSPrintPanel* panel = [NSPrintPanel printPanel]; - [panel setOptions:NSPrintPanelShowsCopies - | NSPrintPanelShowsPageRange - | NSPrintPanelShowsPaperSize - | NSPrintPanelShowsOrientation - | NSPrintPanelShowsScaling ]; - PrintPanelAccessoryController* viewController = - [[PrintPanelAccessoryController alloc] initWithSettings:aSettings]; - [panel addAccessoryController:viewController]; - [viewController release]; - - // Show the dialog. - nsCocoaUtils::PrepareForNativeAppModalDialog(); - int button = [panel runModal]; - nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); - - NSPrintInfo* copy = [[[NSPrintOperation currentOperation] printInfo] copy]; - if (!copy) { - return NS_ERROR_OUT_OF_MEMORY; - } - - [NSPrintOperation setCurrentOperation:nil]; - [tmpView release]; - - if (button != NSFileHandlingPanelOKButton) - return NS_ERROR_ABORT; - - settingsX->SetCocoaPrintInfo(copy); - settingsX->InitUnwriteableMargin(); - - // Save settings unless saving is pref'd off - if (Preferences::GetBool("print.save_print_settings", false)) { - printSettingsSvc->SavePrintSettingsToPrefs(settingsX, true, - nsIPrintSettings::kInitSaveNativeData); - } - - // Get coordinate space resolution for converting paper size units to inches - NSWindow *win = [[NSApplication sharedApplication] mainWindow]; - if (win) { - NSDictionary *devDesc = [win deviceDescription]; - if (devDesc) { - NSSize res = [[devDesc objectForKey: NSDeviceResolution] sizeValue]; - float scale = [win backingScaleFactor]; - if (scale > 0) { - settingsX->SetInchesScale(res.width / scale, res.height / scale); - } - } - } - - // Export settings. - [viewController exportSettings]; - - // If "ignore scaling" is checked, overwrite scaling factor with 1. - bool isShrinkToFitChecked; - settingsX->GetShrinkToFit(&isShrinkToFitChecked); - if (isShrinkToFitChecked) { - NSMutableDictionary* dict = [copy dictionary]; - if (dict) { - [dict setObject: [NSNumber numberWithFloat: 1] - forKey: NSPrintScalingFactor]; - } - // Set the scaling factor to 100% in the NSPrintInfo - // object so that it will not affect the paper size - // retrieved from the PMPageFormat routines. - [copy setScalingFactor:1.0]; - } else { - aSettings->SetScaling([copy scalingFactor]); - } - - // Set the adjusted paper size now that we've updated - // the scaling factor. - settingsX->InitAdjustedPaperSize(); - - [copy release]; - - int16_t pageRange; - aSettings->GetPrintRange(&pageRange); - if (pageRange != nsIPrintSettings::kRangeSelection) { - PMPrintSettings nativePrintSettings = settingsX->GetPMPrintSettings(); - UInt32 firstPage, lastPage; - OSStatus status = ::PMGetFirstPage(nativePrintSettings, &firstPage); - if (status == noErr) { - status = ::PMGetLastPage(nativePrintSettings, &lastPage); - if (status == noErr && lastPage != UINT32_MAX) { - aSettings->SetPrintRange(nsIPrintSettings::kRangeSpecifiedPageRange); - aSettings->SetStartPageRange(firstPage); - aSettings->SetEndPageRange(lastPage); - } - } - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsPrintDialogServiceX::ShowPageSetup(nsPIDOMWindowOuter *aParent, - nsIPrintSettings *aNSSettings) -{ - NS_PRECONDITION(aParent, "aParent must not be null"); - NS_PRECONDITION(aNSSettings, "aSettings must not be null"); - NS_ENSURE_TRUE(aNSSettings, NS_ERROR_FAILURE); - - RefPtr<nsPrintSettingsX> settingsX(do_QueryObject(aNSSettings)); - if (!settingsX) - return NS_ERROR_FAILURE; - - NSPrintInfo* printInfo = settingsX->GetCocoaPrintInfo(); - NSPageLayout *pageLayout = [NSPageLayout pageLayout]; - nsCocoaUtils::PrepareForNativeAppModalDialog(); - int button = [pageLayout runModalWithPrintInfo:printInfo]; - nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); - - return button == NSFileHandlingPanelOKButton ? NS_OK : NS_ERROR_ABORT; -} - -// Accessory view - -@interface PrintPanelAccessoryView (Private) - -- (NSString*)localizedString:(const char*)aKey; - -- (int16_t)chosenFrameSetting; - -- (const char*)headerFooterStringForList:(NSPopUpButton*)aList; - -- (void)exportHeaderFooterSettings; - -- (void)initBundle; - -- (NSTextField*)label:(const char*)aLabel - withFrame:(NSRect)aRect - alignment:(NSTextAlignment)aAlignment; - -- (void)addLabel:(const char*)aLabel - withFrame:(NSRect)aRect - alignment:(NSTextAlignment)aAlignment; - -- (void)addLabel:(const char*)aLabel withFrame:(NSRect)aRect; - -- (void)addCenteredLabel:(const char*)aLabel withFrame:(NSRect)aRect; - -- (NSButton*)checkboxWithLabel:(const char*)aLabel andFrame:(NSRect)aRect; - -- (NSPopUpButton*)headerFooterItemListWithFrame:(NSRect)aRect - selectedItem:(const char16_t*)aCurrentString; - -- (void)addOptionsSection; - -- (void)addAppearanceSection; - -- (void)addFramesSection; - -- (void)addHeaderFooterSection; - -- (NSString*)summaryValueForCheckbox:(NSButton*)aCheckbox; - -- (NSString*)framesSummaryValue; - -- (NSString*)headerSummaryValue; - -- (NSString*)footerSummaryValue; - -@end - -static const char sHeaderFooterTags[][4] = {"", "&T", "&U", "&D", "&P", "&PT"}; - -@implementation PrintPanelAccessoryView - -// Public methods - -- (id)initWithSettings:(nsIPrintSettings*)aSettings -{ - [super initWithFrame:NSMakeRect(0, 0, 540, 270)]; - - mSettings = aSettings; - [self initBundle]; - [self addOptionsSection]; - [self addAppearanceSection]; - [self addFramesSection]; - [self addHeaderFooterSection]; - - return self; -} - -- (void)exportSettings -{ - mSettings->SetPrintRange([mPrintSelectionOnlyCheckbox state] == NSOnState ? - (int16_t)nsIPrintSettings::kRangeSelection : - (int16_t)nsIPrintSettings::kRangeAllPages); - mSettings->SetShrinkToFit([mShrinkToFitCheckbox state] == NSOnState); - mSettings->SetPrintBGColors([mPrintBGColorsCheckbox state] == NSOnState); - mSettings->SetPrintBGImages([mPrintBGImagesCheckbox state] == NSOnState); - mSettings->SetPrintFrameType([self chosenFrameSetting]); - - [self exportHeaderFooterSettings]; -} - -- (void)dealloc -{ - NS_IF_RELEASE(mPrintBundle); - [super dealloc]; -} - -// Localization - -- (void)initBundle -{ - nsCOMPtr<nsIStringBundleService> bundleSvc = do_GetService(NS_STRINGBUNDLE_CONTRACTID); - bundleSvc->CreateBundle("chrome://global/locale/printdialog.properties", &mPrintBundle); -} - -- (NSString*)localizedString:(const char*)aKey -{ - if (!mPrintBundle) - return @""; - - nsXPIDLString intlString; - mPrintBundle->GetStringFromName(NS_ConvertUTF8toUTF16(aKey).get(), getter_Copies(intlString)); - NSMutableString* s = [NSMutableString stringWithUTF8String:NS_ConvertUTF16toUTF8(intlString).get()]; - - // Remove all underscores (they're used in the GTK dialog for accesskeys). - [s replaceOccurrencesOfString:@"_" withString:@"" options:0 range:NSMakeRange(0, [s length])]; - return s; -} - -// Widget helpers - -- (NSTextField*)label:(const char*)aLabel - withFrame:(NSRect)aRect - alignment:(NSTextAlignment)aAlignment -{ - NSTextField* label = [[[NSTextField alloc] initWithFrame:aRect] autorelease]; - [label setStringValue:[self localizedString:aLabel]]; - [label setEditable:NO]; - [label setSelectable:NO]; - [label setBezeled:NO]; - [label setBordered:NO]; - [label setDrawsBackground:NO]; - [label setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]]; - [label setAlignment:aAlignment]; - return label; -} - -- (void)addLabel:(const char*)aLabel - withFrame:(NSRect)aRect - alignment:(NSTextAlignment)aAlignment -{ - NSTextField* label = [self label:aLabel withFrame:aRect alignment:aAlignment]; - [self addSubview:label]; -} - -- (void)addLabel:(const char*)aLabel withFrame:(NSRect)aRect -{ - [self addLabel:aLabel withFrame:aRect alignment:NSRightTextAlignment]; -} - -- (void)addCenteredLabel:(const char*)aLabel withFrame:(NSRect)aRect -{ - [self addLabel:aLabel withFrame:aRect alignment:NSCenterTextAlignment]; -} - -- (NSButton*)checkboxWithLabel:(const char*)aLabel andFrame:(NSRect)aRect -{ - aRect.origin.y += 4.0f; - NSButton* checkbox = [[[NSButton alloc] initWithFrame:aRect] autorelease]; - [checkbox setButtonType:NSSwitchButton]; - [checkbox setTitle:[self localizedString:aLabel]]; - [checkbox setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]]; - [checkbox sizeToFit]; - return checkbox; -} - -- (NSPopUpButton*)headerFooterItemListWithFrame:(NSRect)aRect - selectedItem:(const char16_t*)aCurrentString -{ - NSPopUpButton* list = [[[NSPopUpButton alloc] initWithFrame:aRect pullsDown:NO] autorelease]; - [list setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; - [[list cell] setControlSize:NSSmallControlSize]; - NSArray* items = - [NSArray arrayWithObjects:[self localizedString:"headerFooterBlank"], - [self localizedString:"headerFooterTitle"], - [self localizedString:"headerFooterURL"], - [self localizedString:"headerFooterDate"], - [self localizedString:"headerFooterPage"], - [self localizedString:"headerFooterPageTotal"], - nil]; - [list addItemsWithTitles:items]; - - NS_ConvertUTF16toUTF8 currentStringUTF8(aCurrentString); - for (unsigned int i = 0; i < ArrayLength(sHeaderFooterTags); i++) { - if (!strcmp(currentStringUTF8.get(), sHeaderFooterTags[i])) { - [list selectItemAtIndex:i]; - break; - } - } - - return list; -} - -// Build sections - -- (void)addOptionsSection -{ - // Title - [self addLabel:"optionsTitleMac" withFrame:NSMakeRect(0, 240, 151, 22)]; - - // "Print Selection Only" - mPrintSelectionOnlyCheckbox = [self checkboxWithLabel:"selectionOnly" - andFrame:NSMakeRect(156, 240, 0, 0)]; - - bool canPrintSelection; - mSettings->GetPrintOptions(nsIPrintSettings::kEnableSelectionRB, - &canPrintSelection); - [mPrintSelectionOnlyCheckbox setEnabled:canPrintSelection]; - - int16_t printRange; - mSettings->GetPrintRange(&printRange); - if (printRange == nsIPrintSettings::kRangeSelection) { - [mPrintSelectionOnlyCheckbox setState:NSOnState]; - } - - [self addSubview:mPrintSelectionOnlyCheckbox]; - - // "Shrink To Fit" - mShrinkToFitCheckbox = [self checkboxWithLabel:"shrinkToFit" - andFrame:NSMakeRect(156, 218, 0, 0)]; - - bool shrinkToFit; - mSettings->GetShrinkToFit(&shrinkToFit); - [mShrinkToFitCheckbox setState:(shrinkToFit ? NSOnState : NSOffState)]; - - [self addSubview:mShrinkToFitCheckbox]; -} - -- (void)addAppearanceSection -{ - // Title - [self addLabel:"appearanceTitleMac" withFrame:NSMakeRect(0, 188, 151, 22)]; - - // "Print Background Colors" - mPrintBGColorsCheckbox = [self checkboxWithLabel:"printBGColors" - andFrame:NSMakeRect(156, 188, 0, 0)]; - - bool geckoBool; - mSettings->GetPrintBGColors(&geckoBool); - [mPrintBGColorsCheckbox setState:(geckoBool ? NSOnState : NSOffState)]; - - [self addSubview:mPrintBGColorsCheckbox]; - - // "Print Background Images" - mPrintBGImagesCheckbox = [self checkboxWithLabel:"printBGImages" - andFrame:NSMakeRect(156, 166, 0, 0)]; - - mSettings->GetPrintBGImages(&geckoBool); - [mPrintBGImagesCheckbox setState:(geckoBool ? NSOnState : NSOffState)]; - - [self addSubview:mPrintBGImagesCheckbox]; -} - -- (void)addFramesSection -{ - // Title - [self addLabel:"framesTitleMac" withFrame:NSMakeRect(0, 124, 151, 22)]; - - // Radio matrix - NSButtonCell *radio = [[NSButtonCell alloc] init]; - [radio setButtonType:NSRadioButton]; - [radio setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]]; - NSMatrix *matrix = [[NSMatrix alloc] initWithFrame:NSMakeRect(156, 81, 400, 66) - mode:NSRadioModeMatrix - prototype:(NSCell*)radio - numberOfRows:3 - numberOfColumns:1]; - [radio release]; - [matrix setCellSize:NSMakeSize(400, 21)]; - [self addSubview:matrix]; - [matrix release]; - NSArray *cellArray = [matrix cells]; - mAsLaidOutRadio = [cellArray objectAtIndex:0]; - mSelectedFrameRadio = [cellArray objectAtIndex:1]; - mSeparateFramesRadio = [cellArray objectAtIndex:2]; - [mAsLaidOutRadio setTitle:[self localizedString:"asLaidOut"]]; - [mSelectedFrameRadio setTitle:[self localizedString:"selectedFrame"]]; - [mSeparateFramesRadio setTitle:[self localizedString:"separateFrames"]]; - - // Radio enabled state - int16_t frameUIFlag; - mSettings->GetHowToEnableFrameUI(&frameUIFlag); - if (frameUIFlag == nsIPrintSettings::kFrameEnableNone) { - [mAsLaidOutRadio setEnabled:NO]; - [mSelectedFrameRadio setEnabled:NO]; - [mSeparateFramesRadio setEnabled:NO]; - } else if (frameUIFlag == nsIPrintSettings::kFrameEnableAsIsAndEach) { - [mSelectedFrameRadio setEnabled:NO]; - } - - // Radio values - int16_t printFrameType; - mSettings->GetPrintFrameType(&printFrameType); - switch (printFrameType) { - case nsIPrintSettings::kFramesAsIs: - [mAsLaidOutRadio setState:NSOnState]; - break; - case nsIPrintSettings::kSelectedFrame: - [mSelectedFrameRadio setState:NSOnState]; - break; - case nsIPrintSettings::kEachFrameSep: - [mSeparateFramesRadio setState:NSOnState]; - break; - } -} - -- (void)addHeaderFooterSection -{ - // Labels - [self addLabel:"pageHeadersTitleMac" withFrame:NSMakeRect(0, 44, 151, 22)]; - [self addLabel:"pageFootersTitleMac" withFrame:NSMakeRect(0, 0, 151, 22)]; - [self addCenteredLabel:"left" withFrame:NSMakeRect(156, 22, 100, 22)]; - [self addCenteredLabel:"center" withFrame:NSMakeRect(256, 22, 100, 22)]; - [self addCenteredLabel:"right" withFrame:NSMakeRect(356, 22, 100, 22)]; - - // Lists - nsXPIDLString sel; - - mSettings->GetHeaderStrLeft(getter_Copies(sel)); - mHeaderLeftList = [self headerFooterItemListWithFrame:NSMakeRect(156, 44, 100, 22) - selectedItem:sel]; - [self addSubview:mHeaderLeftList]; - - mSettings->GetHeaderStrCenter(getter_Copies(sel)); - mHeaderCenterList = [self headerFooterItemListWithFrame:NSMakeRect(256, 44, 100, 22) - selectedItem:sel]; - [self addSubview:mHeaderCenterList]; - - mSettings->GetHeaderStrRight(getter_Copies(sel)); - mHeaderRightList = [self headerFooterItemListWithFrame:NSMakeRect(356, 44, 100, 22) - selectedItem:sel]; - [self addSubview:mHeaderRightList]; - - mSettings->GetFooterStrLeft(getter_Copies(sel)); - mFooterLeftList = [self headerFooterItemListWithFrame:NSMakeRect(156, 0, 100, 22) - selectedItem:sel]; - [self addSubview:mFooterLeftList]; - - mSettings->GetFooterStrCenter(getter_Copies(sel)); - mFooterCenterList = [self headerFooterItemListWithFrame:NSMakeRect(256, 0, 100, 22) - selectedItem:sel]; - [self addSubview:mFooterCenterList]; - - mSettings->GetFooterStrRight(getter_Copies(sel)); - mFooterRightList = [self headerFooterItemListWithFrame:NSMakeRect(356, 0, 100, 22) - selectedItem:sel]; - [self addSubview:mFooterRightList]; -} - -// Export settings - -- (int16_t)chosenFrameSetting -{ - if ([mAsLaidOutRadio state] == NSOnState) - return nsIPrintSettings::kFramesAsIs; - if ([mSelectedFrameRadio state] == NSOnState) - return nsIPrintSettings::kSelectedFrame; - if ([mSeparateFramesRadio state] == NSOnState) - return nsIPrintSettings::kEachFrameSep; - return nsIPrintSettings::kNoFrames; -} - -- (const char*)headerFooterStringForList:(NSPopUpButton*)aList -{ - NSInteger index = [aList indexOfSelectedItem]; - NS_ASSERTION(index < NSInteger(ArrayLength(sHeaderFooterTags)), "Index of dropdown is higher than expected!"); - return sHeaderFooterTags[index]; -} - -- (void)exportHeaderFooterSettings -{ - const char* headerFooterStr; - headerFooterStr = [self headerFooterStringForList:mHeaderLeftList]; - mSettings->SetHeaderStrLeft(NS_ConvertUTF8toUTF16(headerFooterStr).get()); - - headerFooterStr = [self headerFooterStringForList:mHeaderCenterList]; - mSettings->SetHeaderStrCenter(NS_ConvertUTF8toUTF16(headerFooterStr).get()); - - headerFooterStr = [self headerFooterStringForList:mHeaderRightList]; - mSettings->SetHeaderStrRight(NS_ConvertUTF8toUTF16(headerFooterStr).get()); - - headerFooterStr = [self headerFooterStringForList:mFooterLeftList]; - mSettings->SetFooterStrLeft(NS_ConvertUTF8toUTF16(headerFooterStr).get()); - - headerFooterStr = [self headerFooterStringForList:mFooterCenterList]; - mSettings->SetFooterStrCenter(NS_ConvertUTF8toUTF16(headerFooterStr).get()); - - headerFooterStr = [self headerFooterStringForList:mFooterRightList]; - mSettings->SetFooterStrRight(NS_ConvertUTF8toUTF16(headerFooterStr).get()); -} - -// Summary - -- (NSString*)summaryValueForCheckbox:(NSButton*)aCheckbox -{ - if (![aCheckbox isEnabled]) - return [self localizedString:"summaryNAValue"]; - - return [aCheckbox state] == NSOnState ? - [self localizedString:"summaryOnValue"] : - [self localizedString:"summaryOffValue"]; -} - -- (NSString*)framesSummaryValue -{ - switch([self chosenFrameSetting]) { - case nsIPrintSettings::kFramesAsIs: - return [self localizedString:"asLaidOut"]; - case nsIPrintSettings::kSelectedFrame: - return [self localizedString:"selectedFrame"]; - case nsIPrintSettings::kEachFrameSep: - return [self localizedString:"separateFrames"]; - } - return [self localizedString:"summaryNAValue"]; -} - -- (NSString*)headerSummaryValue -{ - return [[mHeaderLeftList titleOfSelectedItem] stringByAppendingString: - [@", " stringByAppendingString: - [[mHeaderCenterList titleOfSelectedItem] stringByAppendingString: - [@", " stringByAppendingString: - [mHeaderRightList titleOfSelectedItem]]]]]; -} - -- (NSString*)footerSummaryValue -{ - return [[mFooterLeftList titleOfSelectedItem] stringByAppendingString: - [@", " stringByAppendingString: - [[mFooterCenterList titleOfSelectedItem] stringByAppendingString: - [@", " stringByAppendingString: - [mFooterRightList titleOfSelectedItem]]]]]; -} - -- (NSArray*)localizedSummaryItems -{ - return [NSArray arrayWithObjects: - [NSDictionary dictionaryWithObjectsAndKeys: - [self localizedString:"summaryFramesTitle"], NSPrintPanelAccessorySummaryItemNameKey, - [self framesSummaryValue], NSPrintPanelAccessorySummaryItemDescriptionKey, nil], - [NSDictionary dictionaryWithObjectsAndKeys: - [self localizedString:"summarySelectionOnlyTitle"], NSPrintPanelAccessorySummaryItemNameKey, - [self summaryValueForCheckbox:mPrintSelectionOnlyCheckbox], NSPrintPanelAccessorySummaryItemDescriptionKey, nil], - [NSDictionary dictionaryWithObjectsAndKeys: - [self localizedString:"summaryShrinkToFitTitle"], NSPrintPanelAccessorySummaryItemNameKey, - [self summaryValueForCheckbox:mShrinkToFitCheckbox], NSPrintPanelAccessorySummaryItemDescriptionKey, nil], - [NSDictionary dictionaryWithObjectsAndKeys: - [self localizedString:"summaryPrintBGColorsTitle"], NSPrintPanelAccessorySummaryItemNameKey, - [self summaryValueForCheckbox:mPrintBGColorsCheckbox], NSPrintPanelAccessorySummaryItemDescriptionKey, nil], - [NSDictionary dictionaryWithObjectsAndKeys: - [self localizedString:"summaryPrintBGImagesTitle"], NSPrintPanelAccessorySummaryItemNameKey, - [self summaryValueForCheckbox:mPrintBGImagesCheckbox], NSPrintPanelAccessorySummaryItemDescriptionKey, nil], - [NSDictionary dictionaryWithObjectsAndKeys: - [self localizedString:"summaryHeaderTitle"], NSPrintPanelAccessorySummaryItemNameKey, - [self headerSummaryValue], NSPrintPanelAccessorySummaryItemDescriptionKey, nil], - [NSDictionary dictionaryWithObjectsAndKeys: - [self localizedString:"summaryFooterTitle"], NSPrintPanelAccessorySummaryItemNameKey, - [self footerSummaryValue], NSPrintPanelAccessorySummaryItemDescriptionKey, nil], - nil]; -} - -@end - -// Accessory controller - -@implementation PrintPanelAccessoryController - -- (id)initWithSettings:(nsIPrintSettings*)aSettings -{ - [super initWithNibName:nil bundle:nil]; - - NSView* accView = [[PrintPanelAccessoryView alloc] initWithSettings:aSettings]; - [self setView:accView]; - [accView release]; - return self; -} - -- (void)exportSettings -{ - return [(PrintPanelAccessoryView*)[self view] exportSettings]; -} - -- (NSArray *)localizedSummaryItems -{ - return [(PrintPanelAccessoryView*)[self view] localizedSummaryItems]; -} - -@end diff --git a/widget/cocoa/nsPrintOptionsX.h b/widget/cocoa/nsPrintOptionsX.h deleted file mode 100644 index e34e75059e..0000000000 --- a/widget/cocoa/nsPrintOptionsX.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- 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 nsPrintOptionsX_h_ -#define nsPrintOptionsX_h_ - -#include "nsPrintOptionsImpl.h" - -namespace mozilla -{ -namespace embedding -{ - class PrintData; -} // namespace embedding -} // namespace mozilla - -class nsPrintOptionsX : public nsPrintOptions -{ -public: - nsPrintOptionsX(); - virtual ~nsPrintOptionsX(); - - /* - * These serialize and deserialize methods are not symmetrical in that - * printSettingsX != deserialize(serialize(printSettingsX)). This is because - * the native print settings stored in the nsPrintSettingsX's NSPrintInfo - * object are not fully serialized. Only the values needed for successful - * printing are. - */ - NS_IMETHODIMP SerializeToPrintData(nsIPrintSettings* aSettings, - nsIWebBrowserPrint* aWBP, - mozilla::embedding::PrintData* data); - NS_IMETHODIMP DeserializeToPrintSettings(const mozilla::embedding::PrintData& data, - nsIPrintSettings* settings); - -protected: - nsresult _CreatePrintSettings(nsIPrintSettings **_retval); - nsresult ReadPrefs(nsIPrintSettings* aPS, const nsAString& aPrinterName, uint32_t aFlags); - nsresult WritePrefs(nsIPrintSettings* aPS, const nsAString& aPrinterName, uint32_t aFlags); -}; - -#endif // nsPrintOptionsX_h_ diff --git a/widget/cocoa/nsPrintOptionsX.mm b/widget/cocoa/nsPrintOptionsX.mm deleted file mode 100644 index d9aa17b42e..0000000000 --- a/widget/cocoa/nsPrintOptionsX.mm +++ /dev/null @@ -1,349 +0,0 @@ -/* -*- 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/. */ - -#include "nsCOMPtr.h" -#include "nsQueryObject.h" -#include "nsIServiceManager.h" -#include "nsPrintOptionsX.h" -#include "nsPrintSettingsX.h" - -// The constants for paper orientation were renamed in 10.9. __MAC_10_9 is -// defined on OS X 10.9 and later. Although 10.8 and earlier are not supported -// at this time, this allows for building on those older OS versions. The -// values are consistent across OS versions so the rename does not affect -// runtime, just compilation. -#ifdef __MAC_10_9 -#define NS_PAPER_ORIENTATION_PORTRAIT (NSPaperOrientationPortrait) -#define NS_PAPER_ORIENTATION_LANDSCAPE (NSPaperOrientationLandscape) -#else -#define NS_PAPER_ORIENTATION_PORTRAIT (NSPortraitOrientation) -#define NS_PAPER_ORIENTATION_LANDSCAPE (NSLandscapeOrientation) -#endif - -using namespace mozilla::embedding; - -nsPrintOptionsX::nsPrintOptionsX() -{ -} - -nsPrintOptionsX::~nsPrintOptionsX() -{ -} - -NS_IMETHODIMP -nsPrintOptionsX::SerializeToPrintData(nsIPrintSettings* aSettings, - nsIWebBrowserPrint* aWBP, - PrintData* data) -{ - nsresult rv = nsPrintOptions::SerializeToPrintData(aSettings, aWBP, data); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (aWBP) { - // When serializing an nsIWebBrowserPrint, we need to pass up the first - // document name. We could pass up the entire collection of document - // names, but the OS X printing prompt code only really cares about - // the first one, so we just send the first to save IPC traffic. - char16_t** docTitles; - uint32_t titleCount; - rv = aWBP->EnumerateDocumentNames(&titleCount, &docTitles); - if (NS_SUCCEEDED(rv) && titleCount > 0) { - data->printJobName().Assign(docTitles[0]); - } - - for (int32_t i = titleCount - 1; i >= 0; i--) { - free(docTitles[i]); - } - free(docTitles); - docTitles = nullptr; - } - - RefPtr<nsPrintSettingsX> settingsX(do_QueryObject(aSettings)); - if (NS_WARN_IF(!settingsX)) { - return NS_ERROR_FAILURE; - } - - NSPrintInfo* printInfo = settingsX->GetCocoaPrintInfo(); - if (NS_WARN_IF(!printInfo)) { - return NS_ERROR_FAILURE; - } - - double adjustedWidth, adjustedHeight; - settingsX->GetAdjustedPaperSize(&adjustedWidth, &adjustedHeight); - data->adjustedPaperWidth() = adjustedWidth; - data->adjustedPaperHeight() = adjustedHeight; - - NSDictionary* dict = [printInfo dictionary]; - if (NS_WARN_IF(!dict)) { - return NS_ERROR_FAILURE; - } - - NSString* printerName = [dict objectForKey: NSPrintPrinterName]; - if (printerName) { - nsCocoaUtils::GetStringForNSString(printerName, data->printerName()); - } - - NSString* faxNumber = [dict objectForKey: NSPrintFaxNumber]; - if (faxNumber) { - nsCocoaUtils::GetStringForNSString(faxNumber, data->faxNumber()); - } - - NSURL* printToFileURL = [dict objectForKey: NSPrintJobSavingURL]; - if (printToFileURL) { - nsCocoaUtils::GetStringForNSString([printToFileURL absoluteString], - data->toFileName()); - } - - NSDate* printTime = [dict objectForKey: NSPrintTime]; - if (printTime) { - NSTimeInterval timestamp = [printTime timeIntervalSinceReferenceDate]; - data->printTime() = timestamp; - } - - NSString* disposition = [dict objectForKey: NSPrintJobDisposition]; - if (disposition) { - nsCocoaUtils::GetStringForNSString(disposition, data->disposition()); - } - - NSString* paperName = [dict objectForKey: NSPrintPaperName]; - if (paperName) { - nsCocoaUtils::GetStringForNSString(paperName, data->paperName()); - } - - float scalingFactor = [[dict objectForKey: NSPrintScalingFactor] floatValue]; - data->scalingFactor() = scalingFactor; - - int32_t orientation; - if ([printInfo orientation] == NS_PAPER_ORIENTATION_PORTRAIT) { - orientation = nsIPrintSettings::kPortraitOrientation; - } else { - orientation = nsIPrintSettings::kLandscapeOrientation; - } - data->orientation() = orientation; - - NSSize paperSize = [printInfo paperSize]; - float widthScale, heightScale; - settingsX->GetInchesScale(&widthScale, &heightScale); - if (orientation == nsIPrintSettings::kLandscapeOrientation) { - // switch widths and heights - data->widthScale() = heightScale; - data->heightScale() = widthScale; - data->paperWidth() = paperSize.height / heightScale; - data->paperHeight() = paperSize.width / widthScale; - } else { - data->widthScale() = widthScale; - data->heightScale() = heightScale; - data->paperWidth() = paperSize.width / widthScale; - data->paperHeight() = paperSize.height / heightScale; - } - - data->numCopies() = [[dict objectForKey: NSPrintCopies] intValue]; - data->printAllPages() = [[dict objectForKey: NSPrintAllPages] boolValue]; - data->startPageRange() = [[dict objectForKey: NSPrintFirstPage] intValue]; - data->endPageRange() = [[dict objectForKey: NSPrintLastPage] intValue]; - data->mustCollate() = [[dict objectForKey: NSPrintMustCollate] boolValue]; - data->printReversed() = [[dict objectForKey: NSPrintReversePageOrder] boolValue]; - data->pagesAcross() = [[dict objectForKey: NSPrintPagesAcross] unsignedShortValue]; - data->pagesDown() = [[dict objectForKey: NSPrintPagesDown] unsignedShortValue]; - data->detailedErrorReporting() = [[dict objectForKey: NSPrintDetailedErrorReporting] boolValue]; - data->addHeaderAndFooter() = [[dict objectForKey: NSPrintHeaderAndFooter] boolValue]; - data->fileNameExtensionHidden() = - [[dict objectForKey: NSPrintJobSavingFileNameExtensionHidden] boolValue]; - - bool printSelectionOnly = [[dict objectForKey: NSPrintSelectionOnly] boolValue]; - aSettings->SetPrintOptions(nsIPrintSettings::kEnableSelectionRB, - printSelectionOnly); - aSettings->GetPrintOptionsBits(&data->optionFlags()); - - return NS_OK; -} - -NS_IMETHODIMP -nsPrintOptionsX::DeserializeToPrintSettings(const PrintData& data, - nsIPrintSettings* settings) -{ - nsresult rv = nsPrintOptions::DeserializeToPrintSettings(data, settings); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - RefPtr<nsPrintSettingsX> settingsX(do_QueryObject(settings)); - if (NS_WARN_IF(!settingsX)) { - return NS_ERROR_FAILURE; - } - - NSPrintInfo* sharedPrintInfo = [NSPrintInfo sharedPrintInfo]; - if (NS_WARN_IF(!sharedPrintInfo)) { - return NS_ERROR_FAILURE; - } - - NSDictionary* sharedDict = [sharedPrintInfo dictionary]; - if (NS_WARN_IF(!sharedDict)) { - return NS_ERROR_FAILURE; - } - - // We need to create a new NSMutableDictionary to pass to NSPrintInfo with - // the values that we got from the other process. - NSMutableDictionary* newPrintInfoDict = - [NSMutableDictionary dictionaryWithDictionary:sharedDict]; - if (NS_WARN_IF(!newPrintInfoDict)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - NSString* printerName = nsCocoaUtils::ToNSString(data.printerName()); - if (printerName) { - NSPrinter* printer = [NSPrinter printerWithName: printerName]; - if (printer) { - [newPrintInfoDict setObject: printer forKey: NSPrintPrinter]; - [newPrintInfoDict setObject: printerName forKey: NSPrintPrinterName]; - } - } - - [newPrintInfoDict setObject: [NSNumber numberWithInt: data.numCopies()] - forKey: NSPrintCopies]; - [newPrintInfoDict setObject: [NSNumber numberWithBool: data.printAllPages()] - forKey: NSPrintAllPages]; - [newPrintInfoDict setObject: [NSNumber numberWithInt: data.startPageRange()] - forKey: NSPrintFirstPage]; - [newPrintInfoDict setObject: [NSNumber numberWithInt: data.endPageRange()] - forKey: NSPrintLastPage]; - [newPrintInfoDict setObject: [NSNumber numberWithBool: data.mustCollate()] - forKey: NSPrintMustCollate]; - [newPrintInfoDict setObject: [NSNumber numberWithBool: data.printReversed()] - forKey: NSPrintReversePageOrder]; - - [newPrintInfoDict setObject: nsCocoaUtils::ToNSString(data.disposition()) - forKey: NSPrintJobDisposition]; - - [newPrintInfoDict setObject: nsCocoaUtils::ToNSString(data.paperName()) - forKey: NSPrintPaperName]; - - [newPrintInfoDict setObject: [NSNumber numberWithFloat: data.scalingFactor()] - forKey: NSPrintScalingFactor]; - - CGFloat width = data.paperWidth() * data.widthScale(); - CGFloat height = data.paperHeight() * data.heightScale(); - [newPrintInfoDict setObject: [NSValue valueWithSize:NSMakeSize(width,height)] - forKey: NSPrintPaperSize]; - - int paperOrientation; - if (data.orientation() == nsIPrintSettings::kPortraitOrientation) { - paperOrientation = NS_PAPER_ORIENTATION_PORTRAIT; - settings->SetOrientation(nsIPrintSettings::kPortraitOrientation); - } else { - paperOrientation = NS_PAPER_ORIENTATION_LANDSCAPE; - settings->SetOrientation(nsIPrintSettings::kLandscapeOrientation); - } - [newPrintInfoDict setObject: [NSNumber numberWithInt:paperOrientation] - forKey: NSPrintOrientation]; - - [newPrintInfoDict setObject: [NSNumber numberWithShort: data.pagesAcross()] - forKey: NSPrintPagesAcross]; - [newPrintInfoDict setObject: [NSNumber numberWithShort: data.pagesDown()] - forKey: NSPrintPagesDown]; - [newPrintInfoDict setObject: [NSNumber numberWithBool: data.detailedErrorReporting()] - forKey: NSPrintDetailedErrorReporting]; - [newPrintInfoDict setObject: nsCocoaUtils::ToNSString(data.faxNumber()) - forKey: NSPrintFaxNumber]; - [newPrintInfoDict setObject: [NSNumber numberWithBool: data.addHeaderAndFooter()] - forKey: NSPrintHeaderAndFooter]; - [newPrintInfoDict setObject: [NSNumber numberWithBool: data.fileNameExtensionHidden()] - forKey: NSPrintJobSavingFileNameExtensionHidden]; - - // At this point, the base class should have properly deserialized the print - // options bitfield for nsIPrintSettings, so that it holds the correct value - // for kEnableSelectionRB, which we use to set NSPrintSelectionOnly. - - bool printSelectionOnly = false; - rv = settings->GetPrintOptions(nsIPrintSettings::kEnableSelectionRB, &printSelectionOnly); - if (NS_SUCCEEDED(rv)) { - [newPrintInfoDict setObject: [NSNumber numberWithBool: printSelectionOnly] - forKey: NSPrintSelectionOnly]; - } else { - [newPrintInfoDict setObject: [NSNumber numberWithBool: NO] - forKey: NSPrintSelectionOnly]; - } - - NSURL* jobSavingURL = - [NSURL URLWithString: nsCocoaUtils::ToNSString(data.toFileName())]; - if (jobSavingURL) { - [newPrintInfoDict setObject: jobSavingURL forKey: NSPrintJobSavingURL]; - } - - NSTimeInterval timestamp = data.printTime(); - NSDate* printTime = [NSDate dateWithTimeIntervalSinceReferenceDate: timestamp]; - if (printTime) { - [newPrintInfoDict setObject: printTime forKey: NSPrintTime]; - } - - // Next, we create a new NSPrintInfo with the values in our dictionary. - NSPrintInfo* newPrintInfo = - [[NSPrintInfo alloc] initWithDictionary: newPrintInfoDict]; - if (NS_WARN_IF(!newPrintInfo)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - // And now swap in the new NSPrintInfo we've just populated. - settingsX->SetCocoaPrintInfo(newPrintInfo); - [newPrintInfo release]; - - settingsX->SetAdjustedPaperSize(data.adjustedPaperWidth(), - data.adjustedPaperHeight()); - - return NS_OK; -} - -nsresult -nsPrintOptionsX::ReadPrefs(nsIPrintSettings* aPS, const nsAString& aPrinterName, uint32_t aFlags) -{ - nsresult rv; - - rv = nsPrintOptions::ReadPrefs(aPS, aPrinterName, aFlags); - NS_ASSERTION(NS_SUCCEEDED(rv), "nsPrintOptions::ReadPrefs() failed"); - - RefPtr<nsPrintSettingsX> printSettingsX(do_QueryObject(aPS)); - if (!printSettingsX) - return NS_ERROR_NO_INTERFACE; - rv = printSettingsX->ReadPageFormatFromPrefs(); - - return NS_OK; -} - -nsresult nsPrintOptionsX::_CreatePrintSettings(nsIPrintSettings **_retval) -{ - nsresult rv; - *_retval = nullptr; - - nsPrintSettingsX* printSettings = new nsPrintSettingsX; // does not initially ref count - NS_ENSURE_TRUE(printSettings, NS_ERROR_OUT_OF_MEMORY); - NS_ADDREF(*_retval = printSettings); - - rv = printSettings->Init(); - if (NS_FAILED(rv)) { - NS_RELEASE(*_retval); - return rv; - } - - InitPrintSettingsFromPrefs(*_retval, false, nsIPrintSettings::kInitSaveAll); - return rv; -} - -nsresult -nsPrintOptionsX::WritePrefs(nsIPrintSettings* aPS, const nsAString& aPrinterName, uint32_t aFlags) -{ - nsresult rv; - - rv = nsPrintOptions::WritePrefs(aPS, aPrinterName, aFlags); - NS_ASSERTION(NS_SUCCEEDED(rv), "nsPrintOptions::WritePrefs() failed"); - - RefPtr<nsPrintSettingsX> printSettingsX(do_QueryObject(aPS)); - if (!printSettingsX) - return NS_ERROR_NO_INTERFACE; - rv = printSettingsX->WritePageFormatToPrefs(); - NS_ASSERTION(NS_SUCCEEDED(rv), "nsPrintSettingsX::WritePageFormatToPrefs() failed"); - - return NS_OK; -} diff --git a/widget/cocoa/nsPrintSettingsX.h b/widget/cocoa/nsPrintSettingsX.h deleted file mode 100644 index 1d755b2505..0000000000 --- a/widget/cocoa/nsPrintSettingsX.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -*- 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 nsPrintSettingsX_h_ -#define nsPrintSettingsX_h_ - -#include "nsPrintSettingsImpl.h" -#import <Cocoa/Cocoa.h> - -#define NS_PRINTSETTINGSX_IID \ -{ 0x0DF2FDBD, 0x906D, 0x4726, \ - { 0x9E, 0x4D, 0xCF, 0xE0, 0x87, 0x8D, 0x70, 0x7C } } - -class nsPrintSettingsX : public nsPrintSettings -{ -public: - NS_DECLARE_STATIC_IID_ACCESSOR(NS_PRINTSETTINGSX_IID) - NS_DECL_ISUPPORTS_INHERITED - - nsPrintSettingsX(); - nsresult Init(); - NSPrintInfo* GetCocoaPrintInfo() { return mPrintInfo; } - void SetCocoaPrintInfo(NSPrintInfo* aPrintInfo); - virtual nsresult ReadPageFormatFromPrefs(); - virtual nsresult WritePageFormatToPrefs(); - virtual nsresult GetEffectivePageSize(double *aWidth, - double *aHeight) override; - - // In addition to setting the paper width and height, these - // overrides set the adjusted width and height returned from - // GetEffectivePageSize. This is needed when a paper size is - // set manually without using a print dialog a la reftest-print. - virtual nsresult SetPaperWidth(double aPaperWidth) override; - virtual nsresult SetPaperHeight(double aPaperWidth) override; - - PMPrintSettings GetPMPrintSettings(); - PMPrintSession GetPMPrintSession(); - PMPageFormat GetPMPageFormat(); - void SetPMPageFormat(PMPageFormat aPageFormat); - - // Re-initialize mUnwriteableMargin with values from mPageFormat. - // Should be called whenever mPageFormat is initialized or overwritten. - nsresult InitUnwriteableMargin(); - - // Re-initialize mAdjustedPaper{Width,Height} with values from mPageFormat. - // Should be called whenever mPageFormat is initialized or overwritten. - nsresult InitAdjustedPaperSize(); - - void SetInchesScale(float aWidthScale, float aHeightScale); - void GetInchesScale(float *aWidthScale, float *aHeightScale); - - void SetAdjustedPaperSize(double aWidth, double aHeight); - void GetAdjustedPaperSize(double *aWidth, double *aHeight); - -protected: - virtual ~nsPrintSettingsX(); - - nsPrintSettingsX(const nsPrintSettingsX& src); - nsPrintSettingsX& operator=(const nsPrintSettingsX& rhs); - - nsresult _Clone(nsIPrintSettings **_retval) override; - nsresult _Assign(nsIPrintSettings *aPS) override; - - // The out param has a ref count of 1 on return so caller needs to PMRelase() when done. - OSStatus CreateDefaultPageFormat(PMPrintSession aSession, PMPageFormat& outFormat); - OSStatus CreateDefaultPrintSettings(PMPrintSession aSession, PMPrintSettings& outSettings); - - NSPrintInfo* mPrintInfo; - - // Scaling factors used to convert the NSPrintInfo - // paper size units to inches - float mWidthScale; - float mHeightScale; - double mAdjustedPaperWidth; - double mAdjustedPaperHeight; -}; - -NS_DEFINE_STATIC_IID_ACCESSOR(nsPrintSettingsX, NS_PRINTSETTINGSX_IID) - -#endif // nsPrintSettingsX_h_ diff --git a/widget/cocoa/nsPrintSettingsX.mm b/widget/cocoa/nsPrintSettingsX.mm deleted file mode 100644 index 73a8e78d2b..0000000000 --- a/widget/cocoa/nsPrintSettingsX.mm +++ /dev/null @@ -1,272 +0,0 @@ -/* -*- 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/. */ - -#include "nsPrintSettingsX.h" -#include "nsObjCExceptions.h" - -#include "plbase64.h" -#include "plstr.h" - -#include "nsCocoaUtils.h" - -#include "mozilla/Preferences.h" - -using namespace mozilla; - -#define MAC_OS_X_PAGE_SETUP_PREFNAME "print.macosx.pagesetup-2" -#define COCOA_PAPER_UNITS_PER_INCH 72.0 - -NS_IMPL_ISUPPORTS_INHERITED(nsPrintSettingsX, nsPrintSettings, nsPrintSettingsX) - -nsPrintSettingsX::nsPrintSettingsX() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - mPrintInfo = [[NSPrintInfo sharedPrintInfo] copy]; - mWidthScale = COCOA_PAPER_UNITS_PER_INCH; - mHeightScale = COCOA_PAPER_UNITS_PER_INCH; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -nsPrintSettingsX::nsPrintSettingsX(const nsPrintSettingsX& src) -{ - *this = src; -} - -nsPrintSettingsX::~nsPrintSettingsX() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [mPrintInfo release]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -nsPrintSettingsX& nsPrintSettingsX::operator=(const nsPrintSettingsX& rhs) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - if (this == &rhs) { - return *this; - } - - nsPrintSettings::operator=(rhs); - - [mPrintInfo release]; - mPrintInfo = [rhs.mPrintInfo copy]; - - return *this; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(*this); -} - -nsresult nsPrintSettingsX::Init() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - InitUnwriteableMargin(); - InitAdjustedPaperSize(); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -// Should be called whenever the page format changes. -NS_IMETHODIMP nsPrintSettingsX::InitUnwriteableMargin() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - PMPaper paper; - PMPaperMargins paperMargin; - PMPageFormat pageFormat = GetPMPageFormat(); - ::PMGetPageFormatPaper(pageFormat, &paper); - ::PMPaperGetMargins(paper, &paperMargin); - mUnwriteableMargin.top = NS_POINTS_TO_INT_TWIPS(paperMargin.top); - mUnwriteableMargin.left = NS_POINTS_TO_INT_TWIPS(paperMargin.left); - mUnwriteableMargin.bottom = NS_POINTS_TO_INT_TWIPS(paperMargin.bottom); - mUnwriteableMargin.right = NS_POINTS_TO_INT_TWIPS(paperMargin.right); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP nsPrintSettingsX::InitAdjustedPaperSize() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - PMPageFormat pageFormat = GetPMPageFormat(); - - PMRect paperRect; - ::PMGetAdjustedPaperRect(pageFormat, &paperRect); - - mAdjustedPaperWidth = paperRect.right - paperRect.left; - mAdjustedPaperHeight = paperRect.bottom - paperRect.top; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -void -nsPrintSettingsX::SetCocoaPrintInfo(NSPrintInfo* aPrintInfo) -{ - if (mPrintInfo != aPrintInfo) { - [mPrintInfo release]; - mPrintInfo = [aPrintInfo retain]; - } -} - -NS_IMETHODIMP nsPrintSettingsX::ReadPageFormatFromPrefs() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - nsAutoCString encodedData; - nsresult rv = - Preferences::GetCString(MAC_OS_X_PAGE_SETUP_PREFNAME, &encodedData); - if (NS_FAILED(rv)) { - return rv; - } - - // decode the base64 - char* decodedData = PL_Base64Decode(encodedData.get(), encodedData.Length(), nullptr); - NSData* data = [NSData dataWithBytes:decodedData length:strlen(decodedData)]; - if (!data) - return NS_ERROR_FAILURE; - - PMPageFormat newPageFormat; - OSStatus status = ::PMPageFormatCreateWithDataRepresentation((CFDataRef)data, &newPageFormat); - if (status == noErr) { - SetPMPageFormat(newPageFormat); - } - InitUnwriteableMargin(); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP nsPrintSettingsX::WritePageFormatToPrefs() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - PMPageFormat pageFormat = GetPMPageFormat(); - if (pageFormat == kPMNoPageFormat) - return NS_ERROR_NOT_INITIALIZED; - - NSData* data = nil; - OSStatus err = ::PMPageFormatCreateDataRepresentation(pageFormat, (CFDataRef*)&data, kPMDataFormatXMLDefault); - if (err != noErr) - return NS_ERROR_FAILURE; - - nsAutoCString encodedData; - encodedData.Adopt(PL_Base64Encode((char*)[data bytes], [data length], nullptr)); - if (!encodedData.get()) - return NS_ERROR_OUT_OF_MEMORY; - - return Preferences::SetCString(MAC_OS_X_PAGE_SETUP_PREFNAME, encodedData); - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -nsresult nsPrintSettingsX::_Clone(nsIPrintSettings **_retval) -{ - NS_ENSURE_ARG_POINTER(_retval); - *_retval = nullptr; - - nsPrintSettingsX *newSettings = new nsPrintSettingsX(*this); - if (!newSettings) - return NS_ERROR_FAILURE; - *_retval = newSettings; - NS_ADDREF(*_retval); - return NS_OK; -} - -NS_IMETHODIMP nsPrintSettingsX::_Assign(nsIPrintSettings *aPS) -{ - nsPrintSettingsX *printSettingsX = static_cast<nsPrintSettingsX*>(aPS); - if (!printSettingsX) - return NS_ERROR_UNEXPECTED; - *this = *printSettingsX; - return NS_OK; -} - -PMPrintSettings -nsPrintSettingsX::GetPMPrintSettings() -{ - return static_cast<PMPrintSettings>([mPrintInfo PMPrintSettings]); -} - -PMPrintSession -nsPrintSettingsX::GetPMPrintSession() -{ - return static_cast<PMPrintSession>([mPrintInfo PMPrintSession]); -} - -PMPageFormat -nsPrintSettingsX::GetPMPageFormat() -{ - return static_cast<PMPageFormat>([mPrintInfo PMPageFormat]); -} - -void -nsPrintSettingsX::SetPMPageFormat(PMPageFormat aPageFormat) -{ - PMPageFormat oldPageFormat = GetPMPageFormat(); - ::PMCopyPageFormat(aPageFormat, oldPageFormat); - [mPrintInfo updateFromPMPageFormat]; -} - -void -nsPrintSettingsX::SetInchesScale(float aWidthScale, float aHeightScale) -{ - if (aWidthScale > 0 && aHeightScale > 0) { - mWidthScale = aWidthScale; - mHeightScale = aHeightScale; - } -} - -void -nsPrintSettingsX::GetInchesScale(float *aWidthScale, float *aHeightScale) -{ - *aWidthScale = mWidthScale; - *aHeightScale = mHeightScale; -} - -NS_IMETHODIMP nsPrintSettingsX::SetPaperWidth(double aPaperWidth) -{ - mPaperWidth = aPaperWidth; - mAdjustedPaperWidth = aPaperWidth * mWidthScale; - return NS_OK; -} - -NS_IMETHODIMP nsPrintSettingsX::SetPaperHeight(double aPaperHeight) -{ - mPaperHeight = aPaperHeight; - mAdjustedPaperHeight = aPaperHeight * mHeightScale; - return NS_OK; -} - -NS_IMETHODIMP -nsPrintSettingsX::GetEffectivePageSize(double *aWidth, double *aHeight) -{ - *aWidth = NS_INCHES_TO_TWIPS(mAdjustedPaperWidth / mWidthScale); - *aHeight = NS_INCHES_TO_TWIPS(mAdjustedPaperHeight / mHeightScale); - return NS_OK; -} - -void nsPrintSettingsX::SetAdjustedPaperSize(double aWidth, double aHeight) -{ - mAdjustedPaperWidth = aWidth; - mAdjustedPaperHeight = aHeight; -} - -void nsPrintSettingsX::GetAdjustedPaperSize(double *aWidth, double *aHeight) -{ - *aWidth = mAdjustedPaperWidth; - *aHeight = mAdjustedPaperHeight; -} diff --git a/widget/cocoa/nsSandboxViolationSink.h b/widget/cocoa/nsSandboxViolationSink.h deleted file mode 100644 index 35b5d89af5..0000000000 --- a/widget/cocoa/nsSandboxViolationSink.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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 nsSandboxViolationSink_h_ -#define nsSandboxViolationSink_h_ - -#include <stdint.h> - -// Class for tracking sandbox violations. Currently it just logs them to -// stdout and the system console. In the future it may do more. - -// What makes this possible is the fact that Apple' sandboxd calls -// notify_post("com.apple.sandbox.violation.*") whenever it's notified by the -// Sandbox kernel extension of a sandbox violation. We register to receive -// these notifications. But the notifications are empty, and are sent for -// every violation in every process. So we need to do more to get only "our" -// violations, and to find out what kind of violation they were. See the -// implementation of nsSandboxViolationSink::ViolationHandler(). - -#define SANDBOX_VIOLATION_QUEUE_NAME "org.mozilla.sandbox.violation.queue" -#define SANDBOX_VIOLATION_NOTIFICATION_NAME "com.apple.sandbox.violation.*" - -class nsSandboxViolationSink -{ -public: - static void Start(); - static void Stop(); -private: - static void ViolationHandler(); - static int mNotifyToken; - static uint64_t mLastMsgReceived; -}; - -#endif // nsSandboxViolationSink_h_ diff --git a/widget/cocoa/nsSandboxViolationSink.mm b/widget/cocoa/nsSandboxViolationSink.mm deleted file mode 100644 index 0572173344..0000000000 --- a/widget/cocoa/nsSandboxViolationSink.mm +++ /dev/null @@ -1,115 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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/. */ - -#include "nsSandboxViolationSink.h" - -#include <unistd.h> -#include <time.h> -#include <asl.h> -#include <dispatch/dispatch.h> -#include <notify.h> -#include "nsCocoaDebugUtils.h" -#include "mozilla/Preferences.h" -#include "mozilla/Sprintf.h" - -int nsSandboxViolationSink::mNotifyToken = 0; -uint64_t nsSandboxViolationSink::mLastMsgReceived = 0; - -void -nsSandboxViolationSink::Start() -{ - if (mNotifyToken) { - return; - } - notify_register_dispatch(SANDBOX_VIOLATION_NOTIFICATION_NAME, - &mNotifyToken, - dispatch_queue_create(SANDBOX_VIOLATION_QUEUE_NAME, - DISPATCH_QUEUE_SERIAL), - ^(int token) { ViolationHandler(); }); -} - -void -nsSandboxViolationSink::Stop() -{ - if (!mNotifyToken) { - return; - } - notify_cancel(mNotifyToken); - mNotifyToken = 0; -} - -// We need to query syslogd to find out what violations occurred, and whether -// they were "ours". We can use the Apple System Log facility to do this. -// Besides calling notify_post("com.apple.sandbox.violation.*"), Apple's -// sandboxd also reports all sandbox violations (sent to it by the Sandbox -// kernel extension) to syslogd, which stores them and makes them viewable -// in the system console. This is the database we query. - -// ViolationHandler() is always called on its own secondary thread. This -// makes it unlikely it will interfere with other browser activity. - -void -nsSandboxViolationSink::ViolationHandler() -{ - aslmsg query = asl_new(ASL_TYPE_QUERY); - - asl_set_query(query, ASL_KEY_FACILITY, "com.apple.sandbox", - ASL_QUERY_OP_EQUAL); - - // Only get reports that were generated very recently. - char query_time[30] = {0}; - SprintfLiteral(query_time, "%li", time(NULL) - 2); - asl_set_query(query, ASL_KEY_TIME, query_time, - ASL_QUERY_OP_NUMERIC | ASL_QUERY_OP_GREATER_EQUAL); - - // This code is easier to test if we don't just track "our" violations, - // which are (normally) few and far between. For example (for the time - // being at least) four appleeventsd sandbox violations happen every time - // we start the browser in e10s mode. But it makes sense to default to - // only tracking "our" violations. - if (mozilla::Preferences::GetBool( - "security.sandbox.mac.track.violations.oursonly", true)) { - // This makes each of our processes log its own violations. It might - // be better to make the chrome process log all the other processes' - // violations. - char query_pid[20] = {0}; - SprintfLiteral(query_pid, "%u", getpid()); - asl_set_query(query, ASL_KEY_REF_PID, query_pid, ASL_QUERY_OP_EQUAL); - } - - aslresponse response = asl_search(nullptr, query); - - // Each time ViolationHandler() is called we grab as many messages as are - // available. Otherwise we might not get them all. - if (response) { - while (true) { - aslmsg hit = nullptr; - aslmsg found = nullptr; - const char* id_str; - - while ((hit = aslresponse_next(response))) { - // Record the message id to avoid logging the same violation more - // than once. - id_str = asl_get(hit, ASL_KEY_MSG_ID); - uint64_t id_val = atoll(id_str); - if (id_val <= mLastMsgReceived) { - continue; - } - mLastMsgReceived = id_val; - found = hit; - break; - } - if (!found) { - break; - } - - const char* pid_str = asl_get(found, ASL_KEY_REF_PID); - const char* message_str = asl_get(found, ASL_KEY_MSG); - nsCocoaDebugUtils::DebugLog("nsSandboxViolationSink::ViolationHandler(): id %s, pid %s, message %s", - id_str, pid_str, message_str); - } - aslresponse_free(response); - } -} diff --git a/widget/cocoa/nsScreenCocoa.h b/widget/cocoa/nsScreenCocoa.h deleted file mode 100644 index 268d5beb0a..0000000000 --- a/widget/cocoa/nsScreenCocoa.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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 nsScreenCocoa_h_ -#define nsScreenCocoa_h_ - -#import <Cocoa/Cocoa.h> - -#include "nsBaseScreen.h" - -class nsScreenCocoa : public nsBaseScreen -{ -public: - explicit nsScreenCocoa (NSScreen *screen); - ~nsScreenCocoa (); - - NS_IMETHOD GetId(uint32_t* outId); - NS_IMETHOD GetRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); - NS_IMETHOD GetAvailRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); - NS_IMETHOD GetRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); - NS_IMETHOD GetAvailRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); - NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth); - NS_IMETHOD GetColorDepth(int32_t* aColorDepth); - NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor); - NS_IMETHOD GetDefaultCSSScaleFactor(double* aScaleFactor) - { - return GetContentsScaleFactor(aScaleFactor); - } - - NSScreen *CocoaScreen() { return mScreen; } - -private: - CGFloat BackingScaleFactor(); - - NSScreen *mScreen; - uint32_t mId; -}; - -#endif // nsScreenCocoa_h_ diff --git a/widget/cocoa/nsScreenCocoa.mm b/widget/cocoa/nsScreenCocoa.mm deleted file mode 100644 index 08905bf0ad..0000000000 --- a/widget/cocoa/nsScreenCocoa.mm +++ /dev/null @@ -1,147 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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/. */ - -#include "nsScreenCocoa.h" -#include "nsObjCExceptions.h" -#include "nsCocoaUtils.h" - -static uint32_t sScreenId = 0; - -nsScreenCocoa::nsScreenCocoa (NSScreen *screen) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - mScreen = [screen retain]; - mId = ++sScreenId; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -nsScreenCocoa::~nsScreenCocoa () -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [mScreen release]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -NS_IMETHODIMP -nsScreenCocoa::GetId(uint32_t *outId) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - *outId = mId; - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -NS_IMETHODIMP -nsScreenCocoa::GetRect(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight) -{ - NSRect frame = [mScreen frame]; - - mozilla::LayoutDeviceIntRect r = - nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, BackingScaleFactor()); - - *outX = r.x; - *outY = r.y; - *outWidth = r.width; - *outHeight = r.height; - - return NS_OK; -} - -NS_IMETHODIMP -nsScreenCocoa::GetAvailRect(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight) -{ - NSRect frame = [mScreen visibleFrame]; - - mozilla::LayoutDeviceIntRect r = - nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, BackingScaleFactor()); - - *outX = r.x; - *outY = r.y; - *outWidth = r.width; - *outHeight = r.height; - - return NS_OK; -} - -NS_IMETHODIMP -nsScreenCocoa::GetRectDisplayPix(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight) -{ - NSRect frame = [mScreen frame]; - - mozilla::DesktopIntRect r = nsCocoaUtils::CocoaRectToGeckoRect(frame); - - *outX = r.x; - *outY = r.y; - *outWidth = r.width; - *outHeight = r.height; - - return NS_OK; -} - -NS_IMETHODIMP -nsScreenCocoa::GetAvailRectDisplayPix(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight) -{ - NSRect frame = [mScreen visibleFrame]; - - mozilla::DesktopIntRect r = nsCocoaUtils::CocoaRectToGeckoRect(frame); - - *outX = r.x; - *outY = r.y; - *outWidth = r.width; - *outHeight = r.height; - - return NS_OK; -} - -NS_IMETHODIMP -nsScreenCocoa::GetPixelDepth(int32_t *aPixelDepth) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NSWindowDepth depth = [mScreen depth]; - int bpp = NSBitsPerPixelFromDepth(depth); - - *aPixelDepth = bpp; - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsScreenCocoa::GetColorDepth(int32_t *aColorDepth) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NSWindowDepth depth = [mScreen depth]; - int bpp = NSBitsPerPixelFromDepth (depth); - - *aColorDepth = bpp; - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsScreenCocoa::GetContentsScaleFactor(double *aContentsScaleFactor) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - *aContentsScaleFactor = (double) BackingScaleFactor(); - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -CGFloat -nsScreenCocoa::BackingScaleFactor() -{ - return nsCocoaUtils::GetBackingScaleFactor(mScreen); -} diff --git a/widget/cocoa/nsScreenManagerCocoa.h b/widget/cocoa/nsScreenManagerCocoa.h deleted file mode 100644 index 61a059d977..0000000000 --- a/widget/cocoa/nsScreenManagerCocoa.h +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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 nsScreenManagerCocoa_h_ -#define nsScreenManagerCocoa_h_ - -#import <Cocoa/Cocoa.h> - -#include "mozilla/RefPtr.h" -#include "nsTArray.h" -#include "nsIScreenManager.h" -#include "nsScreenCocoa.h" - -class nsScreenManagerCocoa : public nsIScreenManager -{ -public: - nsScreenManagerCocoa(); - - NS_DECL_ISUPPORTS - NS_DECL_NSISCREENMANAGER - -protected: - virtual ~nsScreenManagerCocoa(); - -private: - - nsScreenCocoa *ScreenForCocoaScreen(NSScreen *screen); - nsTArray< RefPtr<nsScreenCocoa> > mScreenList; -}; - -#endif // nsScreenManagerCocoa_h_ diff --git a/widget/cocoa/nsScreenManagerCocoa.mm b/widget/cocoa/nsScreenManagerCocoa.mm deleted file mode 100644 index 9a0cbb9cc5..0000000000 --- a/widget/cocoa/nsScreenManagerCocoa.mm +++ /dev/null @@ -1,152 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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/. */ - -#include "nsScreenManagerCocoa.h" -#include "nsObjCExceptions.h" -#include "nsCOMPtr.h" -#include "nsCocoaUtils.h" - -using namespace mozilla; - -NS_IMPL_ISUPPORTS(nsScreenManagerCocoa, nsIScreenManager) - -nsScreenManagerCocoa::nsScreenManagerCocoa() -{ -} - -nsScreenManagerCocoa::~nsScreenManagerCocoa() -{ -} - -nsScreenCocoa* -nsScreenManagerCocoa::ScreenForCocoaScreen(NSScreen *screen) -{ - for (uint32_t i = 0; i < mScreenList.Length(); ++i) { - nsScreenCocoa* sc = mScreenList[i]; - if (sc->CocoaScreen() == screen) { - // doesn't addref - return sc; - } - } - - // didn't find it; create and insert - RefPtr<nsScreenCocoa> sc = new nsScreenCocoa(screen); - mScreenList.AppendElement(sc); - return sc.get(); -} - -NS_IMETHODIMP -nsScreenManagerCocoa::ScreenForId (uint32_t aId, nsIScreen **outScreen) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT - - *outScreen = nullptr; - - for (uint32_t i = 0; i < mScreenList.Length(); ++i) { - nsScreenCocoa* sc = mScreenList[i]; - uint32_t id; - nsresult rv = sc->GetId(&id); - - if (NS_SUCCEEDED(rv) && id == aId) { - *outScreen = sc; - NS_ADDREF(*outScreen); - return NS_OK; - } - } - - return NS_ERROR_FAILURE; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsScreenManagerCocoa::ScreenForRect (int32_t aX, int32_t aY, - int32_t aWidth, int32_t aHeight, - nsIScreen **outScreen) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NSEnumerator *screenEnum = [[NSScreen screens] objectEnumerator]; - NSRect inRect = - nsCocoaUtils::GeckoRectToCocoaRect(DesktopIntRect(aX, aY, - aWidth, aHeight)); - NSScreen *screenWindowIsOn = [NSScreen mainScreen]; - float greatestArea = 0; - - while (NSScreen *screen = [screenEnum nextObject]) { - NSDictionary *desc = [screen deviceDescription]; - if ([desc objectForKey:NSDeviceIsScreen] == nil) - continue; - - NSRect r = NSIntersectionRect([screen frame], inRect); - float area = r.size.width * r.size.height; - if (area > greatestArea) { - greatestArea = area; - screenWindowIsOn = screen; - } - } - - *outScreen = ScreenForCocoaScreen(screenWindowIsOn); - NS_ADDREF(*outScreen); - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsScreenManagerCocoa::GetPrimaryScreen (nsIScreen **outScreen) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // the mainScreen is the screen with the "key window" (focus, I assume?) - NSScreen *sc = [[NSScreen screens] objectAtIndex:0]; - - *outScreen = ScreenForCocoaScreen(sc); - NS_ADDREF(*outScreen); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsScreenManagerCocoa::GetNumberOfScreens (uint32_t *aNumberOfScreens) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NSArray *ss = [NSScreen screens]; - - *aNumberOfScreens = [ss count]; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsScreenManagerCocoa::GetSystemDefaultScale(float *aDefaultScale) -{ - *aDefaultScale = 1.0f; - return NS_OK; -} - -NS_IMETHODIMP -nsScreenManagerCocoa::ScreenForNativeWidget (void *nativeWidget, nsIScreen **outScreen) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NSWindow *window = static_cast<NSWindow*>(nativeWidget); - if (window) { - nsIScreen *screen = ScreenForCocoaScreen([window screen]); - *outScreen = screen; - NS_ADDREF(*outScreen); - return NS_OK; - } - - *outScreen = nullptr; - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} diff --git a/widget/cocoa/nsSound.h b/widget/cocoa/nsSound.h deleted file mode 100644 index 0e0293ae28..0000000000 --- a/widget/cocoa/nsSound.h +++ /dev/null @@ -1,27 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * 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 nsSound_h_ -#define nsSound_h_ - -#include "nsISound.h" -#include "nsIStreamLoader.h" - -class nsSound : public nsISound, - public nsIStreamLoaderObserver -{ -public: - nsSound(); - - NS_DECL_ISUPPORTS - NS_DECL_NSISOUND - NS_DECL_NSISTREAMLOADEROBSERVER - -protected: - virtual ~nsSound(); -}; - -#endif // nsSound_h_ diff --git a/widget/cocoa/nsSound.mm b/widget/cocoa/nsSound.mm deleted file mode 100644 index 04c6b4d764..0000000000 --- a/widget/cocoa/nsSound.mm +++ /dev/null @@ -1,108 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * 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/. */ - -#include "nsSound.h" -#include "nsContentUtils.h" -#include "nsObjCExceptions.h" -#include "nsNetUtil.h" -#include "nsCOMPtr.h" -#include "nsIURL.h" -#include "nsString.h" - -#import <Cocoa/Cocoa.h> - -NS_IMPL_ISUPPORTS(nsSound, nsISound, nsIStreamLoaderObserver) - -nsSound::nsSound() -{ -} - -nsSound::~nsSound() -{ -} - -NS_IMETHODIMP -nsSound::Beep() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NSBeep(); - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsSound::OnStreamComplete(nsIStreamLoader *aLoader, - nsISupports *context, - nsresult aStatus, - uint32_t dataLen, - const uint8_t *data) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - NSData *value = [NSData dataWithBytes:data length:dataLen]; - - NSSound *sound = [[NSSound alloc] initWithData:value]; - - [sound play]; - - [sound autorelease]; - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsSound::Play(nsIURL *aURL) -{ - nsCOMPtr<nsIURI> uri(do_QueryInterface(aURL)); - nsCOMPtr<nsIStreamLoader> loader; - return NS_NewStreamLoader(getter_AddRefs(loader), - uri, - this, // aObserver - nsContentUtils::GetSystemPrincipal(), - nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, - nsIContentPolicy::TYPE_OTHER); -} - -NS_IMETHODIMP -nsSound::Init() -{ - return NS_OK; -} - -NS_IMETHODIMP -nsSound::PlaySystemSound(const nsAString &aSoundAlias) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (NS_IsMozAliasSound(aSoundAlias)) { - NS_WARNING("nsISound::playSystemSound is called with \"_moz_\" events, they are obsolete, use nsISound::playEventSound instead"); - // Mac doesn't have system sound settings for each user actions. - return NS_OK; - } - - NSString *name = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(aSoundAlias.BeginReading()) - length:aSoundAlias.Length()]; - NSSound *sound = [NSSound soundNamed:name]; - if (sound) { - [sound stop]; - [sound play]; - } - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsSound::PlayEventSound(uint32_t aEventId) -{ - // Mac doesn't have system sound settings for each user actions. - return NS_OK; -} diff --git a/widget/cocoa/nsStandaloneNativeMenu.h b/widget/cocoa/nsStandaloneNativeMenu.h deleted file mode 100644 index e03742b1e0..0000000000 --- a/widget/cocoa/nsStandaloneNativeMenu.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ -/* 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 nsStandaloneNativeMenu_h_ -#define nsStandaloneNativeMenu_h_ - -#include "nsMenuGroupOwnerX.h" -#include "nsMenuX.h" -#include "nsIStandaloneNativeMenu.h" - -class nsStandaloneNativeMenu : public nsMenuGroupOwnerX, public nsIStandaloneNativeMenu -{ -public: - nsStandaloneNativeMenu(); - - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSISTANDALONENATIVEMENU - - // nsMenuObjectX - nsMenuObjectTypeX MenuObjectType() override { return eStandaloneNativeMenuObjectType; } - void * NativeData() override { return mMenu != nullptr ? mMenu->NativeData() : nullptr; } - virtual void IconUpdated() override; - - nsMenuX * GetMenuXObject() { return mMenu; } - - // If this menu is the menu of a system status bar item (NSStatusItem), - // let the menu know about the status item so that it can propagate - // any icon changes to the status item. - void SetContainerStatusBarItem(NSStatusItem* aItem); - -protected: - virtual ~nsStandaloneNativeMenu(); - - nsMenuX * mMenu; - NSStatusItem* mContainerStatusBarItem; -}; - -#endif diff --git a/widget/cocoa/nsStandaloneNativeMenu.mm b/widget/cocoa/nsStandaloneNativeMenu.mm deleted file mode 100644 index 98a5fd8f6f..0000000000 --- a/widget/cocoa/nsStandaloneNativeMenu.mm +++ /dev/null @@ -1,213 +0,0 @@ -/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ -/* 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/. */ - -#import <Cocoa/Cocoa.h> - -#include "nsStandaloneNativeMenu.h" -#include "nsMenuUtilsX.h" -#include "nsIDOMElement.h" -#include "nsIMutationObserver.h" -#include "nsGkAtoms.h" -#include "nsObjCExceptions.h" - - -NS_IMPL_ISUPPORTS_INHERITED(nsStandaloneNativeMenu, nsMenuGroupOwnerX, - nsIMutationObserver, nsIStandaloneNativeMenu) - -nsStandaloneNativeMenu::nsStandaloneNativeMenu() -: mMenu(nullptr) -, mContainerStatusBarItem(nil) -{ -} - -nsStandaloneNativeMenu::~nsStandaloneNativeMenu() -{ - if (mMenu) delete mMenu; -} - -NS_IMETHODIMP -nsStandaloneNativeMenu::Init(nsIDOMElement * aDOMElement) -{ - NS_ASSERTION(mMenu == nullptr, "nsNativeMenu::Init - mMenu not null!"); - - nsresult rv; - - nsCOMPtr<nsIContent> content = do_QueryInterface(aDOMElement, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - if (!content->IsAnyOfXULElements(nsGkAtoms::menu, nsGkAtoms::menupopup)) - return NS_ERROR_FAILURE; - - rv = nsMenuGroupOwnerX::Create(content); - if (NS_FAILED(rv)) - return rv; - - mMenu = new nsMenuX(); - rv = mMenu->Create(this, this, content); - if (NS_FAILED(rv)) { - delete mMenu; - mMenu = nullptr; - return rv; - } - - mMenu->SetupIcon(); - - return NS_OK; -} - -static void -UpdateMenu(nsMenuX * aMenu) -{ - aMenu->MenuOpened(); - aMenu->MenuClosed(); - - uint32_t itemCount = aMenu->GetItemCount(); - for (uint32_t i = 0; i < itemCount; i++) { - nsMenuObjectX * menuObject = aMenu->GetItemAt(i); - if (menuObject->MenuObjectType() == eSubmenuObjectType) { - UpdateMenu(static_cast<nsMenuX*>(menuObject)); - } - } -} - -NS_IMETHODIMP -nsStandaloneNativeMenu::MenuWillOpen(bool * aResult) -{ - NS_ASSERTION(mMenu != nullptr, "nsStandaloneNativeMenu::OnOpen - mMenu is null!"); - - // Force an update on the mMenu by faking an open/close on all of - // its submenus. - UpdateMenu(mMenu); - - *aResult = true; - return NS_OK; -} - -NS_IMETHODIMP -nsStandaloneNativeMenu::GetNativeMenu(void ** aVoidPointer) -{ - if (mMenu) { - *aVoidPointer = mMenu->NativeData(); - [[(NSObject *)(*aVoidPointer) retain] autorelease]; - return NS_OK; - } else { - *aVoidPointer = nullptr; - return NS_ERROR_NOT_INITIALIZED; - } -} - -static NSMenuItem * -NativeMenuItemWithLocation(NSMenu * currentSubmenu, NSString * locationString) -{ - NSArray * indexes = [locationString componentsSeparatedByString:@"|"]; - NSUInteger indexCount = [indexes count]; - if (indexCount == 0) - return nil; - - for (NSUInteger i = 0; i < indexCount; i++) { - NSInteger targetIndex = [[indexes objectAtIndex:i] integerValue]; - NSInteger itemCount = [currentSubmenu numberOfItems]; - if (targetIndex < itemCount) { - NSMenuItem* menuItem = [currentSubmenu itemAtIndex:targetIndex]; - - // If this is the last index, just return the menu item. - if (i == (indexCount - 1)) - return menuItem; - - // If this is not the last index, find the submenu and keep going. - if ([menuItem hasSubmenu]) - currentSubmenu = [menuItem submenu]; - else - return nil; - } - } - - return nil; -} - -NS_IMETHODIMP -nsStandaloneNativeMenu::ActivateNativeMenuItemAt(const nsAString& indexString) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - if (!mMenu) - return NS_ERROR_NOT_INITIALIZED; - - NSString * locationString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading()) - length:indexString.Length()]; - NSMenu * menu = static_cast<NSMenu *> (mMenu->NativeData()); - NSMenuItem * item = NativeMenuItemWithLocation(menu, locationString); - - // We can't perform an action on an item with a submenu, that will raise - // an obj-c exception. - if (item && ![item hasSubmenu]) { - NSMenu * parent = [item menu]; - if (parent) { - // NSLog(@"Performing action for native menu item titled: %@\n", - // [[currentSubmenu itemAtIndex:targetIndex] title]); - [parent performActionForItemAtIndex:[parent indexOfItem:item]]; - return NS_OK; - } - } - - return NS_ERROR_FAILURE; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsStandaloneNativeMenu::ForceUpdateNativeMenuAt(const nsAString& indexString) -{ - if (!mMenu) - return NS_ERROR_NOT_INITIALIZED; - - NSString* locationString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading()) - length:indexString.Length()]; - NSArray* indexes = [locationString componentsSeparatedByString:@"|"]; - unsigned int indexCount = [indexes count]; - if (indexCount == 0) - return NS_OK; - - nsMenuX* currentMenu = mMenu; - - // now find the correct submenu - for (unsigned int i = 1; currentMenu && i < indexCount; i++) { - int targetIndex = [[indexes objectAtIndex:i] intValue]; - int visible = 0; - uint32_t length = currentMenu->GetItemCount(); - for (unsigned int j = 0; j < length; j++) { - nsMenuObjectX* targetMenu = currentMenu->GetItemAt(j); - if (!targetMenu) - return NS_OK; - if (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(targetMenu->Content())) { - visible++; - if (targetMenu->MenuObjectType() == eSubmenuObjectType && visible == (targetIndex + 1)) { - currentMenu = static_cast<nsMenuX*>(targetMenu); - // fake open/close to cause lazy update to happen - currentMenu->MenuOpened(); - currentMenu->MenuClosed(); - break; - } - } - } - } - - return NS_OK; -} - -void -nsStandaloneNativeMenu::IconUpdated() -{ - if (mContainerStatusBarItem) { - [mContainerStatusBarItem setImage:[mMenu->NativeMenuItem() image]]; - } -} - -void -nsStandaloneNativeMenu::SetContainerStatusBarItem(NSStatusItem* aItem) -{ - mContainerStatusBarItem = aItem; - IconUpdated(); -} diff --git a/widget/cocoa/nsSystemStatusBarCocoa.h b/widget/cocoa/nsSystemStatusBarCocoa.h deleted file mode 100644 index 51aa4df00d..0000000000 --- a/widget/cocoa/nsSystemStatusBarCocoa.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ -/* 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 nsSystemStatusBarCocoa_h_ -#define nsSystemStatusBarCocoa_h_ - -#include "mozilla/RefPtr.h" -#include "nsISystemStatusBar.h" -#include "nsClassHashtable.h" - -class nsStandaloneNativeMenu; -@class NSStatusItem; - -class nsSystemStatusBarCocoa : public nsISystemStatusBar -{ -public: - nsSystemStatusBarCocoa() {} - - NS_DECL_ISUPPORTS - NS_DECL_NSISYSTEMSTATUSBAR - -protected: - virtual ~nsSystemStatusBarCocoa() {} - - struct StatusItem - { - explicit StatusItem(nsStandaloneNativeMenu* aMenu); - ~StatusItem(); - - private: - RefPtr<nsStandaloneNativeMenu> mMenu; - NSStatusItem* mStatusItem; - }; - - nsClassHashtable<nsISupportsHashKey, StatusItem> mItems; -}; - -#endif // nsSystemStatusBarCocoa_h_ diff --git a/widget/cocoa/nsSystemStatusBarCocoa.mm b/widget/cocoa/nsSystemStatusBarCocoa.mm deleted file mode 100644 index 522da71451..0000000000 --- a/widget/cocoa/nsSystemStatusBarCocoa.mm +++ /dev/null @@ -1,74 +0,0 @@ -/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ -/* 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/. */ - -#import <Cocoa/Cocoa.h> - -#include "nsComponentManagerUtils.h" -#include "nsSystemStatusBarCocoa.h" -#include "nsStandaloneNativeMenu.h" -#include "nsObjCExceptions.h" -#include "nsIDOMElement.h" - -NS_IMPL_ISUPPORTS(nsSystemStatusBarCocoa, nsISystemStatusBar) - -NS_IMETHODIMP -nsSystemStatusBarCocoa::AddItem(nsIDOMElement* aDOMElement) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - RefPtr<nsStandaloneNativeMenu> menu = new nsStandaloneNativeMenu(); - nsresult rv = menu->Init(aDOMElement); - if (NS_FAILED(rv)) { - return rv; - } - - nsCOMPtr<nsISupports> keyPtr = aDOMElement; - mItems.Put(keyPtr, new StatusItem(menu)); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -NS_IMETHODIMP -nsSystemStatusBarCocoa::RemoveItem(nsIDOMElement* aDOMElement) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - mItems.Remove(aDOMElement); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -nsSystemStatusBarCocoa::StatusItem::StatusItem(nsStandaloneNativeMenu* aMenu) - : mMenu(aMenu) -{ - MOZ_COUNT_CTOR(nsSystemStatusBarCocoa::StatusItem); - - NSMenu* nativeMenu = nil; - mMenu->GetNativeMenu(reinterpret_cast<void**>(&nativeMenu)); - - mStatusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain]; - [mStatusItem setMenu:nativeMenu]; - [mStatusItem setHighlightMode:YES]; - - // We want the status item to get its image from menu item that mMenu was - // initialized with. Icon loads are asynchronous, so we need to let the menu - // know about the item so that it can update its icon as soon as it has - // loaded. - mMenu->SetContainerStatusBarItem(mStatusItem); -} - -nsSystemStatusBarCocoa::StatusItem::~StatusItem() -{ - mMenu->SetContainerStatusBarItem(nil); - [[NSStatusBar systemStatusBar] removeStatusItem:mStatusItem]; - [mStatusItem release]; - mStatusItem = nil; - - MOZ_COUNT_DTOR(nsSystemStatusBarCocoa::StatusItem); -} diff --git a/widget/cocoa/nsToolkit.h b/widget/cocoa/nsToolkit.h deleted file mode 100644 index 1631a8ac24..0000000000 --- a/widget/cocoa/nsToolkit.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -*- 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 nsToolkit_h_ -#define nsToolkit_h_ - -#include "nscore.h" - -#import <Carbon/Carbon.h> -#import <Cocoa/Cocoa.h> -#import <objc/Object.h> -#import <IOKit/IOKitLib.h> - -class nsToolkit -{ -public: - nsToolkit(); - virtual ~nsToolkit(); - - static nsToolkit* GetToolkit(); - - static void Shutdown() { - delete gToolkit; - gToolkit = nullptr; - } - - static void PostSleepWakeNotification(const char* aNotification); - - static nsresult SwizzleMethods(Class aClass, SEL orgMethod, SEL posedMethod, - bool classMethods = false); - - void RegisterForAllProcessMouseEvents(); - void UnregisterAllProcessMouseEventHandlers(); - -protected: - - nsresult RegisterForSleepWakeNotifications(); - void RemoveSleepWakeNotifications(); - -protected: - - static nsToolkit* gToolkit; - - CFRunLoopSourceRef mSleepWakeNotificationRLS; - io_object_t mPowerNotifier; - - CFMachPortRef mEventTapPort; - CFRunLoopSourceRef mEventTapRLS; -}; - -#endif // nsToolkit_h_ diff --git a/widget/cocoa/nsToolkit.mm b/widget/cocoa/nsToolkit.mm deleted file mode 100644 index 4d0222d5d3..0000000000 --- a/widget/cocoa/nsToolkit.mm +++ /dev/null @@ -1,326 +0,0 @@ -/* -*- 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/. */ - -#include "nsToolkit.h" - -#include <ctype.h> -#include <stdlib.h> -#include <stdio.h> - -#include <mach/mach_port.h> -#include <mach/mach_interface.h> -#include <mach/mach_init.h> - -extern "C" { -#include <mach-o/getsect.h> -} -#include <unistd.h> -#include <dlfcn.h> - -#import <Cocoa/Cocoa.h> -#import <IOKit/pwr_mgt/IOPMLib.h> -#import <IOKit/IOMessage.h> - -#include "nsCocoaUtils.h" -#include "nsObjCExceptions.h" - -#include "nsGkAtoms.h" -#include "nsIRollupListener.h" -#include "nsIWidget.h" -#include "nsBaseWidget.h" - -#include "nsIObserverService.h" -#include "nsIServiceManager.h" - -#include "mozilla/Preferences.h" -#include "mozilla/Services.h" - -using namespace mozilla; - -static io_connect_t gRootPort = MACH_PORT_NULL; - -nsToolkit* nsToolkit::gToolkit = nullptr; - -nsToolkit::nsToolkit() -: mSleepWakeNotificationRLS(nullptr) -, mEventTapPort(nullptr) -, mEventTapRLS(nullptr) -{ - MOZ_COUNT_CTOR(nsToolkit); - RegisterForSleepWakeNotifications(); -} - -nsToolkit::~nsToolkit() -{ - MOZ_COUNT_DTOR(nsToolkit); - RemoveSleepWakeNotifications(); - UnregisterAllProcessMouseEventHandlers(); -} - -void -nsToolkit::PostSleepWakeNotification(const char* aNotification) -{ - nsCOMPtr<nsIObserverService> observerService = services::GetObserverService(); - if (observerService) - observerService->NotifyObservers(nullptr, aNotification, nullptr); -} - -// http://developer.apple.com/documentation/DeviceDrivers/Conceptual/IOKitFundamentals/PowerMgmt/chapter_10_section_3.html -static void ToolkitSleepWakeCallback(void *refCon, io_service_t service, natural_t messageType, void * messageArgument) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - switch (messageType) - { - case kIOMessageSystemWillSleep: - // System is going to sleep now. - nsToolkit::PostSleepWakeNotification(NS_WIDGET_SLEEP_OBSERVER_TOPIC); - ::IOAllowPowerChange(gRootPort, (long)messageArgument); - break; - - case kIOMessageCanSystemSleep: - // In this case, the computer has been idle for several minutes - // and will sleep soon so you must either allow or cancel - // this notification. Important: if you don’t respond, there will - // be a 30-second timeout before the computer sleeps. - // In Mozilla's case, we always allow sleep. - ::IOAllowPowerChange(gRootPort,(long)messageArgument); - break; - - case kIOMessageSystemHasPoweredOn: - // Handle wakeup. - nsToolkit::PostSleepWakeNotification(NS_WIDGET_WAKE_OBSERVER_TOPIC); - break; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -nsresult -nsToolkit::RegisterForSleepWakeNotifications() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - IONotificationPortRef notifyPortRef; - - NS_ASSERTION(!mSleepWakeNotificationRLS, "Already registered for sleep/wake"); - - gRootPort = ::IORegisterForSystemPower(0, ¬ifyPortRef, ToolkitSleepWakeCallback, &mPowerNotifier); - if (gRootPort == MACH_PORT_NULL) { - NS_ERROR("IORegisterForSystemPower failed"); - return NS_ERROR_FAILURE; - } - - mSleepWakeNotificationRLS = ::IONotificationPortGetRunLoopSource(notifyPortRef); - ::CFRunLoopAddSource(::CFRunLoopGetCurrent(), - mSleepWakeNotificationRLS, - kCFRunLoopDefaultMode); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - -void -nsToolkit::RemoveSleepWakeNotifications() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (mSleepWakeNotificationRLS) { - ::IODeregisterForSystemPower(&mPowerNotifier); - ::CFRunLoopRemoveSource(::CFRunLoopGetCurrent(), - mSleepWakeNotificationRLS, - kCFRunLoopDefaultMode); - - mSleepWakeNotificationRLS = nullptr; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// Converts aPoint from the CoreGraphics "global display coordinate" system -// (which includes all displays/screens and has a top-left origin) to its -// (presumed) Cocoa counterpart (assumed to be the same as the "screen -// coordinates" system), which has a bottom-left origin. -static NSPoint ConvertCGGlobalToCocoaScreen(CGPoint aPoint) -{ - NSPoint cocoaPoint; - cocoaPoint.x = aPoint.x; - cocoaPoint.y = nsCocoaUtils::FlippedScreenY(aPoint.y); - return cocoaPoint; -} - -// Since our event tap is "listen only", events arrive here a little after -// they've already been processed. -static CGEventRef EventTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - if ((type == kCGEventTapDisabledByUserInput) || - (type == kCGEventTapDisabledByTimeout)) - return event; - if ([NSApp isActive]) - return event; - - nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); - NS_ENSURE_TRUE(rollupListener, event); - nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget(); - if (!rollupWidget) - return event; - - // Don't bother with rightMouseDown events here -- because of the delay, - // we'll end up closing browser context menus that we just opened. Since - // these events usually raise a context menu, we'll handle them by hooking - // the @"com.apple.HIToolbox.beginMenuTrackingNotification" distributed - // notification (in nsAppShell.mm's AppShellDelegate). - if (type == kCGEventRightMouseDown) - return event; - NSWindow *ctxMenuWindow = (NSWindow*) rollupWidget->GetNativeData(NS_NATIVE_WINDOW); - if (!ctxMenuWindow) - return event; - NSPoint screenLocation = ConvertCGGlobalToCocoaScreen(CGEventGetLocation(event)); - // Don't roll up the rollup widget if our mouseDown happens over it (doing - // so would break the corresponding context menu). - if (NSPointInRect(screenLocation, [ctxMenuWindow frame])) - return event; - rollupListener->Rollup(0, false, nullptr, nullptr); - return event; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NULL); -} - -// Cocoa Firefox's use of custom context menus requires that we explicitly -// handle mouse events from other processes that the OS handles -// "automatically" for native context menus -- mouseMoved events so that -// right-click context menus work properly when our browser doesn't have the -// focus (bmo bug 368077), and mouseDown events so that our browser can -// dismiss a context menu when a mouseDown happens in another process (bmo -// bug 339945). -void -nsToolkit::RegisterForAllProcessMouseEvents() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (getenv("MOZ_DEBUG")) - return; - - // Don't do this for apps that use native context menus. -#ifdef MOZ_USE_NATIVE_POPUP_WINDOWS - return; -#endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */ - - if (!mEventTapRLS) { - // Using an event tap for mouseDown events (instead of installing a - // handler for them on the EventMonitor target) works around an Apple - // bug that causes OS menus (like the Clock menu) not to work properly - // on OS X 10.4.X and below (bmo bug 381448). - // We install our event tap "listen only" to get around yet another Apple - // bug -- when we install it as an event filter on any kind of mouseDown - // event, that kind of event stops working in the main menu, and usually - // mouse event processing stops working in all apps in the current login - // session (so the entire OS appears to be hung)! The downside of - // installing listen-only is that events arrive at our handler slightly - // after they've already been processed. - mEventTapPort = CGEventTapCreate(kCGSessionEventTap, - kCGHeadInsertEventTap, - kCGEventTapOptionListenOnly, - CGEventMaskBit(kCGEventLeftMouseDown) - | CGEventMaskBit(kCGEventRightMouseDown) - | CGEventMaskBit(kCGEventOtherMouseDown), - EventTapCallback, - nullptr); - if (!mEventTapPort) - return; - mEventTapRLS = CFMachPortCreateRunLoopSource(nullptr, mEventTapPort, 0); - if (!mEventTapRLS) { - CFRelease(mEventTapPort); - mEventTapPort = nullptr; - return; - } - CFRunLoopAddSource(CFRunLoopGetCurrent(), mEventTapRLS, kCFRunLoopDefaultMode); - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -void -nsToolkit::UnregisterAllProcessMouseEventHandlers() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (mEventTapRLS) { - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), mEventTapRLS, - kCFRunLoopDefaultMode); - CFRelease(mEventTapRLS); - mEventTapRLS = nullptr; - } - if (mEventTapPort) { - // mEventTapPort must be invalidated as well as released. Otherwise the - // event tap doesn't get destroyed until the browser process ends (it - // keeps showing up in the list returned by CGGetEventTapList()). - CFMachPortInvalidate(mEventTapPort); - CFRelease(mEventTapPort); - mEventTapPort = nullptr; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// Return the nsToolkit instance. If a toolkit does not yet exist, then one -// will be created. -// static -nsToolkit* nsToolkit::GetToolkit() -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - if (!gToolkit) { - gToolkit = new nsToolkit(); - } - - return gToolkit; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nullptr); -} - -// An alternative to [NSObject poseAsClass:] that isn't deprecated on OS X -// Leopard and is available to 64-bit binaries on Leopard and above. Based on -// ideas and code from http://www.cocoadev.com/index.pl?MethodSwizzling. -// Since the Method type becomes an opaque type as of Objective-C 2.0, we'll -// have to switch to using accessor methods like method_exchangeImplementations() -// when we build 64-bit binaries that use Objective-C 2.0 (on and for Leopard -// and above). -// -// Be aware that, if aClass doesn't have an orgMethod selector but one of its -// superclasses does, the method substitution will (in effect) take place in -// that superclass (rather than in aClass itself). The substitution has -// effect on the class where it takes place and all of that class's -// subclasses. In order for method swizzling to work properly, posedMethod -// needs to be unique in the class where the substitution takes place and all -// of its subclasses. -nsresult nsToolkit::SwizzleMethods(Class aClass, SEL orgMethod, SEL posedMethod, - bool classMethods) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - Method original = nil; - Method posed = nil; - - if (classMethods) { - original = class_getClassMethod(aClass, orgMethod); - posed = class_getClassMethod(aClass, posedMethod); - } else { - original = class_getInstanceMethod(aClass, orgMethod); - posed = class_getInstanceMethod(aClass, posedMethod); - } - - if (!original || !posed) - return NS_ERROR_FAILURE; - - method_exchangeImplementations(original, posed); - - return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} diff --git a/widget/cocoa/nsWidgetFactory.mm b/widget/cocoa/nsWidgetFactory.mm deleted file mode 100644 index 3bddaf95ce..0000000000 --- a/widget/cocoa/nsWidgetFactory.mm +++ /dev/null @@ -1,219 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; 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/. */ - -#include "nsIFactory.h" -#include "nsISupports.h" -#include "nsIComponentManager.h" -#include "mozilla/ModuleUtils.h" -#include "mozilla/WidgetUtils.h" - -#include "nsWidgetsCID.h" - -#include "nsChildView.h" -#include "nsCocoaWindow.h" -#include "nsAppShell.h" -#include "nsAppShellSingleton.h" -#include "nsFilePicker.h" -#include "nsColorPicker.h" - -#include "nsClipboard.h" -#include "nsClipboardHelper.h" -#include "nsTransferable.h" -#include "nsHTMLFormatConverter.h" -#include "nsDragService.h" -#include "nsToolkit.h" - -#include "nsLookAndFeel.h" - -#include "nsSound.h" -#include "nsIdleServiceX.h" -#include "NativeKeyBindings.h" -#include "OSXNotificationCenter.h" - -#include "nsScreenManagerCocoa.h" -#include "nsDeviceContextSpecX.h" -#include "nsPrintOptionsX.h" -#include "nsPrintDialogX.h" -#include "nsPrintSession.h" -#include "nsToolkitCompsCID.h" - -using namespace mozilla; -using namespace mozilla::widget; - -NS_GENERIC_FACTORY_CONSTRUCTOR(nsCocoaWindow) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsChildView) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsFilePicker) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsColorPicker) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsSound) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboard) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragService) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerCocoa) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceContextSpecX) -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintOptionsX, Init) -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintDialogServiceX, Init) -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSession, Init) -NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceX, nsIdleServiceX::GetInstance) -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(OSXNotificationCenter, Init) - -#include "nsMenuBarX.h" -NS_GENERIC_FACTORY_CONSTRUCTOR(nsNativeMenuServiceX) - -#include "nsBidiKeyboard.h" -NS_GENERIC_FACTORY_CONSTRUCTOR(nsBidiKeyboard) - -#include "nsNativeThemeCocoa.h" -NS_GENERIC_FACTORY_CONSTRUCTOR(nsNativeThemeCocoa) - -#include "nsMacDockSupport.h" -NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacDockSupport) - -#include "nsMacWebAppUtils.h" -NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacWebAppUtils) - -#include "nsStandaloneNativeMenu.h" -NS_GENERIC_FACTORY_CONSTRUCTOR(nsStandaloneNativeMenu) - -#include "nsSystemStatusBarCocoa.h" -NS_GENERIC_FACTORY_CONSTRUCTOR(nsSystemStatusBarCocoa) - -#include "GfxInfo.h" -namespace mozilla { -namespace widget { -// This constructor should really be shared with all platforms. -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxInfo, Init) -} // namespace widget -} // namespace mozilla - -NS_DEFINE_NAMED_CID(NS_WINDOW_CID); -NS_DEFINE_NAMED_CID(NS_POPUP_CID); -NS_DEFINE_NAMED_CID(NS_CHILD_CID); -NS_DEFINE_NAMED_CID(NS_FILEPICKER_CID); -NS_DEFINE_NAMED_CID(NS_COLORPICKER_CID); -NS_DEFINE_NAMED_CID(NS_APPSHELL_CID); -NS_DEFINE_NAMED_CID(NS_SOUND_CID); -NS_DEFINE_NAMED_CID(NS_TRANSFERABLE_CID); -NS_DEFINE_NAMED_CID(NS_HTMLFORMATCONVERTER_CID); -NS_DEFINE_NAMED_CID(NS_CLIPBOARD_CID); -NS_DEFINE_NAMED_CID(NS_CLIPBOARDHELPER_CID); -NS_DEFINE_NAMED_CID(NS_DRAGSERVICE_CID); -NS_DEFINE_NAMED_CID(NS_BIDIKEYBOARD_CID); -NS_DEFINE_NAMED_CID(NS_THEMERENDERER_CID); -NS_DEFINE_NAMED_CID(NS_SCREENMANAGER_CID); -NS_DEFINE_NAMED_CID(NS_DEVICE_CONTEXT_SPEC_CID); -NS_DEFINE_NAMED_CID(NS_PRINTSESSION_CID); -NS_DEFINE_NAMED_CID(NS_PRINTSETTINGSSERVICE_CID); -NS_DEFINE_NAMED_CID(NS_PRINTDIALOGSERVICE_CID); -NS_DEFINE_NAMED_CID(NS_IDLE_SERVICE_CID); -NS_DEFINE_NAMED_CID(NS_SYSTEMALERTSSERVICE_CID); -NS_DEFINE_NAMED_CID(NS_NATIVEMENUSERVICE_CID); -NS_DEFINE_NAMED_CID(NS_MACDOCKSUPPORT_CID); -NS_DEFINE_NAMED_CID(NS_MACWEBAPPUTILS_CID); -NS_DEFINE_NAMED_CID(NS_STANDALONENATIVEMENU_CID); -NS_DEFINE_NAMED_CID(NS_MACSYSTEMSTATUSBAR_CID); -NS_DEFINE_NAMED_CID(NS_GFXINFO_CID); - -static const mozilla::Module::CIDEntry kWidgetCIDs[] = { - { &kNS_WINDOW_CID, false, NULL, nsCocoaWindowConstructor }, - { &kNS_POPUP_CID, false, NULL, nsCocoaWindowConstructor }, - { &kNS_CHILD_CID, false, NULL, nsChildViewConstructor }, - { &kNS_FILEPICKER_CID, false, NULL, nsFilePickerConstructor, - mozilla::Module::MAIN_PROCESS_ONLY }, - { &kNS_COLORPICKER_CID, false, NULL, nsColorPickerConstructor, - mozilla::Module::MAIN_PROCESS_ONLY }, - { &kNS_APPSHELL_CID, false, NULL, nsAppShellConstructor, mozilla::Module::ALLOW_IN_GPU_PROCESS }, - { &kNS_SOUND_CID, false, NULL, nsSoundConstructor, - mozilla::Module::MAIN_PROCESS_ONLY }, - { &kNS_TRANSFERABLE_CID, false, NULL, nsTransferableConstructor }, - { &kNS_HTMLFORMATCONVERTER_CID, false, NULL, nsHTMLFormatConverterConstructor }, - { &kNS_CLIPBOARD_CID, false, NULL, nsClipboardConstructor, - mozilla::Module::MAIN_PROCESS_ONLY }, - { &kNS_CLIPBOARDHELPER_CID, false, NULL, nsClipboardHelperConstructor }, - { &kNS_DRAGSERVICE_CID, false, NULL, nsDragServiceConstructor, - mozilla::Module::MAIN_PROCESS_ONLY }, - { &kNS_BIDIKEYBOARD_CID, false, NULL, nsBidiKeyboardConstructor, - mozilla::Module::MAIN_PROCESS_ONLY }, - { &kNS_THEMERENDERER_CID, false, NULL, nsNativeThemeCocoaConstructor }, - { &kNS_SCREENMANAGER_CID, false, NULL, nsScreenManagerCocoaConstructor, - mozilla::Module::MAIN_PROCESS_ONLY }, - { &kNS_DEVICE_CONTEXT_SPEC_CID, false, NULL, nsDeviceContextSpecXConstructor }, - { &kNS_PRINTSESSION_CID, false, NULL, nsPrintSessionConstructor }, - { &kNS_PRINTSETTINGSSERVICE_CID, false, NULL, nsPrintOptionsXConstructor }, - { &kNS_PRINTDIALOGSERVICE_CID, false, NULL, nsPrintDialogServiceXConstructor }, - { &kNS_IDLE_SERVICE_CID, false, NULL, nsIdleServiceXConstructor }, - { &kNS_SYSTEMALERTSSERVICE_CID, false, NULL, OSXNotificationCenterConstructor }, - { &kNS_NATIVEMENUSERVICE_CID, false, NULL, nsNativeMenuServiceXConstructor }, - { &kNS_MACDOCKSUPPORT_CID, false, NULL, nsMacDockSupportConstructor }, - { &kNS_MACWEBAPPUTILS_CID, false, NULL, nsMacWebAppUtilsConstructor }, - { &kNS_STANDALONENATIVEMENU_CID, false, NULL, nsStandaloneNativeMenuConstructor }, - { &kNS_MACSYSTEMSTATUSBAR_CID, false, NULL, nsSystemStatusBarCocoaConstructor }, - { &kNS_GFXINFO_CID, false, NULL, mozilla::widget::GfxInfoConstructor }, - { NULL } -}; - -static const mozilla::Module::ContractIDEntry kWidgetContracts[] = { - { "@mozilla.org/widgets/window/mac;1", &kNS_WINDOW_CID }, - { "@mozilla.org/widgets/popup/mac;1", &kNS_POPUP_CID }, - { "@mozilla.org/widgets/childwindow/mac;1", &kNS_CHILD_CID }, - { "@mozilla.org/filepicker;1", &kNS_FILEPICKER_CID, - mozilla::Module::MAIN_PROCESS_ONLY }, - { "@mozilla.org/colorpicker;1", &kNS_COLORPICKER_CID, - mozilla::Module::MAIN_PROCESS_ONLY }, - { "@mozilla.org/widget/appshell/mac;1", &kNS_APPSHELL_CID, mozilla::Module::ALLOW_IN_GPU_PROCESS }, - { "@mozilla.org/sound;1", &kNS_SOUND_CID, - mozilla::Module::MAIN_PROCESS_ONLY }, - { "@mozilla.org/widget/transferable;1", &kNS_TRANSFERABLE_CID }, - { "@mozilla.org/widget/htmlformatconverter;1", &kNS_HTMLFORMATCONVERTER_CID }, - { "@mozilla.org/widget/clipboard;1", &kNS_CLIPBOARD_CID, - mozilla::Module::MAIN_PROCESS_ONLY }, - { "@mozilla.org/widget/clipboardhelper;1", &kNS_CLIPBOARDHELPER_CID }, - { "@mozilla.org/widget/dragservice;1", &kNS_DRAGSERVICE_CID, - mozilla::Module::MAIN_PROCESS_ONLY }, - { "@mozilla.org/widget/bidikeyboard;1", &kNS_BIDIKEYBOARD_CID, - mozilla::Module::MAIN_PROCESS_ONLY }, - { "@mozilla.org/chrome/chrome-native-theme;1", &kNS_THEMERENDERER_CID }, - { "@mozilla.org/gfx/screenmanager;1", &kNS_SCREENMANAGER_CID, - mozilla::Module::MAIN_PROCESS_ONLY }, - { "@mozilla.org/gfx/devicecontextspec;1", &kNS_DEVICE_CONTEXT_SPEC_CID }, - { "@mozilla.org/gfx/printsession;1", &kNS_PRINTSESSION_CID }, - { "@mozilla.org/gfx/printsettings-service;1", &kNS_PRINTSETTINGSSERVICE_CID }, - { NS_PRINTDIALOGSERVICE_CONTRACTID, &kNS_PRINTDIALOGSERVICE_CID }, - { "@mozilla.org/widget/idleservice;1", &kNS_IDLE_SERVICE_CID }, - { "@mozilla.org/system-alerts-service;1", &kNS_SYSTEMALERTSSERVICE_CID }, - { "@mozilla.org/widget/nativemenuservice;1", &kNS_NATIVEMENUSERVICE_CID }, - { "@mozilla.org/widget/macdocksupport;1", &kNS_MACDOCKSUPPORT_CID }, - { "@mozilla.org/widget/mac-web-app-utils;1", &kNS_MACWEBAPPUTILS_CID }, - { "@mozilla.org/widget/standalonenativemenu;1", &kNS_STANDALONENATIVEMENU_CID }, - { "@mozilla.org/widget/macsystemstatusbar;1", &kNS_MACSYSTEMSTATUSBAR_CID }, - { "@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID }, - { NULL } -}; - -static void -nsWidgetCocoaModuleDtor() -{ - // Shutdown all XP level widget classes. - WidgetUtils::Shutdown(); - - NativeKeyBindings::Shutdown(); - nsLookAndFeel::Shutdown(); - nsToolkit::Shutdown(); - nsAppShellShutdown(); -} - -static const mozilla::Module kWidgetModule = { - mozilla::Module::kVersion, - kWidgetCIDs, - kWidgetContracts, - NULL, - NULL, - nsAppShellInit, - nsWidgetCocoaModuleDtor, - mozilla::Module::ALLOW_IN_GPU_PROCESS -}; - -NSMODULE_DEFN(nsWidgetMacModule) = &kWidgetModule; diff --git a/widget/cocoa/nsWindowMap.h b/widget/cocoa/nsWindowMap.h deleted file mode 100644 index c6ad72c010..0000000000 --- a/widget/cocoa/nsWindowMap.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- 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 nsWindowMap_h_ -#define nsWindowMap_h_ - -#import <Cocoa/Cocoa.h> - -// WindowDataMap -// -// In both mozilla and embedding apps, we need to have a place to put -// per-top-level-window logic and data, to handle such things as IME -// commit when the window gains/loses focus. We can't use a window -// delegate, because an embeddor probably already has one. Nor can we -// subclass NSWindow, again because we can't impose that burden on the -// embeddor. -// -// So we have a global map of NSWindow -> TopLevelWindowData, and set -// up TopLevelWindowData as a notification observer etc. - -@interface WindowDataMap : NSObject -{ -@private - NSMutableDictionary* mWindowMap; // dict of TopLevelWindowData keyed by address of NSWindow -} - -+ (WindowDataMap*)sharedWindowDataMap; - -- (void)ensureDataForWindow:(NSWindow*)inWindow; -- (id)dataForWindow:(NSWindow*)inWindow; - -// set data for a given window. inData is retained (and any previously set data -// is released). -- (void)setData:(id)inData forWindow:(NSWindow*)inWindow; - -// remove the data for the given window. the data is released. -- (void)removeDataForWindow:(NSWindow*)inWindow; - -@end - -@class ChildView; - -// TopLevelWindowData -// -// Class to hold per-window data, and handle window state changes. - -@interface TopLevelWindowData : NSObject -{ -@private -} - -- (id)initWithWindow:(NSWindow*)inWindow; -+ (void)activateInWindow:(NSWindow*)aWindow; -+ (void)deactivateInWindow:(NSWindow*)aWindow; -+ (void)activateInWindowViews:(NSWindow*)aWindow; -+ (void)deactivateInWindowViews:(NSWindow*)aWindow; - -@end - -#endif // nsWindowMap_h_ diff --git a/widget/cocoa/nsWindowMap.mm b/widget/cocoa/nsWindowMap.mm deleted file mode 100644 index c43b024086..0000000000 --- a/widget/cocoa/nsWindowMap.mm +++ /dev/null @@ -1,311 +0,0 @@ -/* -*- 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/. */ - -#include "nsWindowMap.h" -#include "nsObjCExceptions.h" -#include "nsChildView.h" -#include "nsCocoaWindow.h" - -@interface WindowDataMap(Private) - -- (NSString*)keyForWindow:(NSWindow*)inWindow; - -@end - -@interface TopLevelWindowData(Private) - -- (void)windowResignedKey:(NSNotification*)inNotification; -- (void)windowBecameKey:(NSNotification*)inNotification; -- (void)windowWillClose:(NSNotification*)inNotification; - -@end - -#pragma mark - - -@implementation WindowDataMap - -+ (WindowDataMap*)sharedWindowDataMap -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - static WindowDataMap* sWindowMap = nil; - if (!sWindowMap) - sWindowMap = [[WindowDataMap alloc] init]; - - return sWindowMap; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (id)init -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - if ((self = [super init])) { - mWindowMap = [[NSMutableDictionary alloc] initWithCapacity:10]; - } - return self; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (void)dealloc -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [mWindowMap release]; - [super dealloc]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)ensureDataForWindow:(NSWindow*)inWindow -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!inWindow || [self dataForWindow:inWindow]) - return; - - TopLevelWindowData* windowData = [[TopLevelWindowData alloc] initWithWindow:inWindow]; - [self setData:windowData forWindow:inWindow]; // takes ownership - [windowData release]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (id)dataForWindow:(NSWindow*)inWindow -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - return [mWindowMap objectForKey:[self keyForWindow:inWindow]]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (void)setData:(id)inData forWindow:(NSWindow*)inWindow -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [mWindowMap setObject:inData forKey:[self keyForWindow:inWindow]]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)removeDataForWindow:(NSWindow*)inWindow -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [mWindowMap removeObjectForKey:[self keyForWindow:inWindow]]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (NSString*)keyForWindow:(NSWindow*)inWindow -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - return [NSString stringWithFormat:@"%p", inWindow]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -@end - -// TopLevelWindowData -// -// This class holds data about top-level windows. We can't use a window -// delegate, because an embedder may already have one. - -@implementation TopLevelWindowData - -- (id)initWithWindow:(NSWindow*)inWindow -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - if ((self = [super init])) { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowBecameKey:) - name:NSWindowDidBecomeKeyNotification - object:inWindow]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowResignedKey:) - name:NSWindowDidResignKeyNotification - object:inWindow]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowBecameMain:) - name:NSWindowDidBecomeMainNotification - object:inWindow]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowResignedMain:) - name:NSWindowDidResignMainNotification - object:inWindow]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(windowWillClose:) - name:NSWindowWillCloseNotification - object:inWindow]; - } - return self; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} - -- (void)dealloc -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [super dealloc]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// As best I can tell, if the notification's object has a corresponding -// top-level widget (an nsCocoaWindow object), it has a delegate (set in -// nsCocoaWindow::StandardCreate()) of class WindowDelegate, and otherwise -// not (Camino didn't use top-level widgets (nsCocoaWindow objects) -- -// only child widgets (nsChildView objects)). (The notification is sent -// to windowBecameKey: or windowBecameMain: below.) -// -// For use with clients that (like Firefox) do use top-level widgets (and -// have NSWindow delegates of class WindowDelegate). -+ (void)activateInWindow:(NSWindow*)aWindow -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - WindowDelegate* delegate = (WindowDelegate*) [aWindow delegate]; - if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) - return; - - if ([delegate toplevelActiveState]) - return; - [delegate sendToplevelActivateEvents]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// See comments above activateInWindow: -// -// If we're using top-level widgets (nsCocoaWindow objects), we send them -// NS_DEACTIVATE events (which propagate to child widgets (nsChildView -// objects) via nsWebShellWindow::HandleEvent()). -// -// For use with clients that (like Firefox) do use top-level widgets (and -// have NSWindow delegates of class WindowDelegate). -+ (void)deactivateInWindow:(NSWindow*)aWindow -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - WindowDelegate* delegate = (WindowDelegate*) [aWindow delegate]; - if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) - return; - - if (![delegate toplevelActiveState]) - return; - [delegate sendToplevelDeactivateEvents]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// For use with clients that (like Camino) don't use top-level widgets (and -// don't have NSWindow delegates of class WindowDelegate). -+ (void)activateInWindowViews:(NSWindow*)aWindow -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - id firstResponder = [aWindow firstResponder]; - if ([firstResponder isKindOfClass:[ChildView class]]) - [firstResponder viewsWindowDidBecomeKey]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// For use with clients that (like Camino) don't use top-level widgets (and -// don't have NSWindow delegates of class WindowDelegate). -+ (void)deactivateInWindowViews:(NSWindow*)aWindow -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - id firstResponder = [aWindow firstResponder]; - if ([firstResponder isKindOfClass:[ChildView class]]) - [firstResponder viewsWindowDidResignKey]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// We make certain exceptions for top-level windows in non-embedders (see -// comment above windowBecameMain below). And we need (elsewhere) to guard -// against sending duplicate events. But in general the NS_ACTIVATE event -// should be sent when a native window becomes key, and the NS_DEACTIVATE -// event should be sent when it resignes key. -- (void)windowBecameKey:(NSNotification*)inNotification -{ - NSWindow* window = (NSWindow*)[inNotification object]; - - id delegate = [window delegate]; - if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) { - [TopLevelWindowData activateInWindowViews:window]; - } else if ([window isSheet]) { - [TopLevelWindowData activateInWindow:window]; - } - - [[window contentView] setNeedsDisplay:YES]; -} - -- (void)windowResignedKey:(NSNotification*)inNotification -{ - NSWindow* window = (NSWindow*)[inNotification object]; - - id delegate = [window delegate]; - if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) { - [TopLevelWindowData deactivateInWindowViews:window]; - } else if ([window isSheet]) { - [TopLevelWindowData deactivateInWindow:window]; - } - - [[window contentView] setNeedsDisplay:YES]; -} - -// The appearance of a top-level window depends on its main state (not its key -// state). So (for non-embedders) we need to ensure that a top-level window -// is main when an NS_ACTIVATE event is sent to Gecko for it. -- (void)windowBecameMain:(NSNotification*)inNotification -{ - NSWindow* window = (NSWindow*)[inNotification object]; - - id delegate = [window delegate]; - // Don't send events to a top-level window that has a sheet open above it -- - // as far as Gecko is concerned, it's inactive, and stays so until the sheet - // closes. - if (delegate && [delegate isKindOfClass:[WindowDelegate class]] && ![window attachedSheet]) - [TopLevelWindowData activateInWindow:window]; -} - -- (void)windowResignedMain:(NSNotification*)inNotification -{ - NSWindow* window = (NSWindow*)[inNotification object]; - - id delegate = [window delegate]; - if (delegate && [delegate isKindOfClass:[WindowDelegate class]] && ![window attachedSheet]) - [TopLevelWindowData deactivateInWindow:window]; -} - -- (void)windowWillClose:(NSNotification*)inNotification -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // postpone our destruction - [[self retain] autorelease]; - - // remove ourselves from the window map (which owns us) - [[WindowDataMap sharedWindowDataMap] removeDataForWindow:[inNotification object]]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -@end diff --git a/widget/cocoa/resources/MainMenu.nib/classes.nib b/widget/cocoa/resources/MainMenu.nib/classes.nib deleted file mode 100644 index b9b4b09f6b..0000000000 --- a/widget/cocoa/resources/MainMenu.nib/classes.nib +++ /dev/null @@ -1,4 +0,0 @@ -{ - IBClasses = ({CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }); - IBVersion = 1; -}
\ No newline at end of file diff --git a/widget/cocoa/resources/MainMenu.nib/info.nib b/widget/cocoa/resources/MainMenu.nib/info.nib deleted file mode 100644 index bcf3ace841..0000000000 --- a/widget/cocoa/resources/MainMenu.nib/info.nib +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>IBDocumentLocation</key> - <string>159 127 356 240 0 0 1920 1178 </string> - <key>IBEditorPositions</key> - <dict> - <key>29</key> - <string>413 971 130 44 0 0 1920 1178 </string> - </dict> - <key>IBFramework Version</key> - <string>443.0</string> - <key>IBOpenObjects</key> - <array> - <integer>29</integer> - </array> - <key>IBSystem Version</key> - <string>8F46</string> -</dict> -</plist> diff --git a/widget/cocoa/resources/MainMenu.nib/keyedobjects.nib b/widget/cocoa/resources/MainMenu.nib/keyedobjects.nib Binary files differdeleted file mode 100644 index 16b3f7e523..0000000000 --- a/widget/cocoa/resources/MainMenu.nib/keyedobjects.nib +++ /dev/null diff --git a/widget/moz.build b/widget/moz.build index c72b2066c3..acb1387341 100644 --- a/widget/moz.build +++ b/widget/moz.build @@ -5,9 +5,6 @@ toolkit = CONFIG['MOZ_WIDGET_TOOLKIT'] -if toolkit in ('cocoa', 'uikit'): - DIRS += [toolkit] - if toolkit in ('gtk2', 'gtk3'): EXPORTS += ['nsIPrintDialogService.h'] @@ -28,19 +25,8 @@ if toolkit == 'windows': 'nsIWindowsUIUtils.idl', 'nsIWinTaskbar.idl', ] -elif toolkit == 'cocoa': - XPIDL_SOURCES += [ - 'nsIMacDockSupport.idl', - 'nsIMacWebAppUtils.idl', - 'nsIStandaloneNativeMenu.idl', - 'nsISystemStatusBar.idl', - 'nsITaskbarProgress.idl', - ] - EXPORTS += [ - 'nsIPrintDialogService.h', - ] -if toolkit in ('cocoa', 'gtk2', 'gtk3'): +if toolkit in ('gtk2', 'gtk3'): EXPORTS += ['nsINativeMenuService.h'] # Don't build the DSO under the 'build' directory as windows does. @@ -216,15 +202,15 @@ if CONFIG['MOZ_X11']: 'WindowSurfaceX11SHM.cpp', ] -if toolkit in ('cocoa', 'windows'): +if toolkit in ('windows'): UNIFIED_SOURCES += [ 'nsBaseClipboard.cpp', ] -if toolkit in {'gtk2', 'gtk3', 'cocoa', 'windows', 'uikit'}: +if toolkit in {'gtk2', 'gtk3', 'windows', 'uikit'}: UNIFIED_SOURCES += ['nsBaseFilePicker.cpp'] -if toolkit in ('gtk2', 'gtk3', 'windows', 'cocoa'): +if toolkit in ('gtk2', 'gtk3', 'windows'): UNIFIED_SOURCES += ['nsNativeTheme.cpp'] if toolkit == 'gtk3': diff --git a/widget/nsIMacDockSupport.idl b/widget/nsIMacDockSupport.idl deleted file mode 100644 index 5783e9c0b9..0000000000 --- a/widget/nsIMacDockSupport.idl +++ /dev/null @@ -1,39 +0,0 @@ -/* 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/. */ - -#include "nsISupports.idl" - -interface nsIStandaloneNativeMenu; - -/** - * Allow applications to interface with the Mac OS X Dock. - * - * Applications may indicate progress on their Dock icon. Only one such - * progress indicator is available to the entire application. - */ - -[scriptable, uuid(8BE66B0C-5F71-4B74-98CF-6C2551B999B1)] -interface nsIMacDockSupport : nsISupports -{ - /** - * Menu to use for application-specific dock menu items. - */ - attribute nsIStandaloneNativeMenu dockMenu; - - /** - * Activate the application. This should be used by an application to - * activate itself when a dock menu is selected as selection of a dock menu - * item does not automatically activate the application. - * - * @param aIgnoreOtherApplications If false, the application is activated - * only if no other application is currently active. If true, the - * application activates regardless. - */ - void activateApplication(in boolean aIgnoreOtherApplications); - - /** - * Text used to badge the dock tile. - */ - attribute AString badgeText; -}; diff --git a/widget/nsIMacWebAppUtils.idl b/widget/nsIMacWebAppUtils.idl deleted file mode 100644 index 4d570a8bf0..0000000000 --- a/widget/nsIMacWebAppUtils.idl +++ /dev/null @@ -1,35 +0,0 @@ -/* 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/. */ - -#include "nsISupports.idl" - -interface nsIMacWebAppUtils; - -[scriptable, function, uuid(8c899c4f-58c1-4b74-9034-3bb64e484b68)] -interface nsITrashAppCallback : nsISupports -{ - void trashAppFinished(in nsresult rv); -}; - -/** - * Allow MozApps API to locate and manipulate natively installed apps - */ - -[scriptable, uuid(c69cf343-ea41-428b-b161-4655fd54d8e7)] -interface nsIMacWebAppUtils : nsISupports { - /** - * Find the path for an app with the given signature. - */ - AString pathForAppWithIdentifier(in AString bundleIdentifier); - - /** - * Launch the app with the given identifier, if it exists. - */ - void launchAppWithIdentifier(in AString bundleIdentifier); - - /** - * Move the app from the given directory to the Trash. - */ - void trashApp(in AString path, in nsITrashAppCallback callback); -}; diff --git a/widget/nsISystemStatusBar.idl b/widget/nsISystemStatusBar.idl deleted file mode 100644 index 9db8015199..0000000000 --- a/widget/nsISystemStatusBar.idl +++ /dev/null @@ -1,36 +0,0 @@ -/* 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/. */ - -#include "nsISupports.idl" - -interface nsIDOMElement; - -/** - * Allow applications to interface with the Mac OS X system status bar. - */ - -[scriptable, uuid(24493180-ee81-4b7c-8b17-9e69480b7b8a)] -interface nsISystemStatusBar : nsISupports -{ - /** - * Add an item to the system status bar. Each item can only be present once, - * subsequent addItem calls with the same element will be ignored. - * The system status bar holds a strong reference to the added XUL menu - * element and the item will stay in the status bar until it is removed via - * a call to removeItem, or until the process shuts down. - * @param aDOMMenuElement A XUL menu element that contains a XUL menupopup - * with regular menu content. The menu's icon is put - * into the system status bar; clicking it will open - * a menu with the contents of the menupopup. - * The menu label is not shown. - */ - void addItem(in nsIDOMElement aDOMMenuElement); - - /** - * Remove a previously-added item from the menu bar. Calling this with an - * element that has not been added before will be silently ignored. - * @param aDOMMenuElement The XUL menu element that you called addItem with. - */ - void removeItem(in nsIDOMElement aDOMMenuElement); -}; diff --git a/widget/uikit/GfxInfo.cpp b/widget/uikit/GfxInfo.cpp deleted file mode 100644 index cabe993dd0..0000000000 --- a/widget/uikit/GfxInfo.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* -*- 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/. */ - -#include "GfxInfo.h" -#include "nsServiceManagerUtils.h" - -namespace mozilla { -namespace widget { - - -#ifdef DEBUG -NS_IMPL_ISUPPORTS_INHERITED(GfxInfo, GfxInfoBase, nsIGfxInfoDebug) -#endif - -GfxInfo::GfxInfo() -{ -} - -GfxInfo::~GfxInfo() -{ -} - -nsresult -GfxInfo::GetD2DEnabled(bool *aEnabled) -{ - return NS_ERROR_FAILURE; -} - -nsresult -GfxInfo::GetDWriteEnabled(bool *aEnabled) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetDWriteVersion(nsAString & aDwriteVersion) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetCleartypeParameters(nsAString & aCleartypeParams) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterDescription(nsAString & aAdapterDescription) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterDescription2(nsAString & aAdapterDescription) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterRAM(nsAString & aAdapterRAM) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterRAM2(nsAString & aAdapterRAM) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterDriver(nsAString & aAdapterDriver) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterDriver2(nsAString & aAdapterDriver) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterDriverVersion(nsAString & aAdapterDriverVersion) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterDriverVersion2(nsAString & aAdapterDriverVersion) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterDriverDate(nsAString & aAdapterDriverDate) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterDriverDate2(nsAString & aAdapterDriverDate) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterVendorID(nsAString & aAdapterVendorID) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterVendorID2(nsAString & aAdapterVendorID) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterDeviceID(nsAString & aAdapterDeviceID) -{ - return NS_ERROR_FAILURE; - return NS_OK; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterDeviceID2(nsAString & aAdapterDeviceID) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterSubsysID(nsAString & aAdapterSubsysID) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetAdapterSubsysID2(nsAString & aAdapterSubsysID) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -GfxInfo::GetIsGPU2Active(bool* aIsGPU2Active) -{ - return NS_ERROR_FAILURE; -} - -const nsTArray<GfxDriverInfo>& -GfxInfo::GetGfxDriverInfo() -{ - if (mDriverInfo->IsEmpty()) { - APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Ios, - (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorAll), GfxDriverInfo::allDevices, - nsIGfxInfo::FEATURE_OPENGL_LAYERS, nsIGfxInfo::FEATURE_STATUS_OK, - DRIVER_COMPARISON_IGNORED, GfxDriverInfo::allDriverVersions ); - } - - return *mDriverInfo; -} - -nsresult -GfxInfo::GetFeatureStatusImpl(int32_t aFeature, - int32_t *aStatus, - nsAString & aSuggestedDriverVersion, - const nsTArray<GfxDriverInfo>& aDriverInfo, - OperatingSystem* aOS /* = nullptr */) -{ - NS_ENSURE_ARG_POINTER(aStatus); - aSuggestedDriverVersion.SetIsVoid(true); - *aStatus = nsIGfxInfo::FEATURE_STATUS_UNKNOWN; - if (aOS) - *aOS = OperatingSystem::Ios; - - if (mShutdownOccurred) { - return NS_OK; - } - - // OpenGL layers are never blacklisted on iOS. - // This early return is so we avoid potentially slow - // GLStrings initialization on startup when we initialize GL layers. - if (aFeature == nsIGfxInfo::FEATURE_OPENGL_LAYERS || - aFeature == nsIGfxInfo::FEATURE_WEBGL_OPENGL || - aFeature == nsIGfxInfo::FEATURE_WEBGL_MSAA) { - *aStatus = nsIGfxInfo::FEATURE_STATUS_OK; - return NS_OK; - } - - return GfxInfoBase::GetFeatureStatusImpl(aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aOS); -} - -#ifdef DEBUG - -// Implement nsIGfxInfoDebug - -NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString & aVendorID) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString & aDeviceID) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString & aDriverVersion) -{ - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP GfxInfo::SpoofOSVersion(uint32_t aVersion) -{ - return NS_ERROR_FAILURE; -} - -#endif - -} -} diff --git a/widget/uikit/GfxInfo.h b/widget/uikit/GfxInfo.h deleted file mode 100644 index 16a2242515..0000000000 --- a/widget/uikit/GfxInfo.h +++ /dev/null @@ -1,78 +0,0 @@ -/* vim: se cin sw=2 ts=2 et : */ -/* -*- 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_GfxInfo_h__ -#define __mozilla_widget_GfxInfo_h__ - -#include "GfxInfoBase.h" -#include "GfxDriverInfo.h" - -#include "nsString.h" -#include "mozilla/UniquePtr.h" - -namespace mozilla { - -namespace gl { -class GLContext; -} - -namespace widget { - -class GfxInfo : public GfxInfoBase -{ -private: - ~GfxInfo(); - -public: - GfxInfo(); - - // We only declare the subset of nsIGfxInfo that we actually implement. The - // rest is brought forward from GfxInfoBase. - NS_IMETHOD GetD2DEnabled(bool *aD2DEnabled); - NS_IMETHOD GetDWriteEnabled(bool *aDWriteEnabled); - NS_IMETHOD GetDWriteVersion(nsAString & aDwriteVersion); - NS_IMETHOD GetCleartypeParameters(nsAString & aCleartypeParams); - NS_IMETHOD GetAdapterDescription(nsAString & aAdapterDescription); - NS_IMETHOD GetAdapterDriver(nsAString & aAdapterDriver); - NS_IMETHOD GetAdapterVendorID(nsAString & aAdapterVendorID); - NS_IMETHOD GetAdapterDeviceID(nsAString & aAdapterDeviceID); - NS_IMETHOD GetAdapterSubsysID(nsAString & aAdapterSubsysID); - NS_IMETHOD GetAdapterRAM(nsAString & aAdapterRAM); - NS_IMETHOD GetAdapterDriverVersion(nsAString & aAdapterDriverVersion); - NS_IMETHOD GetAdapterDriverDate(nsAString & aAdapterDriverDate); - NS_IMETHOD GetAdapterDescription2(nsAString & aAdapterDescription); - NS_IMETHOD GetAdapterDriver2(nsAString & aAdapterDriver); - NS_IMETHOD GetAdapterVendorID2(nsAString & aAdapterVendorID); - NS_IMETHOD GetAdapterDeviceID2(nsAString & aAdapterDeviceID); - NS_IMETHOD GetAdapterSubsysID2(nsAString & aAdapterSubsysID); - NS_IMETHOD GetAdapterRAM2(nsAString & aAdapterRAM); - NS_IMETHOD GetAdapterDriverVersion2(nsAString & aAdapterDriverVersion); - NS_IMETHOD GetAdapterDriverDate2(nsAString & aAdapterDriverDate); - NS_IMETHOD GetIsGPU2Active(bool *aIsGPU2Active); - using GfxInfoBase::GetFeatureStatus; - using GfxInfoBase::GetFeatureSuggestedDriverVersion; - using GfxInfoBase::GetWebGLParameter; - -#ifdef DEBUG - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIGFXINFODEBUG -#endif - -protected: - - virtual nsresult GetFeatureStatusImpl(int32_t aFeature, - int32_t *aStatus, - nsAString & aSuggestedDriverVersion, - const nsTArray<GfxDriverInfo>& aDriverInfo, - OperatingSystem* aOS = nullptr); - virtual const nsTArray<GfxDriverInfo>& GetGfxDriverInfo(); -}; - -} // namespace widget -} // namespace mozilla - -#endif /* __mozilla_widget_GfxInfo_h__ */ diff --git a/widget/uikit/moz.build b/widget/uikit/moz.build deleted file mode 100644 index 2c6c188de6..0000000000 --- a/widget/uikit/moz.build +++ /dev/null @@ -1,18 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# 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/. - -SOURCES += [ - 'GfxInfo.cpp', - 'nsAppShell.mm', - 'nsLookAndFeel.mm', - 'nsScreenManager.mm', - 'nsWidgetFactory.mm', - 'nsWindow.mm', -] - -FINAL_LIBRARY = 'xul' -LOCAL_INCLUDES += [ - '/widget', -] diff --git a/widget/uikit/nsAppShell.h b/widget/uikit/nsAppShell.h deleted file mode 100644 index a88fa8b4f1..0000000000 --- a/widget/uikit/nsAppShell.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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/. */ - -/* - * Runs the main native UIKit run loop, interrupting it as needed to process - * Gecko events. - */ - -#ifndef nsAppShell_h_ -#define nsAppShell_h_ - -#include "nsBaseAppShell.h" -#include "nsTArray.h" - -#include <Foundation/NSAutoreleasePool.h> -#include <CoreFoundation/CFRunLoop.h> -#include <UIKit/UIWindow.h> - -@class AppShellDelegate; - -class nsAppShell : public nsBaseAppShell -{ -public: - NS_IMETHOD ResumeNative(void); - - nsAppShell(); - - nsresult Init(); - - NS_IMETHOD Run(void); - NS_IMETHOD Exit(void); - // Called by the application delegate - void WillTerminate(void); - - static nsAppShell* gAppShell; - static UIWindow* gWindow; - static NSMutableArray* gTopLevelViews; - -protected: - virtual ~nsAppShell(); - - static void ProcessGeckoEvents(void* aInfo); - virtual void ScheduleNativeEventCallback(); - virtual bool ProcessNextNativeEvent(bool aMayWait); - - NSAutoreleasePool* mAutoreleasePool; - AppShellDelegate* mDelegate; - CFRunLoopRef mCFRunLoop; - CFRunLoopSourceRef mCFRunLoopSource; - - bool mTerminated; - bool mNotifiedWillTerminate; -}; - -#endif // nsAppShell_h_ diff --git a/widget/uikit/nsAppShell.mm b/widget/uikit/nsAppShell.mm deleted file mode 100644 index ac007132fd..0000000000 --- a/widget/uikit/nsAppShell.mm +++ /dev/null @@ -1,271 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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/. */ - -#import <UIKit/UIApplication.h> -#import <UIKit/UIScreen.h> -#import <UIKit/UIWindow.h> -#import <UIKit/UIViewController.h> - -#include "nsAppShell.h" -#include "nsCOMPtr.h" -#include "nsIFile.h" -#include "nsDirectoryServiceDefs.h" -#include "nsString.h" -#include "nsIRollupListener.h" -#include "nsIWidget.h" -#include "nsThreadUtils.h" -#include "nsIWindowMediator.h" -#include "nsMemoryPressure.h" -#include "nsServiceManagerUtils.h" -#include "nsIInterfaceRequestor.h" -#include "nsIWebBrowserChrome.h" - -nsAppShell *nsAppShell::gAppShell = NULL; -UIWindow *nsAppShell::gWindow = nil; -NSMutableArray *nsAppShell::gTopLevelViews = [[NSMutableArray alloc] init]; - -#define ALOG(args...) fprintf(stderr, args); fprintf(stderr, "\n") - -// ViewController -@interface ViewController : UIViewController -@end - - -@implementation ViewController - -- (void)loadView { - ALOG("[ViewController loadView]"); - CGRect r = {{0, 0}, {100, 100}}; - self.view = [[UIView alloc] initWithFrame:r]; - [self.view setBackgroundColor:[UIColor lightGrayColor]]; - // add all of the top level views as children - for (UIView* v in nsAppShell::gTopLevelViews) { - ALOG("[ViewController.view addSubView:%p]", v); - [self.view addSubview:v]; - } - [nsAppShell::gTopLevelViews release]; - nsAppShell::gTopLevelViews = nil; -} -@end - -// AppShellDelegate -// -// Acts as a delegate for the UIApplication - -@interface AppShellDelegate : NSObject <UIApplicationDelegate> { -} -@property (strong, nonatomic) UIWindow *window; -@end - -@implementation AppShellDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - ALOG("[AppShellDelegate application:didFinishLaunchingWithOptions:]"); - // We only create one window, since we can only display one window at - // a time anyway. Also, iOS 4 fails to display UIWindows if you - // create them before calling UIApplicationMain, so this makes more sense. - nsAppShell::gWindow = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] retain]; - self.window = nsAppShell::gWindow; - - self.window.rootViewController = [[ViewController alloc] init]; - - // just to make things more visible for now - nsAppShell::gWindow.backgroundColor = [UIColor blueColor]; - [nsAppShell::gWindow makeKeyAndVisible]; - - return YES; -} - -- (void)applicationWillTerminate:(UIApplication *)application -{ - ALOG("[AppShellDelegate applicationWillTerminate:]"); - nsAppShell::gAppShell->WillTerminate(); -} - -- (void)applicationDidBecomeActive:(UIApplication *)application -{ - ALOG("[AppShellDelegate applicationDidBecomeActive:]"); -} - -- (void)applicationWillResignActive:(UIApplication *)application -{ - ALOG("[AppShellDelegate applicationWillResignActive:]"); -} - -- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application -{ - ALOG("[AppShellDelegate applicationDidReceiveMemoryWarning:]"); - NS_DispatchMemoryPressure(MemPressure_New); -} -@end - -// nsAppShell implementation - -NS_IMETHODIMP -nsAppShell::ResumeNative(void) -{ - return nsBaseAppShell::ResumeNative(); -} - -nsAppShell::nsAppShell() - : mAutoreleasePool(NULL), - mDelegate(NULL), - mCFRunLoop(NULL), - mCFRunLoopSource(NULL), - mTerminated(false), - mNotifiedWillTerminate(false) -{ - gAppShell = this; -} - -nsAppShell::~nsAppShell() -{ - if (mAutoreleasePool) { - [mAutoreleasePool release]; - mAutoreleasePool = NULL; - } - - if (mCFRunLoop) { - if (mCFRunLoopSource) { - ::CFRunLoopRemoveSource(mCFRunLoop, mCFRunLoopSource, - kCFRunLoopCommonModes); - ::CFRelease(mCFRunLoopSource); - } - ::CFRelease(mCFRunLoop); - } - - gAppShell = NULL; -} - -// Init -// -// public -nsresult -nsAppShell::Init() -{ - mAutoreleasePool = [[NSAutoreleasePool alloc] init]; - - // Add a CFRunLoopSource to the main native run loop. The source is - // responsible for interrupting the run loop when Gecko events are ready. - - mCFRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop]; - NS_ENSURE_STATE(mCFRunLoop); - ::CFRetain(mCFRunLoop); - - CFRunLoopSourceContext context; - bzero(&context, sizeof(context)); - // context.version = 0; - context.info = this; - context.perform = ProcessGeckoEvents; - - mCFRunLoopSource = ::CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); - NS_ENSURE_STATE(mCFRunLoopSource); - - ::CFRunLoopAddSource(mCFRunLoop, mCFRunLoopSource, kCFRunLoopCommonModes); - - return nsBaseAppShell::Init(); -} - -// ProcessGeckoEvents -// -// The "perform" target of mCFRunLoop, called when mCFRunLoopSource is -// signalled from ScheduleNativeEventCallback. -// -// protected static -void -nsAppShell::ProcessGeckoEvents(void* aInfo) -{ - nsAppShell* self = static_cast<nsAppShell*> (aInfo); - self->NativeEventCallback(); - self->Release(); -} - -// WillTerminate -// -// public -void -nsAppShell::WillTerminate() -{ - mNotifiedWillTerminate = true; - if (mTerminated) - return; - mTerminated = true; - // We won't get another chance to process events - NS_ProcessPendingEvents(NS_GetCurrentThread()); - - // Unless we call nsBaseAppShell::Exit() here, it might not get called - // at all. - nsBaseAppShell::Exit(); -} - -// ScheduleNativeEventCallback -// -// protected virtual -void -nsAppShell::ScheduleNativeEventCallback() -{ - if (mTerminated) - return; - - NS_ADDREF_THIS(); - - // This will invoke ProcessGeckoEvents on the main thread. - ::CFRunLoopSourceSignal(mCFRunLoopSource); - ::CFRunLoopWakeUp(mCFRunLoop); -} - -// ProcessNextNativeEvent -// -// protected virtual -bool -nsAppShell::ProcessNextNativeEvent(bool aMayWait) -{ - if (mTerminated) - return false; - - NSString* currentMode = nil; - NSDate* waitUntil = nil; - if (aMayWait) - waitUntil = [NSDate distantFuture]; - NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop]; - - BOOL eventProcessed = NO; - do { - currentMode = [currentRunLoop currentMode]; - if (!currentMode) - currentMode = NSDefaultRunLoopMode; - - if (aMayWait) - eventProcessed = [currentRunLoop runMode:currentMode beforeDate:waitUntil]; - else - [currentRunLoop acceptInputForMode:currentMode beforeDate:waitUntil]; - } while(eventProcessed && aMayWait); - - return false; -} - -// Run -// -// public -NS_IMETHODIMP -nsAppShell::Run(void) -{ - ALOG("nsAppShell::Run"); - char argv[1][4] = {"app"}; - UIApplicationMain(1, (char**)argv, nil, @"AppShellDelegate"); - // UIApplicationMain doesn't exit. :-( - return NS_OK; -} - -NS_IMETHODIMP -nsAppShell::Exit(void) -{ - if (mTerminated) - return NS_OK; - - mTerminated = true; - return nsBaseAppShell::Exit(); -} diff --git a/widget/uikit/nsLookAndFeel.h b/widget/uikit/nsLookAndFeel.h deleted file mode 100644 index 91c0c2d73e..0000000000 --- a/widget/uikit/nsLookAndFeel.h +++ /dev/null @@ -1,35 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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 __nsLookAndFeel -#define __nsLookAndFeel - -#include "nsXPLookAndFeel.h" - -class nsLookAndFeel: public nsXPLookAndFeel -{ -public: - nsLookAndFeel(); - virtual ~nsLookAndFeel(); - - virtual nsresult NativeGetColor(const ColorID aID, nscolor &aResult); - virtual nsresult GetIntImpl(IntID aID, int32_t &aResult); - virtual nsresult GetFloatImpl(FloatID aID, float &aResult); - virtual bool GetFontImpl(FontID aID, nsString& aFontName, - gfxFontStyle& aFontStyle, - float aDevPixPerCSSPixel); - virtual char16_t GetPasswordCharacterImpl() - { - // unicode value for the bullet character, used for password textfields. - return 0x2022; - } - - static bool UseOverlayScrollbars() - { - return true; - } -}; - -#endif diff --git a/widget/uikit/nsLookAndFeel.mm b/widget/uikit/nsLookAndFeel.mm deleted file mode 100644 index bb593eb51e..0000000000 --- a/widget/uikit/nsLookAndFeel.mm +++ /dev/null @@ -1,401 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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/. */ - -#import <UIKit/UIColor.h> -#import <UIKit/UIInterface.h> - -#include "nsLookAndFeel.h" -#include "nsStyleConsts.h" -#include "gfxFont.h" -#include "gfxFontConstants.h" - -nsLookAndFeel::nsLookAndFeel() - : nsXPLookAndFeel() -{ -} - -nsLookAndFeel::~nsLookAndFeel() -{ -} - -static nscolor GetColorFromUIColor(UIColor* aColor) -{ - CGColorRef cgColor = [aColor CGColor]; - CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(cgColor)); - const CGFloat* components = CGColorGetComponents(cgColor); - if (model == kCGColorSpaceModelRGB) { - return NS_RGB((unsigned int)(components[0] * 255.0), - (unsigned int)(components[1] * 255.0), - (unsigned int)(components[2] * 255.0)); - } - else if (model == kCGColorSpaceModelMonochrome) { - unsigned int val = (unsigned int)(components[0] * 255.0); - return NS_RGBA(val, val, val, - (unsigned int)(components[1] * 255.0)); - } - NS_NOTREACHED("Unhandled color space!"); - return 0; -} - -nsresult -nsLookAndFeel::NativeGetColor(const ColorID aID, nscolor &aResult) -{ - nsresult res = NS_OK; - - switch (aID) { - case eColorID_WindowBackground: - aResult = NS_RGB(0xff,0xff,0xff); - break; - case eColorID_WindowForeground: - aResult = NS_RGB(0x00,0x00,0x00); - break; - case eColorID_WidgetBackground: - aResult = NS_RGB(0xdd,0xdd,0xdd); - break; - case eColorID_WidgetForeground: - aResult = NS_RGB(0x00,0x00,0x00); - break; - case eColorID_WidgetSelectBackground: - aResult = NS_RGB(0x80,0x80,0x80); - break; - case eColorID_WidgetSelectForeground: - aResult = NS_RGB(0x00,0x00,0x80); - break; - case eColorID_Widget3DHighlight: - aResult = NS_RGB(0xa0,0xa0,0xa0); - break; - case eColorID_Widget3DShadow: - aResult = NS_RGB(0x40,0x40,0x40); - break; - case eColorID_TextBackground: - aResult = NS_RGB(0xff,0xff,0xff); - break; - case eColorID_TextForeground: - aResult = NS_RGB(0x00,0x00,0x00); - break; - case eColorID_TextSelectBackground: - case eColorID_highlight: // CSS2 color - aResult = NS_RGB(0xaa,0xaa,0xaa); - break; - case eColorID__moz_menuhover: - aResult = NS_RGB(0xee,0xee,0xee); - break; - case eColorID_TextSelectForeground: - case eColorID_highlighttext: // CSS2 color - case eColorID__moz_menuhovertext: - GetColor(eColorID_TextSelectBackground, aResult); - if (aResult == 0x000000) - aResult = NS_RGB(0xff,0xff,0xff); - else - aResult = NS_DONT_CHANGE_COLOR; - break; - case eColorID_IMESelectedRawTextBackground: - case eColorID_IMESelectedConvertedTextBackground: - case eColorID_IMERawInputBackground: - case eColorID_IMEConvertedTextBackground: - aResult = NS_TRANSPARENT; - break; - case eColorID_IMESelectedRawTextForeground: - case eColorID_IMESelectedConvertedTextForeground: - case eColorID_IMERawInputForeground: - case eColorID_IMEConvertedTextForeground: - aResult = NS_SAME_AS_FOREGROUND_COLOR; - break; - case eColorID_IMERawInputUnderline: - case eColorID_IMEConvertedTextUnderline: - aResult = NS_40PERCENT_FOREGROUND_COLOR; - break; - case eColorID_IMESelectedRawTextUnderline: - case eColorID_IMESelectedConvertedTextUnderline: - aResult = NS_SAME_AS_FOREGROUND_COLOR; - break; - case eColorID_SpellCheckerUnderline: - aResult = NS_RGB(0xff, 0, 0); - break; - - // - // css2 system colors http://www.w3.org/TR/REC-CSS2/ui.html#system-colors - // - case eColorID_buttontext: - case eColorID__moz_buttonhovertext: - case eColorID_captiontext: - case eColorID_menutext: - case eColorID_infotext: - case eColorID__moz_menubartext: - case eColorID_windowtext: - aResult = GetColorFromUIColor([UIColor darkTextColor]); - break; - case eColorID_activecaption: - aResult = NS_RGB(0xff,0xff,0xff); - break; - case eColorID_activeborder: - aResult = NS_RGB(0x00,0x00,0x00); - break; - case eColorID_appworkspace: - aResult = NS_RGB(0xFF,0xFF,0xFF); - break; - case eColorID_background: - aResult = NS_RGB(0x63,0x63,0xCE); - break; - case eColorID_buttonface: - case eColorID__moz_buttonhoverface: - aResult = NS_RGB(0xF0,0xF0,0xF0); - break; - case eColorID_buttonhighlight: - aResult = NS_RGB(0xFF,0xFF,0xFF); - break; - case eColorID_buttonshadow: - aResult = NS_RGB(0xDC,0xDC,0xDC); - break; - case eColorID_graytext: - aResult = NS_RGB(0x44,0x44,0x44); - break; - case eColorID_inactiveborder: - aResult = NS_RGB(0xff,0xff,0xff); - break; - case eColorID_inactivecaption: - aResult = NS_RGB(0xaa,0xaa,0xaa); - break; - case eColorID_inactivecaptiontext: - aResult = NS_RGB(0x45,0x45,0x45); - break; - case eColorID_scrollbar: - aResult = NS_RGB(0,0,0); //XXX - break; - case eColorID_threeddarkshadow: - aResult = NS_RGB(0xDC,0xDC,0xDC); - break; - case eColorID_threedshadow: - aResult = NS_RGB(0xE0,0xE0,0xE0); - break; - case eColorID_threedface: - aResult = NS_RGB(0xF0,0xF0,0xF0); - break; - case eColorID_threedhighlight: - aResult = NS_RGB(0xff,0xff,0xff); - break; - case eColorID_threedlightshadow: - aResult = NS_RGB(0xDA,0xDA,0xDA); - break; - case eColorID_menu: - aResult = NS_RGB(0xff,0xff,0xff); - break; - case eColorID_infobackground: - aResult = NS_RGB(0xFF,0xFF,0xC7); - break; - case eColorID_windowframe: - aResult = NS_RGB(0xaa,0xaa,0xaa); - break; - case eColorID_window: - case eColorID__moz_field: - case eColorID__moz_combobox: - aResult = NS_RGB(0xff,0xff,0xff); - break; - case eColorID__moz_fieldtext: - case eColorID__moz_comboboxtext: - aResult = GetColorFromUIColor([UIColor darkTextColor]); - break; - case eColorID__moz_dialog: - aResult = NS_RGB(0xaa,0xaa,0xaa); - break; - case eColorID__moz_dialogtext: - case eColorID__moz_cellhighlighttext: - case eColorID__moz_html_cellhighlighttext: - aResult = GetColorFromUIColor([UIColor darkTextColor]); - break; - case eColorID__moz_dragtargetzone: - case eColorID__moz_mac_chrome_active: - case eColorID__moz_mac_chrome_inactive: - aResult = NS_RGB(0xaa,0xaa,0xaa); - break; - case eColorID__moz_mac_focusring: - aResult = NS_RGB(0x3F,0x98,0xDD); - break; - case eColorID__moz_mac_menushadow: - aResult = NS_RGB(0xA3,0xA3,0xA3); - break; - case eColorID__moz_mac_menutextdisable: - aResult = NS_RGB(0x88,0x88,0x88); - break; - case eColorID__moz_mac_menutextselect: - aResult = NS_RGB(0xaa,0xaa,0xaa); - break; - case eColorID__moz_mac_disabledtoolbartext: - aResult = NS_RGB(0x3F,0x3F,0x3F); - break; - case eColorID__moz_mac_menuselect: - aResult = NS_RGB(0xaa,0xaa,0xaa); - break; - case eColorID__moz_buttondefault: - aResult = NS_RGB(0xDC,0xDC,0xDC); - break; - case eColorID__moz_cellhighlight: - case eColorID__moz_html_cellhighlight: - case eColorID__moz_mac_secondaryhighlight: - // For inactive list selection - aResult = NS_RGB(0xaa,0xaa,0xaa); - break; - case eColorID__moz_eventreerow: - // Background color of even list rows. - aResult = NS_RGB(0xff,0xff,0xff); - break; - case eColorID__moz_oddtreerow: - // Background color of odd list rows. - aResult = NS_TRANSPARENT; - break; - case eColorID__moz_nativehyperlinktext: - // There appears to be no available system defined color. HARDCODING to the appropriate color. - aResult = NS_RGB(0x14,0x4F,0xAE); - break; - default: - NS_WARNING("Someone asked nsILookAndFeel for a color I don't know about"); - aResult = NS_RGB(0xff,0xff,0xff); - res = NS_ERROR_FAILURE; - break; - } - - return res; -} - -NS_IMETHODIMP -nsLookAndFeel::GetIntImpl(IntID aID, int32_t &aResult) -{ - nsresult res = nsXPLookAndFeel::GetIntImpl(aID, aResult); - if (NS_SUCCEEDED(res)) - return res; - res = NS_OK; - - switch (aID) { - case eIntID_CaretBlinkTime: - aResult = 567; - break; - case eIntID_CaretWidth: - aResult = 1; - break; - case eIntID_ShowCaretDuringSelection: - aResult = 0; - break; - case eIntID_SelectTextfieldsOnKeyFocus: - // Select textfield content when focused by kbd - // used by nsEventStateManager::sTextfieldSelectModel - aResult = 1; - break; - case eIntID_SubmenuDelay: - aResult = 200; - break; - case eIntID_MenusCanOverlapOSBar: - // xul popups are not allowed to overlap the menubar. - aResult = 0; - break; - case eIntID_SkipNavigatingDisabledMenuItem: - aResult = 1; - break; - case eIntID_DragThresholdX: - case eIntID_DragThresholdY: - aResult = 4; - break; - case eIntID_ScrollArrowStyle: - aResult = eScrollArrow_None; - break; - case eIntID_ScrollSliderStyle: - aResult = eScrollThumbStyle_Proportional; - break; - case eIntID_TreeOpenDelay: - aResult = 1000; - break; - case eIntID_TreeCloseDelay: - aResult = 1000; - break; - case eIntID_TreeLazyScrollDelay: - aResult = 150; - break; - case eIntID_TreeScrollDelay: - aResult = 100; - break; - case eIntID_TreeScrollLinesMax: - aResult = 3; - break; - case eIntID_DWMCompositor: - case eIntID_WindowsClassic: - case eIntID_WindowsDefaultTheme: - case eIntID_TouchEnabled: - aResult = 0; - res = NS_ERROR_NOT_IMPLEMENTED; - break; - case eIntID_MacGraphiteTheme: - aResult = 0; - break; - case eIntID_TabFocusModel: - aResult = 1; // default to just textboxes - break; - case eIntID_ScrollToClick: - aResult = 0; - break; - case eIntID_ChosenMenuItemsShouldBlink: - aResult = 1; - break; - case eIntID_IMERawInputUnderlineStyle: - case eIntID_IMEConvertedTextUnderlineStyle: - case eIntID_IMESelectedRawTextUnderlineStyle: - case eIntID_IMESelectedConvertedTextUnderline: - aResult = NS_STYLE_TEXT_DECORATION_STYLE_SOLID; - break; - case eIntID_SpellCheckerUnderlineStyle: - aResult = NS_STYLE_TEXT_DECORATION_STYLE_DOTTED; - break; - case eIntID_ContextMenuOffsetVertical: - case eIntID_ContextMenuOffsetHorizontal: - aResult = 2; - break; - default: - aResult = 0; - res = NS_ERROR_FAILURE; - } - return res; -} - -NS_IMETHODIMP -nsLookAndFeel::GetFloatImpl(FloatID aID, float &aResult) -{ - nsresult res = nsXPLookAndFeel::GetFloatImpl(aID, aResult); - if (NS_SUCCEEDED(res)) - return res; - res = NS_OK; - - switch (aID) { - case eFloatID_IMEUnderlineRelativeSize: - aResult = 2.0f; - break; - case eFloatID_SpellCheckerUnderlineRelativeSize: - aResult = 2.0f; - break; - default: - aResult = -1.0; - res = NS_ERROR_FAILURE; - } - - return res; -} - -bool -nsLookAndFeel::GetFontImpl(FontID aID, nsString &aFontName, - gfxFontStyle &aFontStyle, - float aDevPixPerCSSPixel) -{ - // hack for now - if (aID == eFont_Window || aID == eFont_Document) { - aFontStyle.style = NS_FONT_STYLE_NORMAL; - aFontStyle.weight = NS_FONT_WEIGHT_NORMAL; - aFontStyle.stretch = NS_FONT_STRETCH_NORMAL; - aFontStyle.size = 14 * aDevPixPerCSSPixel; - aFontStyle.systemFont = true; - - aFontName.AssignLiteral("sans-serif"); - return true; - } - - //TODO: implement more here? - return false; -} diff --git a/widget/uikit/nsScreenManager.h b/widget/uikit/nsScreenManager.h deleted file mode 100644 index 1ff6a87ec7..0000000000 --- a/widget/uikit/nsScreenManager.h +++ /dev/null @@ -1,60 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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 nsScreenManager_h_ -#define nsScreenManager_h_ - -#include "nsBaseScreen.h" -#include "nsIScreenManager.h" -#include "nsCOMPtr.h" -#include "nsRect.h" - -@class UIScreen; - -class UIKitScreen : public nsBaseScreen -{ -public: - explicit UIKitScreen (UIScreen* screen); - ~UIKitScreen () {} - - NS_IMETHOD GetId(uint32_t* outId) { - *outId = 0; - return NS_OK; - } - - NS_IMETHOD GetRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); - NS_IMETHOD GetAvailRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); - NS_IMETHOD GetRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); - NS_IMETHOD GetAvailRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); - NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth); - NS_IMETHOD GetColorDepth(int32_t* aColorDepth); - NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor); - NS_IMETHOD GetDefaultCSSScaleFactor(double* aScaleFactor) - { - return GetContentsScaleFactor(aScaleFactor); - } - -private: - UIScreen* mScreen; -}; - -class UIKitScreenManager : public nsIScreenManager -{ -public: - UIKitScreenManager (); - - NS_DECL_ISUPPORTS - - NS_DECL_NSISCREENMANAGER - - static LayoutDeviceIntRect GetBounds(); - -private: - virtual ~UIKitScreenManager () {} - //TODO: support >1 screen, iPad supports external displays - nsCOMPtr<nsIScreen> mScreen; -}; - -#endif // nsScreenManager_h_ diff --git a/widget/uikit/nsScreenManager.mm b/widget/uikit/nsScreenManager.mm deleted file mode 100644 index 601c911cd9..0000000000 --- a/widget/uikit/nsScreenManager.mm +++ /dev/null @@ -1,146 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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/. */ - -#import <UIKit/UIScreen.h> - -#include "gfxPoint.h" -#include "nsScreenManager.h" -#include "nsAppShell.h" - -static LayoutDeviceIntRect gScreenBounds; -static bool gScreenBoundsSet = false; - -UIKitScreen::UIKitScreen(UIScreen* aScreen) -{ - mScreen = [aScreen retain]; -} - -NS_IMETHODIMP -UIKitScreen::GetRect(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight) -{ - return GetRectDisplayPix(outX, outY, outWidth, outHeight); -} - -NS_IMETHODIMP -UIKitScreen::GetAvailRect(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight) -{ - return GetAvailRectDisplayPix(outX, outY, outWidth, outHeight); -} - -NS_IMETHODIMP -UIKitScreen::GetRectDisplayPix(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight) -{ - nsIntRect rect = UIKitScreenManager::GetBounds(); - *outX = rect.x; - *outY = rect.y; - *outWidth = rect.width; - *outHeight = rect.height; - - return NS_OK; -} - -NS_IMETHODIMP -UIKitScreen::GetAvailRectDisplayPix(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight) -{ - CGRect rect = [mScreen applicationFrame]; - CGFloat scale = [mScreen scale]; - - *outX = rect.origin.x * scale; - *outY = rect.origin.y * scale; - *outWidth = rect.size.width * scale; - *outHeight = rect.size.height * scale; - - return NS_OK; -} - -NS_IMETHODIMP -UIKitScreen::GetPixelDepth(int32_t *aPixelDepth) -{ - // Close enough. - *aPixelDepth = 24; - return NS_OK; -} - -NS_IMETHODIMP -UIKitScreen::GetColorDepth(int32_t *aColorDepth) -{ - return GetPixelDepth(aColorDepth); -} - -NS_IMETHODIMP -UIKitScreen::GetContentsScaleFactor(double* aContentsScaleFactor) -{ - *aContentsScaleFactor = [mScreen scale]; - return NS_OK; -} - -NS_IMPL_ISUPPORTS(UIKitScreenManager, nsIScreenManager) - -UIKitScreenManager::UIKitScreenManager() -: mScreen(new UIKitScreen([UIScreen mainScreen])) -{ -} - -LayoutDeviceIntRect -UIKitScreenManager::GetBounds() -{ - if (!gScreenBoundsSet) { - CGRect rect = [[UIScreen mainScreen] bounds]; - CGFloat scale = [[UIScreen mainScreen] scale]; - gScreenBounds.x = rect.origin.x * scale; - gScreenBounds.y = rect.origin.y * scale; - gScreenBounds.width = rect.size.width * scale; - gScreenBounds.height = rect.size.height * scale; - gScreenBoundsSet = true; - } - printf("UIKitScreenManager::GetBounds: %d %d %d %d\n", - gScreenBounds.x, gScreenBounds.y, gScreenBounds.width, gScreenBounds.height); - return gScreenBounds; -} - -NS_IMETHODIMP -UIKitScreenManager::GetPrimaryScreen(nsIScreen** outScreen) -{ - NS_IF_ADDREF(*outScreen = mScreen.get()); - return NS_OK; -} - -NS_IMETHODIMP -UIKitScreenManager::ScreenForRect(int32_t inLeft, - int32_t inTop, - int32_t inWidth, - int32_t inHeight, - nsIScreen** outScreen) -{ - return GetPrimaryScreen(outScreen); -} - -NS_IMETHODIMP -UIKitScreenManager::ScreenForId(uint32_t id, - nsIScreen** outScreen) -{ - return GetPrimaryScreen(outScreen); -} - -NS_IMETHODIMP -UIKitScreenManager::ScreenForNativeWidget(void* aWidget, nsIScreen** outScreen) -{ - return GetPrimaryScreen(outScreen); -} - -NS_IMETHODIMP -UIKitScreenManager::GetNumberOfScreens(uint32_t* aNumberOfScreens) -{ - //TODO: support multiple screens - *aNumberOfScreens = 1; - return NS_OK; -} - -NS_IMETHODIMP -UIKitScreenManager::GetSystemDefaultScale(float* aScale) -{ - *aScale = [UIScreen mainScreen].scale; - return NS_OK; -} diff --git a/widget/uikit/nsWidgetFactory.mm b/widget/uikit/nsWidgetFactory.mm deleted file mode 100644 index 9e4f028ff6..0000000000 --- a/widget/uikit/nsWidgetFactory.mm +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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/. */ - -#include "nsIFactory.h" -#include "nsISupports.h" -#include "nsIComponentManager.h" -#include "mozilla/ModuleUtils.h" - -#include "nsWidgetsCID.h" - -#include "nsAppShell.h" -#include "nsAppShellSingleton.h" -#include "nsLookAndFeel.h" -#include "nsScreenManager.h" -#include "nsWindow.h" - -NS_GENERIC_FACTORY_CONSTRUCTOR(UIKitScreenManager) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow) - -#include "GfxInfo.h" -namespace mozilla { -namespace widget { -// This constructor should really be shared with all platforms. -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxInfo, Init) -} -} - -NS_DEFINE_NAMED_CID(NS_WINDOW_CID); -NS_DEFINE_NAMED_CID(NS_CHILD_CID); -NS_DEFINE_NAMED_CID(NS_APPSHELL_CID); -NS_DEFINE_NAMED_CID(NS_SCREENMANAGER_CID); -NS_DEFINE_NAMED_CID(NS_GFXINFO_CID); - -static const mozilla::Module::CIDEntry kWidgetCIDs[] = { - { &kNS_WINDOW_CID, false, nullptr, nsWindowConstructor }, - { &kNS_CHILD_CID, false, nullptr, nsWindowConstructor }, - { &kNS_APPSHELL_CID, false, nullptr, nsAppShellConstructor }, - { &kNS_SCREENMANAGER_CID, false, nullptr, UIKitScreenManagerConstructor }, - { &kNS_GFXINFO_CID, false, nullptr, mozilla::widget::GfxInfoConstructor }, - { nullptr } -}; - -static const mozilla::Module::ContractIDEntry kWidgetContracts[] = { - { "@mozilla.org/widgets/window/uikit;1", &kNS_WINDOW_CID }, - { "@mozilla.org/widgets/childwindow/uikit;1", &kNS_CHILD_CID }, - { "@mozilla.org/widget/appshell/uikit;1", &kNS_APPSHELL_CID }, - { "@mozilla.org/gfx/screenmanager;1", &kNS_SCREENMANAGER_CID }, - { "@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID }, - { nullptr } -}; - -static void -nsWidgetUIKitModuleDtor() -{ - nsLookAndFeel::Shutdown(); - nsAppShellShutdown(); -} - -static const mozilla::Module kWidgetModule = { - mozilla::Module::kVersion, - kWidgetCIDs, - kWidgetContracts, - nullptr, - nullptr, - nsAppShellInit, - nsWidgetUIKitModuleDtor -}; - -NSMODULE_DEFN(nsWidgetUIKitModule) = &kWidgetModule; diff --git a/widget/uikit/nsWindow.h b/widget/uikit/nsWindow.h deleted file mode 100644 index cb18c09069..0000000000 --- a/widget/uikit/nsWindow.h +++ /dev/null @@ -1,125 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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 NSWINDOW_H_ -#define NSWINDOW_H_ - -#include "nsBaseWidget.h" -#include "gfxPoint.h" - -#include "nsTArray.h" - -@class UIWindow; -@class UIView; -@class ChildView; - -class nsWindow : - public nsBaseWidget -{ - typedef nsBaseWidget Inherited; - -public: - nsWindow(); - - NS_DECL_ISUPPORTS_INHERITED - - // - // nsIWidget - // - - virtual MOZ_MUST_USE nsresult Create(nsIWidget* aParent, - nsNativeWidget aNativeParent, - const LayoutDeviceIntRect& aRect, - nsWidgetInitData* aInitData = nullptr) - override; - virtual void Destroy() override; - NS_IMETHOD Show(bool aState) override; - NS_IMETHOD Enable(bool aState) override { - return NS_OK; - } - virtual bool IsEnabled() const override { - return true; - } - virtual bool IsVisible() const override { - return mVisible; - } - NS_IMETHOD SetFocus(bool aState=false) override; - virtual LayoutDeviceIntPoint WidgetToScreenOffset() override; - - virtual void SetBackgroundColor(const nscolor &aColor) override; - virtual void* GetNativeData(uint32_t aDataType) override; - - NS_IMETHOD Move(double aX, double aY) override; - virtual void SetSizeMode(nsSizeMode aMode) override; - void EnteredFullScreen(bool aFullScreen); - NS_IMETHOD Resize(double aWidth, double aHeight, bool aRepaint) override; - NS_IMETHOD Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) override; - virtual LayoutDeviceIntRect GetScreenBounds() override; - void ReportMoveEvent(); - void ReportSizeEvent(); - void ReportSizeModeEvent(nsSizeMode aMode); - - CGFloat BackingScaleFactor(); - void BackingScaleFactorChanged(); - virtual float GetDPI() override { - //XXX: terrible - return 326.0f; - } - virtual double GetDefaultScaleInternal() override { - return BackingScaleFactor(); - } - virtual int32_t RoundsWidgetCoordinatesTo() override; - - NS_IMETHOD SetTitle(const nsAString& aTitle) override { - return NS_OK; - } - - NS_IMETHOD Invalidate(const LayoutDeviceIntRect& aRect) override; - virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations) override; - NS_IMETHOD DispatchEvent(mozilla::WidgetGUIEvent* aEvent, - nsEventStatus& aStatus) override; - - void WillPaintWindow(); - bool PaintWindow(LayoutDeviceIntRegion aRegion); - - bool HasModalDescendents() { return false; } - - //NS_IMETHOD NotifyIME(const IMENotification& aIMENotification) override; - NS_IMETHOD_(void) SetInputContext( - const InputContext& aContext, - const InputContextAction& aAction); - NS_IMETHOD_(InputContext) GetInputContext(); - /* - NS_IMETHOD_(bool) ExecuteNativeKeyBinding( - NativeKeyBindingsType aType, - const mozilla::WidgetKeyboardEvent& aEvent, - DoCommandCallback aCallback, - void* aCallbackData) override; - */ - -protected: - virtual ~nsWindow(); - void BringToFront(); - nsWindow *FindTopLevel(); - bool IsTopLevel(); - nsresult GetCurrentOffset(uint32_t &aOffset, uint32_t &aLength); - nsresult DeleteRange(int aOffset, int aLen); - - void TearDownView(); - - ChildView* mNativeView; - bool mVisible; - nsTArray<nsWindow*> mChildren; - nsWindow* mParent; - InputContext mInputContext; - - void OnSizeChanged(const mozilla::gfx::IntSize& aSize); - - static void DumpWindows(); - static void DumpWindows(const nsTArray<nsWindow*>& wins, int indent = 0); - static void LogWindow(nsWindow *win, int index, int indent); -}; - -#endif /* NSWINDOW_H_ */ diff --git a/widget/uikit/nsWindow.mm b/widget/uikit/nsWindow.mm deleted file mode 100644 index 874626237c..0000000000 --- a/widget/uikit/nsWindow.mm +++ /dev/null @@ -1,862 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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/. */ - -#import <UIKit/UIEvent.h> -#import <UIKit/UIGraphics.h> -#import <UIKit/UIInterface.h> -#import <UIKit/UIScreen.h> -#import <UIKit/UITapGestureRecognizer.h> -#import <UIKit/UITouch.h> -#import <UIKit/UIView.h> -#import <UIKit/UIViewController.h> -#import <UIKit/UIWindow.h> -#import <QuartzCore/QuartzCore.h> - -#include <algorithm> - -#include "nsWindow.h" -#include "nsScreenManager.h" -#include "nsAppShell.h" - -#include "nsWidgetsCID.h" -#include "nsGfxCIID.h" - -#include "gfxQuartzSurface.h" -#include "gfxUtils.h" -#include "gfxImageSurface.h" -#include "gfxContext.h" -#include "nsRegion.h" -#include "Layers.h" -#include "nsTArray.h" - -#include "mozilla/BasicEvents.h" -#include "mozilla/TouchEvents.h" -#include "mozilla/Unused.h" - -#include "GeckoProfiler.h" - -using namespace mozilla; -using namespace mozilla::dom; -using namespace mozilla::layers; - -#define ALOG(args...) fprintf(stderr, args); fprintf(stderr, "\n") - -static LayoutDeviceIntPoint -UIKitPointsToDevPixels(CGPoint aPoint, CGFloat aBackingScale) -{ - return LayoutDeviceIntPoint(NSToIntRound(aPoint.x * aBackingScale), - NSToIntRound(aPoint.y * aBackingScale)); -} - -static CGRect -DevPixelsToUIKitPoints(const LayoutDeviceIntRect& aRect, CGFloat aBackingScale) -{ - return CGRectMake((CGFloat)aRect.x / aBackingScale, - (CGFloat)aRect.y / aBackingScale, - (CGFloat)aRect.width / aBackingScale, - (CGFloat)aRect.height / aBackingScale); -} - -// Used to retain a Cocoa object for the remainder of a method's execution. -class nsAutoRetainUIKitObject { -public: -nsAutoRetainUIKitObject(id anObject) -{ - mObject = [anObject retain]; -} -~nsAutoRetainUIKitObject() -{ - [mObject release]; -} -private: - id mObject; // [STRONG] -}; - -@interface ChildView : UIView -{ -@public - nsWindow* mGeckoChild; // weak ref - BOOL mWaitingForPaint; - CFMutableDictionaryRef mTouches; - int mNextTouchID; -} -// sets up our view, attaching it to its owning gecko view -- (id)initWithFrame:(CGRect)inFrame geckoChild:(nsWindow*)inChild; -// Our Gecko child was Destroy()ed -- (void)widgetDestroyed; -// Tear down this ChildView -- (void)delayedTearDown; -- (void)sendMouseEvent:(EventMessage) aType point:(LayoutDeviceIntPoint)aPoint widget:(nsWindow*)aWindow; -- (void)handleTap:(UITapGestureRecognizer *)sender; -- (BOOL)isUsingMainThreadOpenGL; -- (void)drawUsingOpenGL; -- (void)drawUsingOpenGLCallback; -- (void)sendTouchEvent:(EventMessage) aType touches:(NSSet*)aTouches widget:(nsWindow*)aWindow; -// Event handling (UIResponder) -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; -@end - -@implementation ChildView -+ (Class)layerClass { - return [CAEAGLLayer class]; -} - -- (id)initWithFrame:(CGRect)inFrame geckoChild:(nsWindow*)inChild -{ - self.multipleTouchEnabled = YES; - if ((self = [super initWithFrame:inFrame])) { - mGeckoChild = inChild; - } - ALOG("[ChildView[%p] initWithFrame:] (mGeckoChild = %p)", (void*)self, (void*)mGeckoChild); - self.opaque = YES; - self.alpha = 1.0; - - UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] - initWithTarget:self action:@selector(handleTap:)]; - tapRecognizer.numberOfTapsRequired = 1; - [self addGestureRecognizer:tapRecognizer]; - - mTouches = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr); - mNextTouchID = 0; - return self; -} - -- (void)widgetDestroyed -{ - mGeckoChild = nullptr; - CFRelease(mTouches); -} - -- (void)delayedTearDown -{ - [self removeFromSuperview]; - [self release]; -} - -- (void)sendMouseEvent:(EventMessage) aType point:(LayoutDeviceIntPoint)aPoint widget:(nsWindow*)aWindow -{ - WidgetMouseEvent event(true, aType, aWindow, - WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); - - event.mRefPoint = aPoint; - event.mClickCount = 1; - event.button = WidgetMouseEvent::eLeftButton; - event.mTime = PR_IntervalNow(); - event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN; - - nsEventStatus status; - aWindow->DispatchEvent(&event, status); -} - -- (void)handleTap:(UITapGestureRecognizer *)sender -{ - if (sender.state == UIGestureRecognizerStateEnded) { - ALOG("[ChildView[%p] handleTap]", self); - LayoutDeviceIntPoint lp = UIKitPointsToDevPixels([sender locationInView:self], [self contentScaleFactor]); - [self sendMouseEvent:eMouseMove point:lp widget:mGeckoChild]; - [self sendMouseEvent:eMouseDown point:lp widget:mGeckoChild]; - [self sendMouseEvent:eMouseUp point:lp widget:mGeckoChild]; - } -} - -- (void)sendTouchEvent:(EventMessage) aType touches:(NSSet*)aTouches widget:(nsWindow*)aWindow -{ - WidgetTouchEvent event(true, aType, aWindow); - //XXX: I think nativeEvent.timestamp * 1000 is probably usable here but - // I don't care that much right now. - event.mTime = PR_IntervalNow(); - event.mTouches.SetCapacity(aTouches.count); - for (UITouch* touch in aTouches) { - LayoutDeviceIntPoint loc = UIKitPointsToDevPixels([touch locationInView:self], [self contentScaleFactor]); - LayoutDeviceIntPoint radius = UIKitPointsToDevPixels([touch majorRadius], [touch majorRadius]); - void* value; - if (!CFDictionaryGetValueIfPresent(mTouches, touch, (const void**)&value)) { - // This shouldn't happen. - NS_ASSERTION(false, "Got a touch that we didn't know about"); - continue; - } - int id = reinterpret_cast<int>(value); - RefPtr<Touch> t = new Touch(id, loc, radius, 0.0f, 1.0f); - event.mRefPoint = loc; - event.mTouches.AppendElement(t); - } - aWindow->DispatchInputEvent(&event); -} - -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event -{ - ALOG("[ChildView[%p] touchesBegan", self); - if (!mGeckoChild) - return; - - for (UITouch* touch : touches) { - CFDictionaryAddValue(mTouches, touch, (void*)mNextTouchID); - mNextTouchID++; - } - [self sendTouchEvent:eTouchStart - touches:[event allTouches] - widget:mGeckoChild]; -} - -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event -{ - ALOG("[ChildView[%p] touchesCancelled", self); - [self sendTouchEvent:eTouchCancel touches:touches widget:mGeckoChild]; - for (UITouch* touch : touches) { - CFDictionaryRemoveValue(mTouches, touch); - } - if (CFDictionaryGetCount(mTouches) == 0) { - mNextTouchID = 0; - } -} - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event -{ - ALOG("[ChildView[%p] touchesEnded", self); - if (!mGeckoChild) - return; - - [self sendTouchEvent:eTouchEnd touches:touches widget:mGeckoChild]; - for (UITouch* touch : touches) { - CFDictionaryRemoveValue(mTouches, touch); - } - if (CFDictionaryGetCount(mTouches) == 0) { - mNextTouchID = 0; - } -} - -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event -{ - ALOG("[ChildView[%p] touchesMoved", self); - if (!mGeckoChild) - return; - - [self sendTouchEvent:eTouchMove - touches:[event allTouches] - widget:mGeckoChild]; -} - -- (void)setNeedsDisplayInRect:(CGRect)aRect -{ - if ([self isUsingMainThreadOpenGL]) { - // Draw without calling drawRect. This prevent us from - // needing to access the normal window buffer surface unnecessarily, so we - // waste less time synchronizing the two surfaces. - if (!mWaitingForPaint) { - mWaitingForPaint = YES; - // Use NSRunLoopCommonModes instead of the default NSDefaultRunLoopMode - // so that the timer also fires while a native menu is open. - [self performSelector:@selector(drawUsingOpenGLCallback) - withObject:nil - afterDelay:0 - inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; - } - } -} - -- (BOOL)isUsingMainThreadOpenGL -{ - if (!mGeckoChild || ![self window]) - return NO; - - return mGeckoChild->GetLayerManager(nullptr)->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_OPENGL; -} - -- (void)drawUsingOpenGL -{ - ALOG("drawUsingOpenGL"); - PROFILER_LABEL("ChildView", "drawUsingOpenGL", - js::ProfileEntry::Category::GRAPHICS); - - if (!mGeckoChild->IsVisible()) - return; - - mWaitingForPaint = NO; - - LayoutDeviceIntRect geckoBounds = mGeckoChild->GetBounds(); - LayoutDeviceIntRegion region(geckoBounds); - - mGeckoChild->PaintWindow(region); -} - -// Called asynchronously after setNeedsDisplay in order to avoid entering the -// normal drawing machinery. -- (void)drawUsingOpenGLCallback -{ - if (mWaitingForPaint) { - [self drawUsingOpenGL]; - } -} - -// The display system has told us that a portion of our view is dirty. Tell -// gecko to paint it -- (void)drawRect:(CGRect)aRect -{ - CGContextRef cgContext = UIGraphicsGetCurrentContext(); - [self drawRect:aRect inContext:cgContext]; -} - -- (void)drawRect:(CGRect)aRect inContext:(CGContextRef)aContext -{ -#ifdef DEBUG_UPDATE - LayoutDeviceIntRect geckoBounds = mGeckoChild->GetBounds(); - - fprintf (stderr, "---- Update[%p][%p] [%f %f %f %f] cgc: %p\n gecko bounds: [%d %d %d %d]\n", - self, mGeckoChild, - aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height, aContext, - geckoBounds.x, geckoBounds.y, geckoBounds.width, geckoBounds.height); - - CGAffineTransform xform = CGContextGetCTM(aContext); - fprintf (stderr, " xform in: [%f %f %f %f %f %f]\n", xform.a, xform.b, xform.c, xform.d, xform.tx, xform.ty); -#endif - - if (true) { - // For Gecko-initiated repaints in OpenGL mode, drawUsingOpenGL is - // directly called from a delayed perform callback - without going through - // drawRect. - // Paints that come through here are triggered by something that Cocoa - // controls, for example by window resizing or window focus changes. - - // Do GL composition and return. - [self drawUsingOpenGL]; - return; - } - PROFILER_LABEL("ChildView", "drawRect", - js::ProfileEntry::Category::GRAPHICS); - - // The CGContext that drawRect supplies us with comes with a transform that - // scales one user space unit to one Cocoa point, which can consist of - // multiple dev pixels. But Gecko expects its supplied context to be scaled - // to device pixels, so we need to reverse the scaling. - double scale = mGeckoChild->BackingScaleFactor(); - CGContextSaveGState(aContext); - CGContextScaleCTM(aContext, 1.0 / scale, 1.0 / scale); - - CGSize viewSize = [self bounds].size; - gfx::IntSize backingSize(viewSize.width * scale, viewSize.height * scale); - - CGContextSaveGState(aContext); - - LayoutDeviceIntRegion region = - LayoutDeviceIntRect(NSToIntRound(aRect.origin.x * scale), - NSToIntRound(aRect.origin.y * scale), - NSToIntRound(aRect.size.width * scale), - NSToIntRound(aRect.size.height * scale)); - - // Create Cairo objects. - RefPtr<gfxQuartzSurface> targetSurface; - - RefPtr<gfxContext> targetContext; - if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(gfx::BackendType::CAIRO)) { - // This is dead code unless you mess with prefs, but keep it around for - // debugging. - targetSurface = new gfxQuartzSurface(aContext, backingSize); - targetSurface->SetAllowUseAsSource(false); - RefPtr<gfx::DrawTarget> dt = - gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(targetSurface, - backingSize); - if (!dt || !dt->IsValid()) { - gfxDevCrash(mozilla::gfx::LogReason::InvalidContext) << "Window context problem 2 " << backingSize; - return; - } - dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr); - targetContext = gfxContext::CreateOrNull(dt); - } else { - MOZ_ASSERT_UNREACHABLE("COREGRAPHICS is the only supported backend"); - } - MOZ_ASSERT(targetContext); // already checked for valid draw targets above - - // Set up the clip region. - targetContext->NewPath(); - for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) { - const LayoutDeviceIntRect& r = iter.Get(); - targetContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height)); - } - targetContext->Clip(); - - //nsAutoRetainCocoaObject kungFuDeathGrip(self); - bool painted = false; - if (mGeckoChild->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) { - nsBaseWidget::AutoLayerManagerSetup - setupLayerManager(mGeckoChild, targetContext, BufferMode::BUFFER_NONE); - painted = mGeckoChild->PaintWindow(region); - } else if (mGeckoChild->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) { - // We only need this so that we actually get DidPaintWindow fired - painted = mGeckoChild->PaintWindow(region); - } - - targetContext = nullptr; - targetSurface = nullptr; - - CGContextRestoreGState(aContext); - - // Undo the scale transform so that from now on the context is in - // CocoaPoints again. - CGContextRestoreGState(aContext); - if (!painted && [self isOpaque]) { - // Gecko refused to draw, but we've claimed to be opaque, so we have to - // draw something--fill with white. - CGContextSetRGBFillColor(aContext, 1, 1, 1, 1); - CGContextFillRect(aContext, aRect); - } - -#ifdef DEBUG_UPDATE - fprintf (stderr, "---- update done ----\n"); - -#if 0 - CGContextSetRGBStrokeColor (aContext, - ((((unsigned long)self) & 0xff)) / 255.0, - ((((unsigned long)self) & 0xff00) >> 8) / 255.0, - ((((unsigned long)self) & 0xff0000) >> 16) / 255.0, - 0.5); -#endif - CGContextSetRGBStrokeColor(aContext, 1, 0, 0, 0.8); - CGContextSetLineWidth(aContext, 4.0); - CGContextStrokeRect(aContext, aRect); -#endif -} -@end - -NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, Inherited) - -nsWindow::nsWindow() -: mNativeView(nullptr), - mVisible(false), - mParent(nullptr) -{ -} - -nsWindow::~nsWindow() -{ - [mNativeView widgetDestroyed]; // Safe if mNativeView is nil. - TearDownView(); // Safe if called twice. -} - -void nsWindow::TearDownView() -{ - if (!mNativeView) - return; - - [mNativeView performSelectorOnMainThread:@selector(delayedTearDown) withObject:nil waitUntilDone:false]; - mNativeView = nil; -} - -bool -nsWindow::IsTopLevel() -{ - return mWindowType == eWindowType_toplevel || - mWindowType == eWindowType_dialog || - mWindowType == eWindowType_invisible; -} - -// -// nsIWidget -// - -nsresult -nsWindow::Create(nsIWidget* aParent, - nsNativeWidget aNativeParent, - const LayoutDeviceIntRect& aRect, - nsWidgetInitData* aInitData) -{ - ALOG("nsWindow[%p]::Create %p/%p [%d %d %d %d]", (void*)this, (void*)aParent, (void*)aNativeParent, aRect.x, aRect.y, aRect.width, aRect.height); - nsWindow* parent = (nsWindow*) aParent; - ChildView* nativeParent = (ChildView*)aNativeParent; - - if (parent == nullptr && nativeParent) - parent = nativeParent->mGeckoChild; - if (parent && nativeParent == nullptr) - nativeParent = parent->mNativeView; - - // for toplevel windows, bounds are fixed to full screen size - if (parent == nullptr) { - if (nsAppShell::gWindow == nil) { - mBounds = UIKitScreenManager::GetBounds(); - } else { - CGRect cgRect = [nsAppShell::gWindow bounds]; - mBounds.x = cgRect.origin.x; - mBounds.y = cgRect.origin.y; - mBounds.width = cgRect.size.width; - mBounds.height = cgRect.size.height; - } - } else { - mBounds = aRect; - } - - ALOG("nsWindow[%p]::Create bounds: %d %d %d %d", (void*)this, - mBounds.x, mBounds.y, mBounds.width, mBounds.height); - - // Set defaults which can be overriden from aInitData in BaseCreate - mWindowType = eWindowType_toplevel; - mBorderStyle = eBorderStyle_default; - - Inherited::BaseCreate(aParent, aInitData); - - NS_ASSERTION(IsTopLevel() || parent, "non top level window doesn't have a parent!"); - - mNativeView = [[ChildView alloc] initWithFrame:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor()) geckoChild:this]; - mNativeView.hidden = YES; - - if (parent) { - parent->mChildren.AppendElement(this); - mParent = parent; - } - - if (nativeParent) { - [nativeParent addSubview:mNativeView]; - } else if (nsAppShell::gWindow) { - [nsAppShell::gWindow.rootViewController.view addSubview:mNativeView]; - } - else { - [nsAppShell::gTopLevelViews addObject:mNativeView]; - } - - return NS_OK; -} - -void -nsWindow::Destroy() -{ - for (uint32_t i = 0; i < mChildren.Length(); ++i) { - // why do we still have children? - mChildren[i]->SetParent(nullptr); - } - - if (mParent) - mParent->mChildren.RemoveElement(this); - - [mNativeView widgetDestroyed]; - - nsBaseWidget::Destroy(); - - //ReportDestroyEvent(); - - TearDownView(); - - nsBaseWidget::OnDestroy(); - - return NS_OK; -} - -NS_IMETHODIMP -nsWindow::ConfigureChildren(const nsTArray<nsIWidget::Configuration>& config) -{ - for (uint32_t i = 0; i < config.Length(); ++i) { - nsWindow *childWin = (nsWindow*) config[i].mChild.get(); - childWin->Resize(config[i].mBounds.x, - config[i].mBounds.y, - config[i].mBounds.width, - config[i].mBounds.height, - false); - } - - return NS_OK; -} - -NS_IMETHODIMP -nsWindow::Show(bool aState) -{ - if (aState != mVisible) { - mNativeView.hidden = aState ? NO : YES; - if (aState) { - UIView* parentView = mParent ? mParent->mNativeView : nsAppShell::gWindow.rootViewController.view; - [parentView bringSubviewToFront:mNativeView]; - [mNativeView setNeedsDisplay]; - } - mVisible = aState; - } - return NS_OK; -} - -NS_IMETHODIMP -nsWindow::Move(double aX, double aY) -{ - if (!mNativeView || (mBounds.x == aX && mBounds.y == aY)) - return NS_OK; - - //XXX: handle this - // The point we have is in Gecko coordinates (origin top-left). Convert - // it to Cocoa ones (origin bottom-left). - mBounds.x = aX; - mBounds.y = aY; - - mNativeView.frame = DevPixelsToUIKitPoints(mBounds, BackingScaleFactor()); - - if (mVisible) - [mNativeView setNeedsDisplay]; - - ReportMoveEvent(); - return NS_OK; -} - -NS_IMETHODIMP -nsWindow::Resize(double aX, double aY, - double aWidth, double aHeight, - bool aRepaint) -{ - BOOL isMoving = (mBounds.x != aX || mBounds.y != aY); - BOOL isResizing = (mBounds.width != aWidth || mBounds.height != aHeight); - if (!mNativeView || (!isMoving && !isResizing)) - return NS_OK; - - if (isMoving) { - mBounds.x = aX; - mBounds.y = aY; - } - if (isResizing) { - mBounds.width = aWidth; - mBounds.height = aHeight; - } - - [mNativeView setFrame:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())]; - - if (mVisible && aRepaint) - [mNativeView setNeedsDisplay]; - - if (isMoving) - ReportMoveEvent(); - - if (isResizing) - ReportSizeEvent(); - - return NS_OK; -} - -NS_IMETHODIMP nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) -{ - if (!mNativeView || (mBounds.width == aWidth && mBounds.height == aHeight)) - return NS_OK; - - mBounds.width = aWidth; - mBounds.height = aHeight; - - [mNativeView setFrame:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())]; - - if (mVisible && aRepaint) - [mNativeView setNeedsDisplay]; - - ReportSizeEvent(); - - return NS_OK; -} - -void -nsWindow::SetSizeMode(nsSizeMode aMode) -{ - if (aMode == static_cast<int32_t>(mSizeMode)) { - return; - } - - mSizeMode = static_cast<nsSizeMode>(aMode); - if (aMode == nsSizeMode_Maximized || aMode == nsSizeMode_Fullscreen) { - // Resize to fill screen - nsBaseWidget::InfallibleMakeFullScreen(true); - } - ReportSizeModeEvent(aMode); -} - -NS_IMETHODIMP -nsWindow::Invalidate(const LayoutDeviceIntRect& aRect) -{ - if (!mNativeView || !mVisible) - return NS_OK; - - MOZ_RELEASE_ASSERT(GetLayerManager()->GetBackendType() != LayersBackend::LAYERS_CLIENT, - "Shouldn't need to invalidate with accelerated OMTC layers!"); - - - [mNativeView setNeedsLayout]; - [mNativeView setNeedsDisplayInRect:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())]; - - return NS_OK; -} - -NS_IMETHODIMP -nsWindow::SetFocus(bool aRaise) -{ - [[mNativeView window] makeKeyWindow]; - [mNativeView becomeFirstResponder]; - return NS_OK; -} - -void nsWindow::WillPaintWindow() -{ - if (mWidgetListener) { - mWidgetListener->WillPaintWindow(this); - } -} - -bool nsWindow::PaintWindow(LayoutDeviceIntRegion aRegion) -{ - if (!mWidgetListener) - return false; - - bool returnValue = false; - returnValue = mWidgetListener->PaintWindow(this, aRegion); - - if (mWidgetListener) { - mWidgetListener->DidPaintWindow(); - } - - return returnValue; -} - -void nsWindow::ReportMoveEvent() -{ - NotifyWindowMoved(mBounds.x, mBounds.y); -} - -void nsWindow::ReportSizeModeEvent(nsSizeMode aMode) -{ - if (mWidgetListener) { - // This is terrible. - nsSizeMode theMode; - switch (aMode) { - case nsSizeMode_Maximized: - theMode = nsSizeMode_Maximized; - break; - case nsSizeMode_Fullscreen: - theMode = nsSizeMode_Fullscreen; - break; - default: - return; - } - mWidgetListener->SizeModeChanged(theMode); - } -} - -void nsWindow::ReportSizeEvent() -{ - if (mWidgetListener) { - LayoutDeviceIntRect innerBounds = GetClientBounds(); - mWidgetListener->WindowResized(this, innerBounds.width, innerBounds.height); - } -} - -LayoutDeviceIntRect -nsWindow::GetScreenBounds() -{ - return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size()); -} - -LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset() -{ - LayoutDeviceIntPoint offset(0, 0); - if (mParent) { - offset = mParent->WidgetToScreenOffset(); - } - - CGPoint temp = [mNativeView convertPoint:temp toView:nil]; - - if (!mParent && nsAppShell::gWindow) { - // convert to screen coords - temp = [nsAppShell::gWindow convertPoint:temp toWindow:nil]; - } - - offset.x += temp.x; - offset.y += temp.y; - - return offset; -} - -NS_IMETHODIMP -nsWindow::DispatchEvent(mozilla::WidgetGUIEvent* aEvent, - nsEventStatus& aStatus) -{ - aStatus = nsEventStatus_eIgnore; - nsCOMPtr<nsIWidget> kungFuDeathGrip(aEvent->mWidget); - - if (mWidgetListener) - aStatus = mWidgetListener->HandleEvent(aEvent, mUseAttachedEvents); - - return NS_OK; -} - -NS_IMETHODIMP_(void) -nsWindow::SetInputContext(const InputContext& aContext, - const InputContextAction& aAction) -{ - //TODO: actually show VKB - mInputContext = aContext; -} - -NS_IMETHODIMP_(mozilla::widget::InputContext) -nsWindow::GetInputContext() -{ - return mInputContext; -} - -void -nsWindow::SetBackgroundColor(const nscolor &aColor) -{ - mNativeView.backgroundColor = [UIColor colorWithRed:NS_GET_R(aColor) - green:NS_GET_G(aColor) - blue:NS_GET_B(aColor) - alpha:NS_GET_A(aColor)]; -} - -void* nsWindow::GetNativeData(uint32_t aDataType) -{ - void* retVal = nullptr; - - switch (aDataType) - { - case NS_NATIVE_WIDGET: - case NS_NATIVE_DISPLAY: - retVal = (void*)mNativeView; - break; - - case NS_NATIVE_WINDOW: - retVal = [mNativeView window]; - break; - - case NS_NATIVE_GRAPHIC: - NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a UIKit child view!"); - break; - - case NS_NATIVE_OFFSETX: - retVal = 0; - break; - - case NS_NATIVE_OFFSETY: - retVal = 0; - break; - - case NS_NATIVE_PLUGIN_PORT: - // not implemented - break; - - case NS_RAW_NATIVE_IME_CONTEXT: - retVal = GetPseudoIMEContext(); - if (retVal) { - break; - } - retVal = NS_ONLY_ONE_NATIVE_IME_CONTEXT; - break; - } - - return retVal; -} - -CGFloat -nsWindow::BackingScaleFactor() -{ - if (mNativeView) { - return [mNativeView contentScaleFactor]; - } - return [UIScreen mainScreen].scale; -} - -int32_t -nsWindow::RoundsWidgetCoordinatesTo() -{ - if (BackingScaleFactor() == 2.0) { - return 2; - } - return 1; -} |