mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-09 17:31:47 +09:00
Issue #1751 -- Remove cocoa support code from /dom
This commit is contained in:
parent
82b363f05d
commit
a6ae35ba91
23 changed files with 1 additions and 2293 deletions
|
|
@ -1,590 +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/. */
|
||||
|
||||
// mostly derived from the Allegro source code at:
|
||||
// http://alleg.svn.sourceforge.net/viewvc/alleg/allegro/branches/4.9/src/macosx/hidjoy.m?revision=13760&view=markup
|
||||
|
||||
#include "mozilla/dom/Gamepad.h"
|
||||
#include "mozilla/dom/GamepadPlatformService.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/hid/IOHIDBase.h>
|
||||
#include <IOKit/hid/IOHIDKeys.h>
|
||||
#include <IOKit/hid/IOHIDManager.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using std::vector;
|
||||
|
||||
struct Button {
|
||||
int id;
|
||||
bool analog;
|
||||
IOHIDElementRef element;
|
||||
CFIndex min;
|
||||
CFIndex max;
|
||||
|
||||
Button(int aId, IOHIDElementRef aElement, CFIndex aMin, CFIndex aMax) :
|
||||
id(aId),
|
||||
analog((aMax - aMin) > 1),
|
||||
element(aElement),
|
||||
min(aMin),
|
||||
max(aMax) {}
|
||||
};
|
||||
|
||||
struct Axis {
|
||||
int id;
|
||||
IOHIDElementRef element;
|
||||
uint32_t usagePage;
|
||||
uint32_t usage;
|
||||
CFIndex min;
|
||||
CFIndex max;
|
||||
};
|
||||
|
||||
typedef bool dpad_buttons[4];
|
||||
|
||||
// These values can be found in the USB HID Usage Tables:
|
||||
// http://www.usb.org/developers/hidpage
|
||||
const unsigned kDesktopUsagePage = 0x01;
|
||||
const unsigned kSimUsagePage = 0x02;
|
||||
const unsigned kAcceleratorUsage = 0xC4;
|
||||
const unsigned kBrakeUsage = 0xC5;
|
||||
const unsigned kJoystickUsage = 0x04;
|
||||
const unsigned kGamepadUsage = 0x05;
|
||||
const unsigned kAxisUsageMin = 0x30;
|
||||
const unsigned kAxisUsageMax = 0x35;
|
||||
const unsigned kDpadUsage = 0x39;
|
||||
const unsigned kButtonUsagePage = 0x09;
|
||||
const unsigned kConsumerPage = 0x0C;
|
||||
const unsigned kHomeUsage = 0x223;
|
||||
const unsigned kBackUsage = 0x224;
|
||||
|
||||
|
||||
class Gamepad {
|
||||
private:
|
||||
IOHIDDeviceRef mDevice;
|
||||
nsTArray<Button> buttons;
|
||||
nsTArray<Axis> axes;
|
||||
IOHIDElementRef mDpad;
|
||||
dpad_buttons mDpadState;
|
||||
|
||||
public:
|
||||
Gamepad() : mDevice(nullptr), mDpad(nullptr), mSuperIndex(-1) {}
|
||||
bool operator==(IOHIDDeviceRef device) const { return mDevice == device; }
|
||||
bool empty() const { return mDevice == nullptr; }
|
||||
void clear()
|
||||
{
|
||||
mDevice = nullptr;
|
||||
buttons.Clear();
|
||||
axes.Clear();
|
||||
mDpad = nullptr;
|
||||
mSuperIndex = -1;
|
||||
}
|
||||
void init(IOHIDDeviceRef device);
|
||||
size_t numButtons() { return buttons.Length() + (mDpad ? 4 : 0); }
|
||||
size_t numAxes() { return axes.Length(); }
|
||||
|
||||
// Index given by our superclass.
|
||||
uint32_t mSuperIndex;
|
||||
|
||||
bool isDpad(IOHIDElementRef element) const
|
||||
{
|
||||
return element == mDpad;
|
||||
}
|
||||
|
||||
const dpad_buttons& getDpadState() const
|
||||
{
|
||||
return mDpadState;
|
||||
}
|
||||
|
||||
void setDpadState(const dpad_buttons& dpadState)
|
||||
{
|
||||
for (unsigned i = 0; i < ArrayLength(mDpadState); i++) {
|
||||
mDpadState[i] = dpadState[i];
|
||||
}
|
||||
}
|
||||
|
||||
const Button* lookupButton(IOHIDElementRef element) const
|
||||
{
|
||||
for (unsigned i = 0; i < buttons.Length(); i++) {
|
||||
if (buttons[i].element == element)
|
||||
return &buttons[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Axis* lookupAxis(IOHIDElementRef element) const
|
||||
{
|
||||
for (unsigned i = 0; i < axes.Length(); i++) {
|
||||
if (axes[i].element == element)
|
||||
return &axes[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class AxisComparator {
|
||||
public:
|
||||
bool Equals(const Axis& a1, const Axis& a2) const
|
||||
{
|
||||
return a1.usagePage == a2.usagePage && a1.usage == a2.usage;
|
||||
}
|
||||
bool LessThan(const Axis& a1, const Axis& a2) const
|
||||
{
|
||||
if (a1.usagePage == a2.usagePage) {
|
||||
return a1.usage < a2.usage;
|
||||
}
|
||||
return a1.usagePage < a2.usagePage;
|
||||
}
|
||||
};
|
||||
|
||||
void Gamepad::init(IOHIDDeviceRef device)
|
||||
{
|
||||
clear();
|
||||
mDevice = device;
|
||||
|
||||
CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device,
|
||||
nullptr,
|
||||
kIOHIDOptionsTypeNone);
|
||||
CFIndex n = CFArrayGetCount(elements);
|
||||
for (CFIndex i = 0; i < n; i++) {
|
||||
IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements,
|
||||
i);
|
||||
uint32_t usagePage = IOHIDElementGetUsagePage(element);
|
||||
uint32_t usage = IOHIDElementGetUsage(element);
|
||||
|
||||
if (usagePage == kDesktopUsagePage &&
|
||||
usage >= kAxisUsageMin &&
|
||||
usage <= kAxisUsageMax)
|
||||
{
|
||||
Axis axis = { int(axes.Length()),
|
||||
element,
|
||||
usagePage,
|
||||
usage,
|
||||
IOHIDElementGetLogicalMin(element),
|
||||
IOHIDElementGetLogicalMax(element) };
|
||||
axes.AppendElement(axis);
|
||||
} else if (usagePage == kDesktopUsagePage && usage == kDpadUsage &&
|
||||
// Don't know how to handle d-pads that return weird values.
|
||||
IOHIDElementGetLogicalMax(element) - IOHIDElementGetLogicalMin(element) == 7) {
|
||||
mDpad = element;
|
||||
} else if ((usagePage == kSimUsagePage &&
|
||||
(usage == kAcceleratorUsage ||
|
||||
usage == kBrakeUsage)) ||
|
||||
(usagePage == kButtonUsagePage) ||
|
||||
(usagePage == kConsumerPage &&
|
||||
(usage == kHomeUsage ||
|
||||
usage == kBackUsage))) {
|
||||
Button button(int(buttons.Length()), element, IOHIDElementGetLogicalMin(element), IOHIDElementGetLogicalMax(element));
|
||||
buttons.AppendElement(button);
|
||||
} else {
|
||||
//TODO: handle other usage pages
|
||||
}
|
||||
}
|
||||
|
||||
AxisComparator comparator;
|
||||
axes.Sort(comparator);
|
||||
for (unsigned i = 0; i < axes.Length(); i++) {
|
||||
axes[i].id = i;
|
||||
}
|
||||
}
|
||||
|
||||
class DarwinGamepadService {
|
||||
private:
|
||||
IOHIDManagerRef mManager;
|
||||
vector<Gamepad> mGamepads;
|
||||
|
||||
//Workaround to support running in background thread
|
||||
CFRunLoopRef mMonitorRunLoop;
|
||||
nsCOMPtr<nsIThread> mMonitorThread;
|
||||
|
||||
static void DeviceAddedCallback(void* data, IOReturn result,
|
||||
void* sender, IOHIDDeviceRef device);
|
||||
static void DeviceRemovedCallback(void* data, IOReturn result,
|
||||
void* sender, IOHIDDeviceRef device);
|
||||
static void InputValueChangedCallback(void* data, IOReturn result,
|
||||
void* sender, IOHIDValueRef newValue);
|
||||
|
||||
void DeviceAdded(IOHIDDeviceRef device);
|
||||
void DeviceRemoved(IOHIDDeviceRef device);
|
||||
void InputValueChanged(IOHIDValueRef value);
|
||||
void StartupInternal();
|
||||
|
||||
public:
|
||||
DarwinGamepadService();
|
||||
~DarwinGamepadService();
|
||||
void Startup();
|
||||
void Shutdown();
|
||||
friend class DarwinGamepadServiceStartupRunnable;
|
||||
};
|
||||
|
||||
class DarwinGamepadServiceStartupRunnable final : public Runnable
|
||||
{
|
||||
private:
|
||||
~DarwinGamepadServiceStartupRunnable() {}
|
||||
// This Runnable schedules startup of DarwinGamepadService
|
||||
// in a new thread, pointer to DarwinGamepadService is only
|
||||
// used by this Runnable within its thread.
|
||||
DarwinGamepadService MOZ_NON_OWNING_REF *mService;
|
||||
public:
|
||||
explicit DarwinGamepadServiceStartupRunnable(DarwinGamepadService *service)
|
||||
: mService(service) {}
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
mService->StartupInternal();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
DarwinGamepadService::DeviceAdded(IOHIDDeviceRef device)
|
||||
{
|
||||
RefPtr<GamepadPlatformService> service =
|
||||
GamepadPlatformService::GetParentService();
|
||||
if (!service) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t slot = size_t(-1);
|
||||
for (size_t i = 0; i < mGamepads.size(); i++) {
|
||||
if (mGamepads[i] == device)
|
||||
return;
|
||||
if (slot == size_t(-1) && mGamepads[i].empty())
|
||||
slot = i;
|
||||
}
|
||||
|
||||
if (slot == size_t(-1)) {
|
||||
slot = mGamepads.size();
|
||||
mGamepads.push_back(Gamepad());
|
||||
}
|
||||
mGamepads[slot].init(device);
|
||||
|
||||
// Gather some identifying information
|
||||
CFNumberRef vendorIdRef =
|
||||
(CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
|
||||
CFNumberRef productIdRef =
|
||||
(CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
|
||||
CFStringRef productRef =
|
||||
(CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
|
||||
int vendorId, productId;
|
||||
CFNumberGetValue(vendorIdRef, kCFNumberIntType, &vendorId);
|
||||
CFNumberGetValue(productIdRef, kCFNumberIntType, &productId);
|
||||
char product_name[128];
|
||||
CFStringGetCString(productRef, product_name,
|
||||
sizeof(product_name), kCFStringEncodingASCII);
|
||||
char buffer[256];
|
||||
sprintf(buffer, "%x-%x-%s", vendorId, productId, product_name);
|
||||
uint32_t index = service->AddGamepad(buffer,
|
||||
mozilla::dom::GamepadMappingType::_empty,
|
||||
(int)mGamepads[slot].numButtons(),
|
||||
(int)mGamepads[slot].numAxes());
|
||||
mGamepads[slot].mSuperIndex = index;
|
||||
}
|
||||
|
||||
void
|
||||
DarwinGamepadService::DeviceRemoved(IOHIDDeviceRef device)
|
||||
{
|
||||
RefPtr<GamepadPlatformService> service =
|
||||
GamepadPlatformService::GetParentService();
|
||||
if (!service) {
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < mGamepads.size(); i++) {
|
||||
if (mGamepads[i] == device) {
|
||||
service->RemoveGamepad(mGamepads[i].mSuperIndex);
|
||||
mGamepads[i].clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a value from a d-pad (POV hat in USB HID terminology),
|
||||
* represent it as 4 buttons, one for each cardinal direction.
|
||||
*/
|
||||
static void
|
||||
UnpackDpad(int dpad_value, int min, int max, dpad_buttons& buttons)
|
||||
{
|
||||
const unsigned kUp = 0;
|
||||
const unsigned kDown = 1;
|
||||
const unsigned kLeft = 2;
|
||||
const unsigned kRight = 3;
|
||||
|
||||
// Different controllers have different ways of representing
|
||||
// "nothing is pressed", but they're all outside the range of values.
|
||||
if (dpad_value < min || dpad_value > max) {
|
||||
// Nothing is pressed.
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize value to start at 0.
|
||||
int value = dpad_value - min;
|
||||
|
||||
// Value will be in the range 0-7. The value represents the
|
||||
// position of the d-pad around a circle, with 0 being straight up,
|
||||
// 2 being right, 4 being straight down, and 6 being left.
|
||||
if (value < 2 || value > 6) {
|
||||
buttons[kUp] = true;
|
||||
}
|
||||
if (value > 2 && value < 6) {
|
||||
buttons[kDown] = true;
|
||||
}
|
||||
if (value > 4) {
|
||||
buttons[kLeft] = true;
|
||||
}
|
||||
if (value > 0 && value < 4) {
|
||||
buttons[kRight] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DarwinGamepadService::InputValueChanged(IOHIDValueRef value)
|
||||
{
|
||||
RefPtr<GamepadPlatformService> service =
|
||||
GamepadPlatformService::GetParentService();
|
||||
if (!service) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t value_length = IOHIDValueGetLength(value);
|
||||
if (value_length > 4) {
|
||||
// Workaround for bizarre issue with PS3 controllers that try to return
|
||||
// massive (30+ byte) values and crash IOHIDValueGetIntegerValue
|
||||
return;
|
||||
}
|
||||
IOHIDElementRef element = IOHIDValueGetElement(value);
|
||||
IOHIDDeviceRef device = IOHIDElementGetDevice(element);
|
||||
|
||||
for (unsigned i = 0; i < mGamepads.size(); i++) {
|
||||
Gamepad &gamepad = mGamepads[i];
|
||||
if (gamepad == device) {
|
||||
if (gamepad.isDpad(element)) {
|
||||
const dpad_buttons& oldState = gamepad.getDpadState();
|
||||
dpad_buttons newState = { false, false, false, false };
|
||||
UnpackDpad(IOHIDValueGetIntegerValue(value),
|
||||
IOHIDElementGetLogicalMin(element),
|
||||
IOHIDElementGetLogicalMax(element),
|
||||
newState);
|
||||
const int numButtons = gamepad.numButtons();
|
||||
for (unsigned b = 0; b < ArrayLength(newState); b++) {
|
||||
if (newState[b] != oldState[b]) {
|
||||
service->NewButtonEvent(gamepad.mSuperIndex,
|
||||
numButtons - 4 + b,
|
||||
newState[b]);
|
||||
}
|
||||
}
|
||||
gamepad.setDpadState(newState);
|
||||
} else if (const Axis* axis = gamepad.lookupAxis(element)) {
|
||||
double d = IOHIDValueGetIntegerValue(value);
|
||||
double v = 2.0f * (d - axis->min) /
|
||||
(double)(axis->max - axis->min) - 1.0f;
|
||||
service->NewAxisMoveEvent(gamepad.mSuperIndex, axis->id, v);
|
||||
} else if (const Button* button = gamepad.lookupButton(element)) {
|
||||
int iv = IOHIDValueGetIntegerValue(value);
|
||||
bool pressed = iv != 0;
|
||||
double v = 0;
|
||||
if (button->analog) {
|
||||
double dv = iv;
|
||||
v = (dv - button->min) / (double)(button->max - button->min);
|
||||
} else {
|
||||
v = pressed ? 1.0 : 0.0;
|
||||
}
|
||||
service->NewButtonEvent(gamepad.mSuperIndex, button->id, pressed, v);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DarwinGamepadService::DeviceAddedCallback(void* data, IOReturn result,
|
||||
void* sender, IOHIDDeviceRef device)
|
||||
{
|
||||
DarwinGamepadService* service = (DarwinGamepadService*)data;
|
||||
service->DeviceAdded(device);
|
||||
}
|
||||
|
||||
void
|
||||
DarwinGamepadService::DeviceRemovedCallback(void* data, IOReturn result,
|
||||
void* sender, IOHIDDeviceRef device)
|
||||
{
|
||||
DarwinGamepadService* service = (DarwinGamepadService*)data;
|
||||
service->DeviceRemoved(device);
|
||||
}
|
||||
|
||||
void
|
||||
DarwinGamepadService::InputValueChangedCallback(void* data,
|
||||
IOReturn result,
|
||||
void* sender,
|
||||
IOHIDValueRef newValue)
|
||||
{
|
||||
DarwinGamepadService* service = (DarwinGamepadService*)data;
|
||||
service->InputValueChanged(newValue);
|
||||
}
|
||||
|
||||
static CFMutableDictionaryRef
|
||||
MatchingDictionary(UInt32 inUsagePage, UInt32 inUsage)
|
||||
{
|
||||
CFMutableDictionaryRef dict =
|
||||
CFDictionaryCreateMutable(kCFAllocatorDefault,
|
||||
0,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
if (!dict)
|
||||
return nullptr;
|
||||
CFNumberRef number = CFNumberCreate(kCFAllocatorDefault,
|
||||
kCFNumberIntType,
|
||||
&inUsagePage);
|
||||
if (!number) {
|
||||
CFRelease(dict);
|
||||
return nullptr;
|
||||
}
|
||||
CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), number);
|
||||
CFRelease(number);
|
||||
|
||||
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage);
|
||||
if (!number) {
|
||||
CFRelease(dict);
|
||||
return nullptr;
|
||||
}
|
||||
CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), number);
|
||||
CFRelease(number);
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
DarwinGamepadService::DarwinGamepadService() : mManager(nullptr) {}
|
||||
|
||||
DarwinGamepadService::~DarwinGamepadService()
|
||||
{
|
||||
if (mManager != nullptr)
|
||||
CFRelease(mManager);
|
||||
}
|
||||
|
||||
void
|
||||
DarwinGamepadService::StartupInternal()
|
||||
{
|
||||
if (mManager != nullptr)
|
||||
return;
|
||||
|
||||
IOHIDManagerRef manager = IOHIDManagerCreate(kCFAllocatorDefault,
|
||||
kIOHIDOptionsTypeNone);
|
||||
|
||||
CFMutableDictionaryRef criteria_arr[2];
|
||||
criteria_arr[0] = MatchingDictionary(kDesktopUsagePage,
|
||||
kJoystickUsage);
|
||||
if (!criteria_arr[0]) {
|
||||
CFRelease(manager);
|
||||
return;
|
||||
}
|
||||
|
||||
criteria_arr[1] = MatchingDictionary(kDesktopUsagePage,
|
||||
kGamepadUsage);
|
||||
if (!criteria_arr[1]) {
|
||||
CFRelease(criteria_arr[0]);
|
||||
CFRelease(manager);
|
||||
return;
|
||||
}
|
||||
|
||||
CFArrayRef criteria =
|
||||
CFArrayCreate(kCFAllocatorDefault, (const void**)criteria_arr, 2, nullptr);
|
||||
if (!criteria) {
|
||||
CFRelease(criteria_arr[1]);
|
||||
CFRelease(criteria_arr[0]);
|
||||
CFRelease(manager);
|
||||
return;
|
||||
}
|
||||
|
||||
IOHIDManagerSetDeviceMatchingMultiple(manager, criteria);
|
||||
CFRelease(criteria);
|
||||
CFRelease(criteria_arr[1]);
|
||||
CFRelease(criteria_arr[0]);
|
||||
|
||||
IOHIDManagerRegisterDeviceMatchingCallback(manager,
|
||||
DeviceAddedCallback,
|
||||
this);
|
||||
IOHIDManagerRegisterDeviceRemovalCallback(manager,
|
||||
DeviceRemovedCallback,
|
||||
this);
|
||||
IOHIDManagerRegisterInputValueCallback(manager,
|
||||
InputValueChangedCallback,
|
||||
this);
|
||||
IOHIDManagerScheduleWithRunLoop(manager,
|
||||
CFRunLoopGetCurrent(),
|
||||
kCFRunLoopDefaultMode);
|
||||
IOReturn rv = IOHIDManagerOpen(manager, kIOHIDOptionsTypeNone);
|
||||
if (rv != kIOReturnSuccess) {
|
||||
CFRelease(manager);
|
||||
return;
|
||||
}
|
||||
|
||||
mManager = manager;
|
||||
|
||||
// We held the handle of the CFRunLoop to make sure we
|
||||
// can shut it down explicitly by CFRunLoopStop in another
|
||||
// thread.
|
||||
mMonitorRunLoop = CFRunLoopGetCurrent();
|
||||
|
||||
// CFRunLoopRun() is a blocking message loop when it's called in
|
||||
// non-main thread so this thread cannot receive any other runnables
|
||||
// and nsITimer timeout events after it's called.
|
||||
CFRunLoopRun();
|
||||
}
|
||||
|
||||
void DarwinGamepadService::Startup()
|
||||
{
|
||||
Unused << NS_NewThread(getter_AddRefs(mMonitorThread),
|
||||
new DarwinGamepadServiceStartupRunnable(this));
|
||||
}
|
||||
|
||||
void DarwinGamepadService::Shutdown()
|
||||
{
|
||||
IOHIDManagerRef manager = (IOHIDManagerRef)mManager;
|
||||
CFRunLoopStop(mMonitorRunLoop);
|
||||
if (manager) {
|
||||
IOHIDManagerClose(manager, 0);
|
||||
CFRelease(manager);
|
||||
mManager = nullptr;
|
||||
}
|
||||
mMonitorThread->Shutdown();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
DarwinGamepadService* gService = nullptr;
|
||||
|
||||
void StartGamepadMonitoring()
|
||||
{
|
||||
if (gService) {
|
||||
return;
|
||||
}
|
||||
|
||||
gService = new DarwinGamepadService();
|
||||
gService->Startup();
|
||||
}
|
||||
|
||||
void StopGamepadMonitoring()
|
||||
{
|
||||
if (!gService) {
|
||||
return;
|
||||
}
|
||||
|
||||
gService->Shutdown();
|
||||
delete gService;
|
||||
gService = nullptr;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
@ -48,10 +48,6 @@ if CONFIG['MOZ_GAMEPAD']:
|
|||
SOURCES += [
|
||||
'fallback/FallbackGamepad.cpp'
|
||||
]
|
||||
elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'cocoa':
|
||||
SOURCES += [
|
||||
'cocoa/CocoaGamepad.cpp'
|
||||
]
|
||||
elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'windows':
|
||||
SOURCES += [
|
||||
'windows/WindowsGamepad.cpp'
|
||||
|
|
|
|||
|
|
@ -29,10 +29,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
|
|||
LOCAL_INCLUDES += [
|
||||
'/dom/system/android',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/system/mac',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/system/windows',
|
||||
|
|
|
|||
|
|
@ -13,9 +13,6 @@ SOURCES += [
|
|||
'../VideoSegment.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
UNIFIED_SOURCES += ['../systemservices/OSXRunLoopSingleton.cpp']
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/caps',
|
||||
'/dom/base',
|
||||
|
|
|
|||
|
|
@ -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/. */
|
||||
|
||||
#include "OSXRunLoopSingleton.h"
|
||||
#include <mozilla/StaticMutex.h>
|
||||
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <CoreAudio/AudioHardware.h>
|
||||
#include <CoreAudio/HostTime.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
static bool gRunLoopSet = false;
|
||||
static mozilla::StaticMutex gMutex;
|
||||
|
||||
void mozilla_set_coreaudio_notification_runloop_if_needed()
|
||||
{
|
||||
mozilla::StaticMutexAutoLock lock(gMutex);
|
||||
if (gRunLoopSet) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* This is needed so that AudioUnit listeners get called on this thread, and
|
||||
* not the main thread. If we don't do that, they are not called, or a crash
|
||||
* occur, depending on the OSX version. */
|
||||
AudioObjectPropertyAddress runloop_address = {
|
||||
kAudioHardwarePropertyRunLoop,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
CFRunLoopRef run_loop = nullptr;
|
||||
|
||||
OSStatus r;
|
||||
r = AudioObjectSetPropertyData(kAudioObjectSystemObject,
|
||||
&runloop_address,
|
||||
0, NULL, sizeof(CFRunLoopRef), &run_loop);
|
||||
if (r != noErr) {
|
||||
NS_WARNING("Could not make global CoreAudio notifications use their own thread.");
|
||||
}
|
||||
|
||||
gRunLoopSet = true;
|
||||
}
|
||||
|
|
@ -1,25 +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 OSXRUNLOOPSINGLETON_H_
|
||||
#define OSXRUNLOOPSINGLETON_H_
|
||||
|
||||
#include <mozilla/Types.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This function tells CoreAudio to use its own thread for device change
|
||||
* notifications, and can be called from any thread without external
|
||||
* synchronization. */
|
||||
void MOZ_EXPORT
|
||||
mozilla_set_coreaudio_notification_runloop_if_needed();
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OSXRUNLOOPSINGLETON_H_
|
||||
|
|
@ -38,10 +38,6 @@ if CONFIG['OS_TARGET'] == 'Android':
|
|||
'OpenSLESProvider.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
SOURCES += ['OSXRunLoopSingleton.cpp']
|
||||
EXPORTS += ['OSXRunLoopSingleton.h']
|
||||
|
||||
if CONFIG['_MSC_VER']:
|
||||
DEFINES['__PRETTY_FUNCTION__'] = '__FUNCSIG__'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,56 +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 "mozilla/ModuleUtils.h"
|
||||
#include "nsIClassInfoImpl.h"
|
||||
|
||||
#include "OSXSpeechSynthesizerService.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
#define OSXSPEECHSYNTHESIZERSERVICE_CID \
|
||||
{0x914e73b4, 0x6337, 0x4bef, {0x97, 0xf3, 0x4d, 0x06, 0x9e, 0x05, 0x3a, 0x12}}
|
||||
|
||||
#define OSXSPEECHSYNTHESIZERSERVICE_CONTRACTID "@mozilla.org/synthsystem;1"
|
||||
|
||||
// Defines OSXSpeechSynthesizerServiceConstructor
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(OSXSpeechSynthesizerService,
|
||||
OSXSpeechSynthesizerService::GetInstanceForService)
|
||||
|
||||
// Defines kOSXSERVICE_CID
|
||||
NS_DEFINE_NAMED_CID(OSXSPEECHSYNTHESIZERSERVICE_CID);
|
||||
|
||||
static const mozilla::Module::CIDEntry kCIDs[] = {
|
||||
{ &kOSXSPEECHSYNTHESIZERSERVICE_CID, true, nullptr, OSXSpeechSynthesizerServiceConstructor },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module::ContractIDEntry kContracts[] = {
|
||||
{ OSXSPEECHSYNTHESIZERSERVICE_CONTRACTID, &kOSXSPEECHSYNTHESIZERSERVICE_CID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module::CategoryEntry kCategories[] = {
|
||||
{ "speech-synth-started", "OSX Speech Synth", OSXSPEECHSYNTHESIZERSERVICE_CONTRACTID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static void
|
||||
UnloadOSXSpeechSynthesizerModule()
|
||||
{
|
||||
OSXSpeechSynthesizerService::Shutdown();
|
||||
}
|
||||
|
||||
static const mozilla::Module kModule = {
|
||||
mozilla::Module::kVersion,
|
||||
kCIDs,
|
||||
kContracts,
|
||||
kCategories,
|
||||
nullptr,
|
||||
nullptr,
|
||||
UnloadOSXSpeechSynthesizerModule
|
||||
};
|
||||
|
||||
NSMODULE_DEFN(osxsynth) = &kModule;
|
||||
|
|
@ -1,43 +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 mozilla_dom_OsxSpeechSynthesizerService_h
|
||||
#define mozilla_dom_OsxSpeechSynthesizerService_h
|
||||
|
||||
#include "nsISpeechService.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class OSXSpeechSynthesizerService final : public nsISpeechService
|
||||
, public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSISPEECHSERVICE
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
bool Init();
|
||||
|
||||
static OSXSpeechSynthesizerService* GetInstance();
|
||||
static already_AddRefed<OSXSpeechSynthesizerService> GetInstanceForService();
|
||||
static void Shutdown();
|
||||
|
||||
private:
|
||||
OSXSpeechSynthesizerService();
|
||||
virtual ~OSXSpeechSynthesizerService();
|
||||
|
||||
bool RegisterVoices();
|
||||
|
||||
bool mInitialized;
|
||||
static mozilla::StaticRefPtr<OSXSpeechSynthesizerService> sSingleton;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
|
@ -1,498 +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 "nsISupports.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsObjCExceptions.h"
|
||||
#include "nsCocoaUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/dom/nsSynthVoiceRegistry.h"
|
||||
#include "mozilla/dom/nsSpeechTask.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "OSXSpeechSynthesizerService.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
// We can escape the default delimiters ("[[" and "]]") by temporarily
|
||||
// changing the delimiters just before they appear, and changing them back
|
||||
// just after.
|
||||
#define DLIM_ESCAPE_START "[[dlim (( ))]]"
|
||||
#define DLIM_ESCAPE_END "((dlim [[ ]]))"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
class SpeechTaskCallback final : public nsISpeechTaskCallback
|
||||
{
|
||||
public:
|
||||
SpeechTaskCallback(nsISpeechTask* aTask,
|
||||
NSSpeechSynthesizer* aSynth,
|
||||
const nsTArray<size_t>& aOffsets)
|
||||
: mTask(aTask)
|
||||
, mSpeechSynthesizer(aSynth)
|
||||
, mOffsets(aOffsets)
|
||||
{
|
||||
mStartingTime = TimeStamp::Now();
|
||||
}
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(SpeechTaskCallback, nsISpeechTaskCallback)
|
||||
|
||||
NS_DECL_NSISPEECHTASKCALLBACK
|
||||
|
||||
void OnWillSpeakWord(uint32_t aIndex);
|
||||
void OnError(uint32_t aIndex);
|
||||
void OnDidFinishSpeaking();
|
||||
|
||||
private:
|
||||
virtual ~SpeechTaskCallback()
|
||||
{
|
||||
[mSpeechSynthesizer release];
|
||||
}
|
||||
|
||||
float GetTimeDurationFromStart();
|
||||
|
||||
nsCOMPtr<nsISpeechTask> mTask;
|
||||
NSSpeechSynthesizer* mSpeechSynthesizer;
|
||||
TimeStamp mStartingTime;
|
||||
uint32_t mCurrentIndex;
|
||||
nsTArray<size_t> mOffsets;
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(SpeechTaskCallback, mTask);
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SpeechTaskCallback)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISpeechTaskCallback)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpeechTaskCallback)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(SpeechTaskCallback)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(SpeechTaskCallback)
|
||||
|
||||
NS_IMETHODIMP
|
||||
SpeechTaskCallback::OnCancel()
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
[mSpeechSynthesizer stopSpeaking];
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SpeechTaskCallback::OnPause()
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
[mSpeechSynthesizer pauseSpeakingAtBoundary:NSSpeechImmediateBoundary];
|
||||
if (!mTask) {
|
||||
// When calling pause() on child porcess, it may not receive end event
|
||||
// from chrome process yet.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mTask->DispatchPause(GetTimeDurationFromStart(), mCurrentIndex);
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SpeechTaskCallback::OnResume()
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
[mSpeechSynthesizer continueSpeaking];
|
||||
if (!mTask) {
|
||||
// When calling resume() on child porcess, it may not receive end event
|
||||
// from chrome process yet.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mTask->DispatchResume(GetTimeDurationFromStart(), mCurrentIndex);
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SpeechTaskCallback::OnVolumeChanged(float aVolume)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
[mSpeechSynthesizer setObject:[NSNumber numberWithFloat:aVolume]
|
||||
forProperty:NSSpeechVolumeProperty error:nil];
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
float
|
||||
SpeechTaskCallback::GetTimeDurationFromStart()
|
||||
{
|
||||
TimeDuration duration = TimeStamp::Now() - mStartingTime;
|
||||
return duration.ToMilliseconds();
|
||||
}
|
||||
|
||||
void
|
||||
SpeechTaskCallback::OnWillSpeakWord(uint32_t aIndex)
|
||||
{
|
||||
mCurrentIndex = aIndex < mOffsets.Length() ? mOffsets[aIndex] : mCurrentIndex;
|
||||
if (!mTask) {
|
||||
return;
|
||||
}
|
||||
mTask->DispatchBoundary(NS_LITERAL_STRING("word"),
|
||||
GetTimeDurationFromStart(), mCurrentIndex);
|
||||
}
|
||||
|
||||
void
|
||||
SpeechTaskCallback::OnError(uint32_t aIndex)
|
||||
{
|
||||
if (!mTask) {
|
||||
return;
|
||||
}
|
||||
mTask->DispatchError(GetTimeDurationFromStart(), aIndex);
|
||||
}
|
||||
|
||||
void
|
||||
SpeechTaskCallback::OnDidFinishSpeaking()
|
||||
{
|
||||
mTask->DispatchEnd(GetTimeDurationFromStart(), mCurrentIndex);
|
||||
// no longer needed
|
||||
[mSpeechSynthesizer setDelegate:nil];
|
||||
mTask = nullptr;
|
||||
}
|
||||
|
||||
@interface SpeechDelegate : NSObject<NSSpeechSynthesizerDelegate>
|
||||
{
|
||||
@private
|
||||
SpeechTaskCallback* mCallback;
|
||||
}
|
||||
|
||||
- (id)initWithCallback:(SpeechTaskCallback*)aCallback;
|
||||
@end
|
||||
|
||||
@implementation SpeechDelegate
|
||||
- (id)initWithCallback:(SpeechTaskCallback*)aCallback
|
||||
{
|
||||
[super init];
|
||||
mCallback = aCallback;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)speechSynthesizer:(NSSpeechSynthesizer *)aSender
|
||||
willSpeakWord:(NSRange)aRange ofString:(NSString*)aString
|
||||
{
|
||||
mCallback->OnWillSpeakWord(aRange.location);
|
||||
}
|
||||
|
||||
- (void)speechSynthesizer:(NSSpeechSynthesizer *)aSender
|
||||
didFinishSpeaking:(BOOL)aFinishedSpeaking
|
||||
{
|
||||
mCallback->OnDidFinishSpeaking();
|
||||
}
|
||||
|
||||
- (void)speechSynthesizer:(NSSpeechSynthesizer*)aSender
|
||||
didEncounterErrorAtIndex:(NSUInteger)aCharacterIndex
|
||||
ofString:(NSString*)aString
|
||||
message:(NSString*)aMessage
|
||||
{
|
||||
mCallback->OnError(aCharacterIndex);
|
||||
}
|
||||
@end
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct OSXVoice
|
||||
{
|
||||
OSXVoice() : mIsDefault(false)
|
||||
{
|
||||
}
|
||||
|
||||
nsString mUri;
|
||||
nsString mName;
|
||||
nsString mLocale;
|
||||
bool mIsDefault;
|
||||
};
|
||||
|
||||
class RegisterVoicesRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
RegisterVoicesRunnable(OSXSpeechSynthesizerService* aSpeechService,
|
||||
nsTArray<OSXVoice>& aList)
|
||||
: mSpeechService(aSpeechService)
|
||||
, mVoices(aList)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
private:
|
||||
~RegisterVoicesRunnable()
|
||||
{
|
||||
}
|
||||
|
||||
// This runnable always use sync mode. It is unnecesarry to reference object
|
||||
OSXSpeechSynthesizerService* mSpeechService;
|
||||
nsTArray<OSXVoice>& mVoices;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
RegisterVoicesRunnable::Run()
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsISynthVoiceRegistry> registry =
|
||||
do_GetService(NS_SYNTHVOICEREGISTRY_CONTRACTID, &rv);
|
||||
if (!registry) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
for (OSXVoice voice : mVoices) {
|
||||
rv = registry->AddVoice(mSpeechService, voice.mUri, voice.mName, voice.mLocale, true, false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (voice.mIsDefault) {
|
||||
registry->SetDefaultVoice(voice.mUri, true);
|
||||
}
|
||||
}
|
||||
|
||||
registry->NotifyVoicesChanged();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class EnumVoicesRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
explicit EnumVoicesRunnable(OSXSpeechSynthesizerService* aSpeechService)
|
||||
: mSpeechService(aSpeechService)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
private:
|
||||
~EnumVoicesRunnable()
|
||||
{
|
||||
}
|
||||
|
||||
RefPtr<OSXSpeechSynthesizerService> mSpeechService;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
EnumVoicesRunnable::Run()
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
AutoTArray<OSXVoice, 64> list;
|
||||
|
||||
NSArray* voices = [NSSpeechSynthesizer availableVoices];
|
||||
NSString* defaultVoice = [NSSpeechSynthesizer defaultVoice];
|
||||
|
||||
for (NSString* voice in voices) {
|
||||
OSXVoice item;
|
||||
|
||||
NSDictionary* attr = [NSSpeechSynthesizer attributesForVoice:voice];
|
||||
|
||||
nsAutoString identifier;
|
||||
nsCocoaUtils::GetStringForNSString([attr objectForKey:NSVoiceIdentifier],
|
||||
identifier);
|
||||
|
||||
nsCocoaUtils::GetStringForNSString([attr objectForKey:NSVoiceName], item.mName);
|
||||
|
||||
nsCocoaUtils::GetStringForNSString(
|
||||
[attr objectForKey:NSVoiceLocaleIdentifier], item.mLocale);
|
||||
item.mLocale.ReplaceChar('_', '-');
|
||||
|
||||
item.mUri.AssignLiteral("urn:moz-tts:osx:");
|
||||
item.mUri.Append(identifier);
|
||||
|
||||
if ([voice isEqualToString:defaultVoice]) {
|
||||
item.mIsDefault = true;
|
||||
}
|
||||
|
||||
list.AppendElement(item);
|
||||
}
|
||||
|
||||
RefPtr<RegisterVoicesRunnable> runnable = new RegisterVoicesRunnable(mSpeechService, list);
|
||||
NS_DispatchToMainThread(runnable, NS_DISPATCH_SYNC);
|
||||
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
StaticRefPtr<OSXSpeechSynthesizerService> OSXSpeechSynthesizerService::sSingleton;
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(OSXSpeechSynthesizerService)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISpeechService)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpeechService)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_ADDREF(OSXSpeechSynthesizerService)
|
||||
NS_IMPL_RELEASE(OSXSpeechSynthesizerService)
|
||||
|
||||
OSXSpeechSynthesizerService::OSXSpeechSynthesizerService()
|
||||
: mInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
OSXSpeechSynthesizerService::~OSXSpeechSynthesizerService()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
OSXSpeechSynthesizerService::Init()
|
||||
{
|
||||
if (Preferences::GetBool("media.webspeech.synth.test") ||
|
||||
!Preferences::GetBool("media.webspeech.synth.enabled")) {
|
||||
// When test is enabled, we shouldn't add OS backend (Bug 1160844)
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
if (NS_FAILED(NS_NewNamedThread("SpeechWorker", getter_AddRefs(thread)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get all the voices and register in the SynthVoiceRegistry
|
||||
nsCOMPtr<nsIRunnable> runnable = new EnumVoicesRunnable(this);
|
||||
thread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
|
||||
mInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
OSXSpeechSynthesizerService::Speak(const nsAString& aText,
|
||||
const nsAString& aUri,
|
||||
float aVolume,
|
||||
float aRate,
|
||||
float aPitch,
|
||||
nsISpeechTask* aTask)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
MOZ_ASSERT(StringBeginsWith(aUri, NS_LITERAL_STRING("urn:moz-tts:osx:")),
|
||||
"OSXSpeechSynthesizerService doesn't allow this voice URI");
|
||||
|
||||
NSSpeechSynthesizer* synth = [[NSSpeechSynthesizer alloc] init];
|
||||
// strlen("urn:moz-tts:osx:") == 16
|
||||
NSString* identifier = nsCocoaUtils::ToNSString(Substring(aUri, 16));
|
||||
[synth setVoice:identifier];
|
||||
|
||||
// default rate is 180-220
|
||||
[synth setObject:[NSNumber numberWithInt:aRate * 200]
|
||||
forProperty:NSSpeechRateProperty error:nil];
|
||||
// volume allows 0.0-1.0
|
||||
[synth setObject:[NSNumber numberWithFloat:aVolume]
|
||||
forProperty:NSSpeechVolumeProperty error:nil];
|
||||
// Use default pitch value to calculate this
|
||||
NSNumber* defaultPitch =
|
||||
[synth objectForProperty:NSSpeechPitchBaseProperty error:nil];
|
||||
if (defaultPitch) {
|
||||
int newPitch = [defaultPitch intValue] * (aPitch / 2 + 0.5);
|
||||
[synth setObject:[NSNumber numberWithInt:newPitch]
|
||||
forProperty:NSSpeechPitchBaseProperty error:nil];
|
||||
}
|
||||
|
||||
nsAutoString escapedText;
|
||||
// We need to map the the offsets from the given text to the escaped text.
|
||||
// The index of the offsets array is the position in the escaped text,
|
||||
// the element value is the position in the user-supplied text.
|
||||
nsTArray<size_t> offsets;
|
||||
offsets.SetCapacity(aText.Length());
|
||||
|
||||
// This loop looks for occurances of "[[" or "]]", escapes them, and
|
||||
// populates the offsets array to supply a map to the original offsets.
|
||||
for (size_t i = 0; i < aText.Length(); i++) {
|
||||
if (aText.Length() > i + 1 &&
|
||||
((aText[i] == ']' && aText[i+1] == ']') ||
|
||||
(aText[i] == '[' && aText[i+1] == '['))) {
|
||||
escapedText.AppendLiteral(DLIM_ESCAPE_START);
|
||||
offsets.AppendElements(strlen(DLIM_ESCAPE_START));
|
||||
escapedText.Append(aText[i]);
|
||||
offsets.AppendElement(i);
|
||||
escapedText.Append(aText[++i]);
|
||||
offsets.AppendElement(i);
|
||||
escapedText.AppendLiteral(DLIM_ESCAPE_END);
|
||||
offsets.AppendElements(strlen(DLIM_ESCAPE_END));
|
||||
} else {
|
||||
escapedText.Append(aText[i]);
|
||||
offsets.AppendElement(i);
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<SpeechTaskCallback> callback = new SpeechTaskCallback(aTask, synth, offsets);
|
||||
nsresult rv = aTask->Setup(callback, 0, 0, 0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
SpeechDelegate* delegate = [[SpeechDelegate alloc] initWithCallback:callback];
|
||||
[synth setDelegate:delegate];
|
||||
[delegate release ];
|
||||
|
||||
NSString* text = nsCocoaUtils::ToNSString(escapedText);
|
||||
BOOL success = [synth startSpeakingString:text];
|
||||
NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
|
||||
|
||||
aTask->DispatchStart();
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
OSXSpeechSynthesizerService::GetServiceType(SpeechServiceType* aServiceType)
|
||||
{
|
||||
*aServiceType = nsISpeechService::SERVICETYPE_INDIRECT_AUDIO;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
OSXSpeechSynthesizerService::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
OSXSpeechSynthesizerService*
|
||||
OSXSpeechSynthesizerService::GetInstance()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!sSingleton) {
|
||||
RefPtr<OSXSpeechSynthesizerService> speechService =
|
||||
new OSXSpeechSynthesizerService();
|
||||
if (speechService->Init()) {
|
||||
sSingleton = speechService;
|
||||
}
|
||||
}
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
already_AddRefed<OSXSpeechSynthesizerService>
|
||||
OSXSpeechSynthesizerService::GetInstanceForService()
|
||||
{
|
||||
RefPtr<OSXSpeechSynthesizerService> speechService = GetInstance();
|
||||
return speechService.forget();
|
||||
}
|
||||
|
||||
void
|
||||
OSXSpeechSynthesizerService::Shutdown()
|
||||
{
|
||||
if (!sSingleton) {
|
||||
return;
|
||||
}
|
||||
sSingleton = nullptr;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
@ -1,11 +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 += [
|
||||
'OSXSpeechSynthesizerModule.cpp',
|
||||
'OSXSpeechSynthesizerService.mm'
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
|
@ -32,9 +32,6 @@ SOURCES += [
|
|||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
DIRS += ['windows']
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
DIRS += ['cocoa']
|
||||
|
||||
if CONFIG['MOZ_SYNTH_SPEECHD']:
|
||||
DIRS += ['speechd']
|
||||
|
||||
|
|
|
|||
|
|
@ -112,6 +112,6 @@ TEST_DIRS += [
|
|||
'imptests',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'cocoa', 'windows', 'android'):
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'windows', 'android'):
|
||||
TEST_DIRS += ['plugins/test']
|
||||
|
||||
|
|
|
|||
|
|
@ -56,11 +56,6 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
|||
'nsPluginNativeWindowWin.cpp',
|
||||
'nsPluginsDirWin.cpp',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
SOURCES += [
|
||||
'nsPluginNativeWindow.cpp',
|
||||
'nsPluginsDirDarwin.cpp',
|
||||
]
|
||||
else:
|
||||
SOURCES += [
|
||||
'nsPluginsDirUnix.cpp',
|
||||
|
|
@ -82,7 +77,6 @@ LOCAL_INCLUDES += [
|
|||
'/layout/xul',
|
||||
'/netwerk/base',
|
||||
'/widget',
|
||||
'/widget/cocoa',
|
||||
'/xpcom/base',
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,572 +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/. */
|
||||
|
||||
/*
|
||||
nsPluginsDirDarwin.cpp
|
||||
|
||||
Mac OS X implementation of the nsPluginsDir/nsPluginsFile classes.
|
||||
|
||||
by Patrick C. Beard.
|
||||
*/
|
||||
|
||||
#include "GeckoChildProcessHost.h"
|
||||
#include "base/process_util.h"
|
||||
|
||||
#include "prlink.h"
|
||||
#include "prnetdb.h"
|
||||
#include "nsXPCOM.h"
|
||||
|
||||
#include "nsPluginsDir.h"
|
||||
#include "nsNPAPIPlugin.h"
|
||||
#include "nsPluginsDirUtils.h"
|
||||
|
||||
#include "nsILocalFileMac.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "nsCocoaFeatures.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <Carbon/Carbon.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/fat.h>
|
||||
|
||||
typedef NS_NPAPIPLUGIN_CALLBACK(const char *, NP_GETMIMEDESCRIPTION) ();
|
||||
typedef NS_NPAPIPLUGIN_CALLBACK(OSErr, BP_GETSUPPORTEDMIMETYPES) (BPSupportedMIMETypes *mimeInfo, UInt32 flags);
|
||||
|
||||
/*
|
||||
** Returns a CFBundleRef if the path refers to a Mac OS X bundle directory.
|
||||
** The caller is responsible for calling CFRelease() to deallocate.
|
||||
*/
|
||||
static CFBundleRef getPluginBundle(const char* path)
|
||||
{
|
||||
CFBundleRef bundle = nullptr;
|
||||
CFStringRef pathRef = ::CFStringCreateWithCString(nullptr, path,
|
||||
kCFStringEncodingUTF8);
|
||||
if (pathRef) {
|
||||
CFURLRef bundleURL = ::CFURLCreateWithFileSystemPath(nullptr, pathRef,
|
||||
kCFURLPOSIXPathStyle,
|
||||
true);
|
||||
if (bundleURL) {
|
||||
bundle = ::CFBundleCreate(nullptr, bundleURL);
|
||||
::CFRelease(bundleURL);
|
||||
}
|
||||
::CFRelease(pathRef);
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
static nsresult toCFURLRef(nsIFile* file, CFURLRef& outURL)
|
||||
{
|
||||
nsCOMPtr<nsILocalFileMac> lfm = do_QueryInterface(file);
|
||||
if (!lfm)
|
||||
return NS_ERROR_FAILURE;
|
||||
CFURLRef url;
|
||||
nsresult rv = lfm->GetCFURL(&url);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
outURL = url;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool nsPluginsDir::IsPluginFile(nsIFile* file)
|
||||
{
|
||||
nsCString fileName;
|
||||
file->GetNativeLeafName(fileName);
|
||||
/*
|
||||
* Don't load the VDP fake plugin, to avoid tripping a bad bug in OS X
|
||||
* 10.5.3 (see bug 436575).
|
||||
*/
|
||||
if (!strcmp(fileName.get(), "VerifiedDownloadPlugin.plugin")) {
|
||||
NS_WARNING("Preventing load of VerifiedDownloadPlugin.plugin (see bug 436575)");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Caller is responsible for freeing returned buffer.
|
||||
static char* CFStringRefToUTF8Buffer(CFStringRef cfString)
|
||||
{
|
||||
const char* buffer = ::CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8);
|
||||
if (buffer) {
|
||||
return PL_strdup(buffer);
|
||||
}
|
||||
|
||||
int bufferLength =
|
||||
::CFStringGetMaximumSizeForEncoding(::CFStringGetLength(cfString),
|
||||
kCFStringEncodingUTF8) + 1;
|
||||
char* newBuffer = static_cast<char*>(moz_xmalloc(bufferLength));
|
||||
if (!newBuffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!::CFStringGetCString(cfString, newBuffer, bufferLength,
|
||||
kCFStringEncodingUTF8)) {
|
||||
free(newBuffer);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
newBuffer = static_cast<char*>(moz_xrealloc(newBuffer,
|
||||
strlen(newBuffer) + 1));
|
||||
return newBuffer;
|
||||
}
|
||||
|
||||
class AutoCFTypeObject {
|
||||
public:
|
||||
explicit AutoCFTypeObject(CFTypeRef aObject)
|
||||
{
|
||||
mObject = aObject;
|
||||
}
|
||||
~AutoCFTypeObject()
|
||||
{
|
||||
::CFRelease(mObject);
|
||||
}
|
||||
private:
|
||||
CFTypeRef mObject;
|
||||
};
|
||||
|
||||
static Boolean MimeTypeEnabled(CFDictionaryRef mimeDict) {
|
||||
if (!mimeDict) {
|
||||
return true;
|
||||
}
|
||||
|
||||
CFTypeRef value;
|
||||
if (::CFDictionaryGetValueIfPresent(mimeDict, CFSTR("WebPluginTypeEnabled"), &value)) {
|
||||
if (value && ::CFGetTypeID(value) == ::CFBooleanGetTypeID()) {
|
||||
return ::CFBooleanGetValue(static_cast<CFBooleanRef>(value));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static CFDictionaryRef ParsePlistForMIMETypesFilename(CFBundleRef bundle)
|
||||
{
|
||||
CFTypeRef mimeFileName = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginMIMETypesFilename"));
|
||||
if (!mimeFileName || ::CFGetTypeID(mimeFileName) != ::CFStringGetTypeID()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FSRef homeDir;
|
||||
if (::FSFindFolder(kUserDomain, kCurrentUserFolderType, kDontCreateFolder, &homeDir) != noErr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CFURLRef userDirURL = ::CFURLCreateFromFSRef(kCFAllocatorDefault, &homeDir);
|
||||
if (!userDirURL) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AutoCFTypeObject userDirURLAutorelease(userDirURL);
|
||||
CFStringRef mimeFilePath = ::CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("Library/Preferences/%@"), static_cast<CFStringRef>(mimeFileName));
|
||||
if (!mimeFilePath) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AutoCFTypeObject mimeFilePathAutorelease(mimeFilePath);
|
||||
CFURLRef mimeFileURL = ::CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorDefault, mimeFilePath, kCFURLPOSIXPathStyle, false, userDirURL);
|
||||
if (!mimeFileURL) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AutoCFTypeObject mimeFileURLAutorelease(mimeFileURL);
|
||||
SInt32 errorCode = 0;
|
||||
CFDataRef mimeFileData = nullptr;
|
||||
Boolean result = ::CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, mimeFileURL, &mimeFileData, nullptr, nullptr, &errorCode);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AutoCFTypeObject mimeFileDataAutorelease(mimeFileData);
|
||||
if (errorCode != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CFPropertyListRef propertyList = ::CFPropertyListCreateFromXMLData(kCFAllocatorDefault, mimeFileData, kCFPropertyListImmutable, nullptr);
|
||||
if (!propertyList) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AutoCFTypeObject propertyListAutorelease(propertyList);
|
||||
if (::CFGetTypeID(propertyList) != ::CFDictionaryGetTypeID()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CFTypeRef mimeTypes = ::CFDictionaryGetValue(static_cast<CFDictionaryRef>(propertyList), CFSTR("WebPluginMIMETypes"));
|
||||
if (!mimeTypes || ::CFGetTypeID(mimeTypes) != ::CFDictionaryGetTypeID() || ::CFDictionaryGetCount(static_cast<CFDictionaryRef>(mimeTypes)) == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return static_cast<CFDictionaryRef>(::CFRetain(mimeTypes));
|
||||
}
|
||||
|
||||
static void ParsePlistPluginInfo(nsPluginInfo& info, CFBundleRef bundle)
|
||||
{
|
||||
CFDictionaryRef mimeDict = ParsePlistForMIMETypesFilename(bundle);
|
||||
|
||||
if (!mimeDict) {
|
||||
CFTypeRef mimeTypes = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginMIMETypes"));
|
||||
if (!mimeTypes || ::CFGetTypeID(mimeTypes) != ::CFDictionaryGetTypeID() || ::CFDictionaryGetCount(static_cast<CFDictionaryRef>(mimeTypes)) == 0)
|
||||
return;
|
||||
mimeDict = static_cast<CFDictionaryRef>(::CFRetain(mimeTypes));
|
||||
}
|
||||
|
||||
AutoCFTypeObject mimeDictAutorelease(mimeDict);
|
||||
int mimeDictKeyCount = ::CFDictionaryGetCount(mimeDict);
|
||||
|
||||
// Allocate memory for mime data
|
||||
int mimeDataArraySize = mimeDictKeyCount * sizeof(char*);
|
||||
info.fMimeTypeArray = static_cast<char**>(moz_xmalloc(mimeDataArraySize));
|
||||
if (!info.fMimeTypeArray)
|
||||
return;
|
||||
memset(info.fMimeTypeArray, 0, mimeDataArraySize);
|
||||
info.fExtensionArray = static_cast<char**>(moz_xmalloc(mimeDataArraySize));
|
||||
if (!info.fExtensionArray)
|
||||
return;
|
||||
memset(info.fExtensionArray, 0, mimeDataArraySize);
|
||||
info.fMimeDescriptionArray = static_cast<char**>(moz_xmalloc(mimeDataArraySize));
|
||||
if (!info.fMimeDescriptionArray)
|
||||
return;
|
||||
memset(info.fMimeDescriptionArray, 0, mimeDataArraySize);
|
||||
|
||||
// Allocate memory for mime dictionary keys and values
|
||||
mozilla::UniquePtr<CFTypeRef[]> keys(new CFTypeRef[mimeDictKeyCount]);
|
||||
if (!keys)
|
||||
return;
|
||||
mozilla::UniquePtr<CFTypeRef[]> values(new CFTypeRef[mimeDictKeyCount]);
|
||||
if (!values)
|
||||
return;
|
||||
|
||||
info.fVariantCount = 0;
|
||||
|
||||
::CFDictionaryGetKeysAndValues(mimeDict, keys.get(), values.get());
|
||||
for (int i = 0; i < mimeDictKeyCount; i++) {
|
||||
CFTypeRef mimeString = keys[i];
|
||||
if (!mimeString || ::CFGetTypeID(mimeString) != ::CFStringGetTypeID()) {
|
||||
continue;
|
||||
}
|
||||
CFTypeRef mimeDict = values[i];
|
||||
if (mimeDict && ::CFGetTypeID(mimeDict) == ::CFDictionaryGetTypeID()) {
|
||||
if (!MimeTypeEnabled(static_cast<CFDictionaryRef>(mimeDict))) {
|
||||
continue;
|
||||
}
|
||||
info.fMimeTypeArray[info.fVariantCount] = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(mimeString));
|
||||
if (!info.fMimeTypeArray[info.fVariantCount]) {
|
||||
continue;
|
||||
}
|
||||
CFTypeRef extensions = ::CFDictionaryGetValue(static_cast<CFDictionaryRef>(mimeDict), CFSTR("WebPluginExtensions"));
|
||||
if (extensions && ::CFGetTypeID(extensions) == ::CFArrayGetTypeID()) {
|
||||
int extensionCount = ::CFArrayGetCount(static_cast<CFArrayRef>(extensions));
|
||||
CFMutableStringRef extensionList = ::CFStringCreateMutable(kCFAllocatorDefault, 0);
|
||||
for (int j = 0; j < extensionCount; j++) {
|
||||
CFTypeRef extension = ::CFArrayGetValueAtIndex(static_cast<CFArrayRef>(extensions), j);
|
||||
if (extension && ::CFGetTypeID(extension) == ::CFStringGetTypeID()) {
|
||||
if (j > 0)
|
||||
::CFStringAppend(extensionList, CFSTR(","));
|
||||
::CFStringAppend(static_cast<CFMutableStringRef>(extensionList), static_cast<CFStringRef>(extension));
|
||||
}
|
||||
}
|
||||
info.fExtensionArray[info.fVariantCount] = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(extensionList));
|
||||
::CFRelease(extensionList);
|
||||
}
|
||||
CFTypeRef description = ::CFDictionaryGetValue(static_cast<CFDictionaryRef>(mimeDict), CFSTR("WebPluginTypeDescription"));
|
||||
if (description && ::CFGetTypeID(description) == ::CFStringGetTypeID())
|
||||
info.fMimeDescriptionArray[info.fVariantCount] = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(description));
|
||||
}
|
||||
info.fVariantCount++;
|
||||
}
|
||||
}
|
||||
|
||||
nsPluginFile::nsPluginFile(nsIFile *spec)
|
||||
: mPlugin(spec)
|
||||
{
|
||||
}
|
||||
|
||||
nsPluginFile::~nsPluginFile() {}
|
||||
|
||||
nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary)
|
||||
{
|
||||
if (!mPlugin)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
// 64-bit NSPR does not (yet) support bundles. So in 64-bit builds we need
|
||||
// (for now) to load the bundle's executable. However this can cause
|
||||
// problems: CFBundleCreate() doesn't run the bundle's executable's
|
||||
// initialization code, while NSAddImage() and dlopen() do run it. So using
|
||||
// NSPR's dyld loading mechanisms here (NSAddImage() or dlopen()) can cause
|
||||
// a bundle's initialization code to run earlier than expected, and lead to
|
||||
// crashes. See bug 577967.
|
||||
#ifdef __LP64__
|
||||
char executablePath[PATH_MAX];
|
||||
executablePath[0] = '\0';
|
||||
nsAutoCString bundlePath;
|
||||
mPlugin->GetNativePath(bundlePath);
|
||||
CFStringRef pathRef = ::CFStringCreateWithCString(nullptr, bundlePath.get(),
|
||||
kCFStringEncodingUTF8);
|
||||
if (pathRef) {
|
||||
CFURLRef bundleURL = ::CFURLCreateWithFileSystemPath(nullptr, pathRef,
|
||||
kCFURLPOSIXPathStyle,
|
||||
true);
|
||||
if (bundleURL) {
|
||||
CFBundleRef bundle = ::CFBundleCreate(nullptr, bundleURL);
|
||||
if (bundle) {
|
||||
CFURLRef executableURL = ::CFBundleCopyExecutableURL(bundle);
|
||||
if (executableURL) {
|
||||
if (!::CFURLGetFileSystemRepresentation(executableURL, true, (UInt8*)&executablePath, PATH_MAX))
|
||||
executablePath[0] = '\0';
|
||||
::CFRelease(executableURL);
|
||||
}
|
||||
::CFRelease(bundle);
|
||||
}
|
||||
::CFRelease(bundleURL);
|
||||
}
|
||||
::CFRelease(pathRef);
|
||||
}
|
||||
#else
|
||||
nsAutoCString bundlePath;
|
||||
mPlugin->GetNativePath(bundlePath);
|
||||
const char *executablePath = bundlePath.get();
|
||||
#endif
|
||||
|
||||
*outLibrary = PR_LoadLibrary(executablePath);
|
||||
pLibrary = *outLibrary;
|
||||
if (!pLibrary) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("[loaded plugin %s]\n", bundlePath.get());
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static char* p2cstrdup(StringPtr pstr)
|
||||
{
|
||||
int len = pstr[0];
|
||||
char* cstr = static_cast<char*>(moz_xmalloc(len + 1));
|
||||
if (cstr) {
|
||||
memmove(cstr, pstr + 1, len);
|
||||
cstr[len] = '\0';
|
||||
}
|
||||
return cstr;
|
||||
}
|
||||
|
||||
static char* GetNextPluginStringFromHandle(Handle h, short *index)
|
||||
{
|
||||
char *ret = p2cstrdup((unsigned char*)(*h + *index));
|
||||
*index += (ret ? strlen(ret) : 0) + 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool IsCompatibleArch(nsIFile *file)
|
||||
{
|
||||
CFURLRef pluginURL = nullptr;
|
||||
if (NS_FAILED(toCFURLRef(file, pluginURL)))
|
||||
return false;
|
||||
|
||||
bool isPluginFile = false;
|
||||
|
||||
CFBundleRef pluginBundle = ::CFBundleCreate(kCFAllocatorDefault, pluginURL);
|
||||
if (pluginBundle) {
|
||||
UInt32 packageType, packageCreator;
|
||||
::CFBundleGetPackageInfo(pluginBundle, &packageType, &packageCreator);
|
||||
if (packageType == 'BRPL' || packageType == 'IEPL' || packageType == 'NSPL') {
|
||||
// Get path to plugin as a C string.
|
||||
char executablePath[PATH_MAX];
|
||||
executablePath[0] = '\0';
|
||||
if (!::CFURLGetFileSystemRepresentation(pluginURL, true, (UInt8*)&executablePath, PATH_MAX)) {
|
||||
executablePath[0] = '\0';
|
||||
}
|
||||
|
||||
uint32_t pluginLibArchitectures;
|
||||
nsresult rv = mozilla::ipc::GeckoChildProcessHost::GetArchitecturesForBinary(executablePath, &pluginLibArchitectures);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t supportedArchitectures =
|
||||
#ifdef __LP64__
|
||||
mozilla::ipc::GeckoChildProcessHost::GetSupportedArchitecturesForProcessType(GeckoProcessType_Plugin);
|
||||
#else
|
||||
base::GetCurrentProcessArchitecture();
|
||||
#endif
|
||||
|
||||
// Consider the plugin architecture valid if there is any overlap in the masks.
|
||||
isPluginFile = !!(supportedArchitectures & pluginLibArchitectures);
|
||||
}
|
||||
::CFRelease(pluginBundle);
|
||||
}
|
||||
|
||||
::CFRelease(pluginURL);
|
||||
return isPluginFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains all of the information currently available for this plugin.
|
||||
*/
|
||||
nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary)
|
||||
{
|
||||
*outLibrary = nullptr;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (!IsCompatibleArch(mPlugin)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// clear out the info, except for the first field.
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
// Try to get a bundle reference.
|
||||
nsAutoCString path;
|
||||
if (NS_FAILED(rv = mPlugin->GetNativePath(path)))
|
||||
return rv;
|
||||
CFBundleRef bundle = getPluginBundle(path.get());
|
||||
|
||||
// fill in full path
|
||||
info.fFullPath = PL_strdup(path.get());
|
||||
|
||||
// fill in file name
|
||||
nsAutoCString fileName;
|
||||
if (NS_FAILED(rv = mPlugin->GetNativeLeafName(fileName)))
|
||||
return rv;
|
||||
info.fFileName = PL_strdup(fileName.get());
|
||||
|
||||
// Get fName
|
||||
if (bundle) {
|
||||
CFTypeRef name = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginName"));
|
||||
if (name && ::CFGetTypeID(name) == ::CFStringGetTypeID())
|
||||
info.fName = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(name));
|
||||
}
|
||||
|
||||
// Get fDescription
|
||||
if (bundle) {
|
||||
CFTypeRef description = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("WebPluginDescription"));
|
||||
if (description && ::CFGetTypeID(description) == ::CFStringGetTypeID())
|
||||
info.fDescription = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(description));
|
||||
}
|
||||
|
||||
// Get fVersion
|
||||
if (bundle) {
|
||||
// Look for the release version first
|
||||
CFTypeRef version = ::CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString"));
|
||||
if (!version) // try the build version
|
||||
version = ::CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleVersionKey);
|
||||
if (version && ::CFGetTypeID(version) == ::CFStringGetTypeID())
|
||||
info.fVersion = CFStringRefToUTF8Buffer(static_cast<CFStringRef>(version));
|
||||
}
|
||||
|
||||
// The last thing we need to do is get MIME data
|
||||
// fVariantCount, fMimeTypeArray, fExtensionArray, fMimeDescriptionArray
|
||||
|
||||
// First look for data in a bundle plist
|
||||
if (bundle) {
|
||||
ParsePlistPluginInfo(info, bundle);
|
||||
::CFRelease(bundle);
|
||||
if (info.fVariantCount > 0)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Don't load "fbplugin" or any plugins whose name starts with "fbplugin_"
|
||||
// (Facebook plugins) if we're running on OS X 10.10 (Yosemite) or later.
|
||||
// A "fbplugin" file crashes on load, in the call to LoadPlugin() below.
|
||||
// See bug 1086977.
|
||||
if (nsCocoaFeatures::OnYosemiteOrLater()) {
|
||||
if (fileName.EqualsLiteral("fbplugin") ||
|
||||
StringBeginsWith(fileName, NS_LITERAL_CSTRING("fbplugin_"))) {
|
||||
nsAutoCString msg;
|
||||
msg.AppendPrintf("Preventing load of %s (see bug 1086977)",
|
||||
fileName.get());
|
||||
NS_WARNING(msg.get());
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// It's possible that our plugin has 2 entry points that'll give us mime type
|
||||
// info. Quicktime does this to get around the need of having admin rights to
|
||||
// change mime info in the resource fork. We need to use this info instead of
|
||||
// the resource. See bug 113464.
|
||||
|
||||
// Sadly we have to load the library for this to work.
|
||||
rv = LoadPlugin(outLibrary);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// Try to get data from NP_GetMIMEDescription
|
||||
if (pLibrary) {
|
||||
NP_GETMIMEDESCRIPTION pfnGetMimeDesc = (NP_GETMIMEDESCRIPTION)PR_FindFunctionSymbol(pLibrary, NP_GETMIMEDESCRIPTION_NAME);
|
||||
if (pfnGetMimeDesc)
|
||||
ParsePluginMimeDescription(pfnGetMimeDesc(), info);
|
||||
if (info.fVariantCount)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We'll fill this in using BP_GetSupportedMIMETypes and/or resource fork data
|
||||
BPSupportedMIMETypes mi = {kBPSupportedMIMETypesStructVers_1, nullptr, nullptr};
|
||||
|
||||
// Try to get data from BP_GetSupportedMIMETypes
|
||||
if (pLibrary) {
|
||||
BP_GETSUPPORTEDMIMETYPES pfnMime = (BP_GETSUPPORTEDMIMETYPES)PR_FindFunctionSymbol(pLibrary, "BP_GetSupportedMIMETypes");
|
||||
if (pfnMime && noErr == pfnMime(&mi, 0) && mi.typeStrings) {
|
||||
info.fVariantCount = (**(short**)mi.typeStrings) / 2;
|
||||
::HLock(mi.typeStrings);
|
||||
if (mi.infoStrings) // it's possible some plugins have infoStrings missing
|
||||
::HLock(mi.infoStrings);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the info struct based on the data in the BPSupportedMIMETypes struct
|
||||
int variantCount = info.fVariantCount;
|
||||
info.fMimeTypeArray = static_cast<char**>(moz_xmalloc(variantCount * sizeof(char*)));
|
||||
if (!info.fMimeTypeArray)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
info.fExtensionArray = static_cast<char**>(moz_xmalloc(variantCount * sizeof(char*)));
|
||||
if (!info.fExtensionArray)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
if (mi.infoStrings) {
|
||||
info.fMimeDescriptionArray = static_cast<char**>(moz_xmalloc(variantCount * sizeof(char*)));
|
||||
if (!info.fMimeDescriptionArray)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
short mimeIndex = 2;
|
||||
short descriptionIndex = 2;
|
||||
for (int i = 0; i < variantCount; i++) {
|
||||
info.fMimeTypeArray[i] = GetNextPluginStringFromHandle(mi.typeStrings, &mimeIndex);
|
||||
info.fExtensionArray[i] = GetNextPluginStringFromHandle(mi.typeStrings, &mimeIndex);
|
||||
if (mi.infoStrings)
|
||||
info.fMimeDescriptionArray[i] = GetNextPluginStringFromHandle(mi.infoStrings, &descriptionIndex);
|
||||
}
|
||||
|
||||
::HUnlock(mi.typeStrings);
|
||||
::DisposeHandle(mi.typeStrings);
|
||||
if (mi.infoStrings) {
|
||||
::HUnlock(mi.infoStrings);
|
||||
::DisposeHandle(mi.infoStrings);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info)
|
||||
{
|
||||
free(info.fName);
|
||||
free(info.fDescription);
|
||||
int variantCount = info.fVariantCount;
|
||||
for (int i = 0; i < variantCount; i++) {
|
||||
free(info.fMimeTypeArray[i]);
|
||||
free(info.fExtensionArray[i]);
|
||||
free(info.fMimeDescriptionArray[i]);
|
||||
}
|
||||
free(info.fMimeTypeArray);
|
||||
free(info.fMimeDescriptionArray);
|
||||
free(info.fExtensionArray);
|
||||
free(info.fFileName);
|
||||
free(info.fFullPath);
|
||||
free(info.fVersion);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
@ -1,59 +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 "nsCOMPtr.h"
|
||||
#include "nsIGeolocationProvider.h"
|
||||
|
||||
|
||||
/*
|
||||
* The CoreLocationObjects class contains the CoreLocation objects
|
||||
* we'll need.
|
||||
*
|
||||
* Declaring them directly in CoreLocationLocationProvider
|
||||
* would require Objective-C++ syntax, which would contaminate all
|
||||
* files that include this header and require them to be Objective-C++
|
||||
* as well.
|
||||
*
|
||||
* The solution then is to forward-declare CoreLocationObjects here and
|
||||
* hold a pointer to it in CoreLocationLocationProvider, and only actually
|
||||
* define it in CoreLocationLocationProvider.mm, thus making it safe
|
||||
* for nsGeolocation.cpp, which is C++-only, to include this header.
|
||||
*/
|
||||
class CoreLocationObjects;
|
||||
class MLSFallback;
|
||||
|
||||
class CoreLocationLocationProvider
|
||||
: public nsIGeolocationProvider
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIGEOLOCATIONPROVIDER
|
||||
|
||||
CoreLocationLocationProvider();
|
||||
void NotifyError(uint16_t aErrorCode);
|
||||
void Update(nsIDOMGeoPosition* aSomewhere);
|
||||
void CreateMLSFallbackProvider();
|
||||
void CancelMLSFallbackProvider();
|
||||
|
||||
private:
|
||||
virtual ~CoreLocationLocationProvider();
|
||||
|
||||
CoreLocationObjects* mCLObjects;
|
||||
nsCOMPtr<nsIGeolocationUpdate> mCallback;
|
||||
RefPtr<MLSFallback> mMLSFallbackProvider;
|
||||
|
||||
class MLSUpdate : public nsIGeolocationUpdate
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIGEOLOCATIONUPDATE
|
||||
|
||||
explicit MLSUpdate(CoreLocationLocationProvider& parentProvider);
|
||||
|
||||
private:
|
||||
CoreLocationLocationProvider& mParentLocationProvider;
|
||||
virtual ~MLSUpdate();
|
||||
};
|
||||
};
|
||||
|
|
@ -1,268 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsGeoPosition.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsIDOMGeoPositionError.h"
|
||||
#include "CoreLocationLocationProvider.h"
|
||||
#include "nsCocoaFeatures.h"
|
||||
#include "prtime.h"
|
||||
#include "MLSFallback.h"
|
||||
|
||||
#include <CoreLocation/CLError.h>
|
||||
#include <CoreLocation/CLLocation.h>
|
||||
#include <CoreLocation/CLLocationManager.h>
|
||||
#include <CoreLocation/CLLocationManagerDelegate.h>
|
||||
|
||||
#include <objc/objc.h>
|
||||
#include <objc/objc-runtime.h>
|
||||
|
||||
#include "nsObjCExceptions.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
static const CLLocationAccuracy kHIGH_ACCURACY = kCLLocationAccuracyBest;
|
||||
static const CLLocationAccuracy kDEFAULT_ACCURACY = kCLLocationAccuracyNearestTenMeters;
|
||||
|
||||
@interface LocationDelegate : NSObject <CLLocationManagerDelegate>
|
||||
{
|
||||
CoreLocationLocationProvider* mProvider;
|
||||
}
|
||||
|
||||
- (id)init:(CoreLocationLocationProvider*)aProvider;
|
||||
- (void)locationManager:(CLLocationManager*)aManager
|
||||
didFailWithError:(NSError *)aError;
|
||||
- (void)locationManager:(CLLocationManager*)aManager didUpdateLocations:(NSArray*)locations;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LocationDelegate
|
||||
- (id) init:(CoreLocationLocationProvider*) aProvider
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
mProvider = aProvider;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)locationManager:(CLLocationManager*)aManager
|
||||
didFailWithError:(NSError *)aError
|
||||
{
|
||||
nsCOMPtr<nsIConsoleService> console =
|
||||
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
|
||||
|
||||
NS_ENSURE_TRUE_VOID(console);
|
||||
|
||||
NSString* message =
|
||||
[@"Failed to acquire position: " stringByAppendingString: [aError localizedDescription]];
|
||||
|
||||
console->LogStringMessage(NS_ConvertUTF8toUTF16([message UTF8String]).get());
|
||||
|
||||
if ([aError code] == kCLErrorDenied) {
|
||||
mProvider->NotifyError(nsIDOMGeoPositionError::PERMISSION_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
// The CL provider does not fallback to GeoIP, so use NetworkGeolocationProvider for this.
|
||||
// The concept here is: on error, hand off geolocation to MLS, which will then report
|
||||
// back a location or error. We can't call this with no delay however, as this method
|
||||
// is called with an error code of 0 in both failed geolocation cases, and also when
|
||||
// geolocation is not immediately available.
|
||||
// The 2 sec delay is arbitrarily large enough that CL has a reasonable head start and
|
||||
// if it is likely to succeed, it should complete before the MLS provider.
|
||||
// Take note that in locationManager:didUpdateLocations: the handoff to MLS is stopped.
|
||||
mProvider->CreateMLSFallbackProvider();
|
||||
}
|
||||
|
||||
- (void)locationManager:(CLLocationManager*)aManager didUpdateLocations:(NSArray*)aLocations
|
||||
{
|
||||
if (aLocations.count < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
mProvider->CancelMLSFallbackProvider();
|
||||
|
||||
CLLocation* location = [aLocations objectAtIndex:0];
|
||||
|
||||
nsCOMPtr<nsIDOMGeoPosition> geoPosition =
|
||||
new nsGeoPosition(location.coordinate.latitude,
|
||||
location.coordinate.longitude,
|
||||
location.altitude,
|
||||
location.horizontalAccuracy,
|
||||
location.verticalAccuracy,
|
||||
location.course,
|
||||
location.speed,
|
||||
PR_Now() / PR_USEC_PER_MSEC);
|
||||
|
||||
mProvider->Update(geoPosition);
|
||||
}
|
||||
@end
|
||||
|
||||
NS_IMPL_ISUPPORTS(CoreLocationLocationProvider::MLSUpdate, nsIGeolocationUpdate);
|
||||
|
||||
CoreLocationLocationProvider::MLSUpdate::MLSUpdate(CoreLocationLocationProvider& parentProvider)
|
||||
: mParentLocationProvider(parentProvider)
|
||||
{
|
||||
}
|
||||
|
||||
CoreLocationLocationProvider::MLSUpdate::~MLSUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CoreLocationLocationProvider::MLSUpdate::Update(nsIDOMGeoPosition *position)
|
||||
{
|
||||
nsCOMPtr<nsIDOMGeoPositionCoords> coords;
|
||||
position->GetCoords(getter_AddRefs(coords));
|
||||
if (!coords) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mParentLocationProvider.Update(position);
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHODIMP
|
||||
CoreLocationLocationProvider::MLSUpdate::NotifyError(uint16_t error)
|
||||
{
|
||||
mParentLocationProvider.NotifyError(error);
|
||||
return NS_OK;
|
||||
}
|
||||
class CoreLocationObjects {
|
||||
public:
|
||||
nsresult Init(CoreLocationLocationProvider* aProvider) {
|
||||
mLocationManager = [[CLLocationManager alloc] init];
|
||||
NS_ENSURE_TRUE(mLocationManager, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
mLocationDelegate = [[LocationDelegate alloc] init:aProvider];
|
||||
NS_ENSURE_TRUE(mLocationDelegate, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
mLocationManager.desiredAccuracy = kDEFAULT_ACCURACY;
|
||||
mLocationManager.delegate = mLocationDelegate;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
~CoreLocationObjects() {
|
||||
if (mLocationManager) {
|
||||
[mLocationManager release];
|
||||
}
|
||||
|
||||
if (mLocationDelegate) {
|
||||
[mLocationDelegate release];
|
||||
}
|
||||
}
|
||||
|
||||
LocationDelegate* mLocationDelegate;
|
||||
CLLocationManager* mLocationManager;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(CoreLocationLocationProvider, nsIGeolocationProvider)
|
||||
|
||||
CoreLocationLocationProvider::CoreLocationLocationProvider()
|
||||
: mCLObjects(nullptr), mMLSFallbackProvider(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
CoreLocationLocationProvider::~CoreLocationLocationProvider()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CoreLocationLocationProvider::Startup()
|
||||
{
|
||||
if (!mCLObjects) {
|
||||
nsAutoPtr<CoreLocationObjects> clObjs(new CoreLocationObjects());
|
||||
|
||||
nsresult rv = clObjs->Init(this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mCLObjects = clObjs.forget();
|
||||
}
|
||||
|
||||
// Must be stopped before starting or response (success or failure) is not guaranteed
|
||||
[mCLObjects->mLocationManager stopUpdatingLocation];
|
||||
[mCLObjects->mLocationManager startUpdatingLocation];
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CoreLocationLocationProvider::Watch(nsIGeolocationUpdate* aCallback)
|
||||
{
|
||||
if (mCallback) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mCallback = aCallback;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CoreLocationLocationProvider::Shutdown()
|
||||
{
|
||||
NS_ENSURE_STATE(mCLObjects);
|
||||
|
||||
[mCLObjects->mLocationManager stopUpdatingLocation];
|
||||
|
||||
delete mCLObjects;
|
||||
mCLObjects = nullptr;
|
||||
|
||||
if (mMLSFallbackProvider) {
|
||||
mMLSFallbackProvider->Shutdown();
|
||||
mMLSFallbackProvider = nullptr;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
CoreLocationLocationProvider::SetHighAccuracy(bool aEnable)
|
||||
{
|
||||
NS_ENSURE_STATE(mCLObjects);
|
||||
|
||||
mCLObjects->mLocationManager.desiredAccuracy =
|
||||
(aEnable ? kHIGH_ACCURACY : kDEFAULT_ACCURACY);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
CoreLocationLocationProvider::Update(nsIDOMGeoPosition* aSomewhere)
|
||||
{
|
||||
if (aSomewhere && mCallback) {
|
||||
mCallback->Update(aSomewhere);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CoreLocationLocationProvider::NotifyError(uint16_t aErrorCode)
|
||||
{
|
||||
mCallback->NotifyError(aErrorCode);
|
||||
}
|
||||
|
||||
void
|
||||
CoreLocationLocationProvider::CreateMLSFallbackProvider()
|
||||
{
|
||||
if (mMLSFallbackProvider) {
|
||||
return;
|
||||
}
|
||||
|
||||
mMLSFallbackProvider = new MLSFallback();
|
||||
mMLSFallbackProvider->Startup(new MLSUpdate(*this));
|
||||
}
|
||||
|
||||
void
|
||||
CoreLocationLocationProvider::CancelMLSFallbackProvider()
|
||||
{
|
||||
if (!mMLSFallbackProvider) {
|
||||
return;
|
||||
}
|
||||
|
||||
mMLSFallbackProvider->Shutdown();
|
||||
mMLSFallbackProvider = nullptr;
|
||||
}
|
||||
|
|
@ -1,14 +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 += ['CoreLocationLocationProvider.mm']
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/geolocation',
|
||||
]
|
||||
|
||||
|
|
@ -7,8 +7,6 @@ toolkit = CONFIG['MOZ_WIDGET_TOOLKIT']
|
|||
|
||||
if toolkit == 'windows':
|
||||
DIRS += ['windows']
|
||||
elif toolkit == 'cocoa':
|
||||
DIRS += ['mac']
|
||||
elif toolkit == 'android':
|
||||
DIRS += ['android']
|
||||
elif toolkit in ('gtk2', 'gtk3'):
|
||||
|
|
|
|||
|
|
@ -1,6 +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/.
|
||||
|
||||
toolkit.jar:
|
||||
* content/global/platformHTMLBindings.xml (platformHTMLBindings.xml)
|
||||
|
|
@ -1,6 +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/.
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
<?xml version="1.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/. -->
|
||||
|
||||
|
||||
<bindings id="htmlBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<binding id="inputFields" bindToUntrustedContent="true">
|
||||
<handlers>
|
||||
<handler event="keypress" key="c" modifiers="accel" command="cmd_copy"/>
|
||||
<handler event="keypress" key="x" modifiers="accel" command="cmd_cut"/>
|
||||
<handler event="keypress" key="v" modifiers="accel" command="cmd_paste"/>
|
||||
<handler event="keypress" key="z" modifiers="accel" command="cmd_undo"/>
|
||||
<handler event="keypress" key="z" modifiers="accel,shift" command="cmd_redo"/>
|
||||
<handler event="keypress" key="a" modifiers="accel" command="cmd_selectAll"/>
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
<binding id="textAreas" bindToUntrustedContent="true">
|
||||
<handlers>
|
||||
<handler event="keypress" key="c" modifiers="accel" command="cmd_copy"/>
|
||||
<handler event="keypress" key="x" modifiers="accel" command="cmd_cut"/>
|
||||
<handler event="keypress" key="v" modifiers="accel" command="cmd_paste"/>
|
||||
<handler event="keypress" key="z" modifiers="accel" command="cmd_undo"/>
|
||||
<handler event="keypress" key="z" modifiers="accel,shift" command="cmd_redo"/>
|
||||
<handler event="keypress" key="a" modifiers="accel" command="cmd_selectAll"/>
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
<binding id="browser">
|
||||
<handlers>
|
||||
#include ../browser-base.inc
|
||||
<handler event="keypress" keycode="VK_PAGE_UP" command="cmd_scrollPageUp"/>
|
||||
<handler event="keypress" keycode="VK_PAGE_DOWN" command="cmd_scrollPageDown"/>
|
||||
<handler event="keypress" keycode="VK_HOME" command="cmd_scrollTop" />
|
||||
<handler event="keypress" keycode="VK_END" command="cmd_scrollBottom" />
|
||||
|
||||
<handler event="keypress" keycode="VK_LEFT" modifiers="alt" command="cmd_moveLeft2" />
|
||||
<handler event="keypress" keycode="VK_RIGHT" modifiers="alt" command="cmd_moveRight2" />
|
||||
<handler event="keypress" keycode="VK_LEFT" modifiers="alt,shift" command="cmd_selectLeft2" />
|
||||
<handler event="keypress" keycode="VK_RIGHT" modifiers="alt,shift" command="cmd_selectRight2" />
|
||||
<handler event="keypress" keycode="VK_LEFT" modifiers="shift" command="cmd_selectLeft" />
|
||||
<handler event="keypress" keycode="VK_RIGHT" modifiers="shift" command="cmd_selectRight" />
|
||||
<handler event="keypress" keycode="VK_UP" modifiers="alt,shift" command="cmd_selectUp2" />
|
||||
<handler event="keypress" keycode="VK_DOWN" modifiers="alt,shift" command="cmd_selectDown2" />
|
||||
<handler event="keypress" keycode="VK_UP" modifiers="shift" command="cmd_selectUp" />
|
||||
<handler event="keypress" keycode="VK_DOWN" modifiers="shift" command="cmd_selectDown" />
|
||||
<handler event="keypress" keycode="VK_UP" modifiers="accel" command="cmd_moveUp2"/>
|
||||
<handler event="keypress" keycode="VK_DOWN" modifiers="accel" command="cmd_moveDown2"/>
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
<binding id="editor">
|
||||
<handlers>
|
||||
<handler event="keypress" key=" " modifiers="shift" command="cmd_scrollPageUp" />
|
||||
<handler event="keypress" key=" " command="cmd_scrollPageDown" />
|
||||
|
||||
<handler event="keypress" key="z" command="cmd_undo" modifiers="accel"/>
|
||||
<handler event="keypress" key="z" command="cmd_redo" modifiers="accel,shift" />
|
||||
<handler event="keypress" key="x" command="cmd_cut" modifiers="accel"/>
|
||||
<handler event="keypress" key="c" command="cmd_copy" modifiers="accel"/>
|
||||
<handler event="keypress" key="v" command="cmd_paste" modifiers="accel"/>
|
||||
<handler event="keypress" key="v" command="cmd_pasteNoFormatting" modifiers="accel,shift"/>
|
||||
<handler event="keypress" key="a" command="cmd_selectAll" modifiers="accel"/>
|
||||
<handler event="keypress" key="v" command="cmd_pasteNoFormatting" modifiers="accel,alt,shift"/>
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
</bindings>
|
||||
|
|
@ -5,8 +5,6 @@
|
|||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
DIRS += ['win']
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
DIRS += ['mac']
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
|
||||
DIRS += ['android']
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue