summaryrefslogtreecommitdiff
path: root/dom
diff options
context:
space:
mode:
authorMoonchild <moonchild@palemoon.org>2022-05-01 09:28:43 +0000
committerMoonchild <moonchild@palemoon.org>2022-05-01 09:28:43 +0000
commit633728842e06fc9d61228a98a1d077efa5bf5ff2 (patch)
tree9450f24188f6011588851d9fd42802d2524e2e4b /dom
parentbf110158b926f99fa36c180150d242453c8b0f95 (diff)
parentbc2841f9f916b2a6aade0cd6dae4a2e4407352b6 (diff)
downloaduxp-633728842e06fc9d61228a98a1d077efa5bf5ff2.tar.gz
Merge branch 'master' into vpx-format-update
Diffstat (limited to 'dom')
-rw-r--r--dom/base/Navigator.cpp6
-rw-r--r--dom/base/nsContentUtils.cpp5
-rw-r--r--dom/base/nsFocusManager.cpp13
-rw-r--r--dom/base/nsGlobalWindow.cpp21
-rw-r--r--dom/base/nsJSEnvironment.cpp4
-rw-r--r--dom/base/nsObjectLoadingContent.cpp19
-rw-r--r--dom/base/nsWindowRoot.cpp6
-rw-r--r--dom/canvas/WebGL2Context.cpp7
-rw-r--r--dom/canvas/WebGLBuffer.cpp2
-rw-r--r--dom/canvas/WebGLContext.h13
-rw-r--r--dom/canvas/WebGLContextBuffers.cpp16
-rw-r--r--dom/canvas/WebGLContextDraw.cpp7
-rw-r--r--dom/canvas/WebGLContextValidate.cpp12
-rw-r--r--dom/canvas/WebGLProgram.cpp34
-rw-r--r--dom/canvas/WebGLShaderValidator.cpp37
-rw-r--r--dom/events/EventStateManager.cpp52
-rw-r--r--dom/events/TextComposition.cpp13
-rw-r--r--dom/gamepad/cocoa/CocoaGamepad.cpp590
-rw-r--r--dom/gamepad/moz.build4
-rw-r--r--dom/geolocation/moz.build6
-rw-r--r--dom/html/HTMLButtonElement.cpp6
-rw-r--r--dom/html/HTMLImageElement.cpp3
-rw-r--r--dom/html/HTMLInputElement.cpp10
-rw-r--r--dom/html/HTMLInputElement.h2
-rw-r--r--dom/html/HTMLObjectElement.cpp141
-rw-r--r--dom/html/HTMLObjectElement.h12
-rw-r--r--dom/html/HTMLSharedObjectElement.cpp26
-rw-r--r--dom/html/HTMLSharedObjectElement.h5
-rw-r--r--dom/html/HTMLSummaryElement.cpp9
-rw-r--r--dom/html/nsGenericHTMLElement.cpp4
-rw-r--r--dom/media/GraphDriver.cpp82
-rw-r--r--dom/media/GraphDriver.h5
-rw-r--r--dom/media/eme/MediaKeySystemAccessManager.cpp3
-rw-r--r--dom/media/gmp/GMPChild.cpp6
-rw-r--r--dom/media/gmp/rlz/GMPDeviceBinding.cpp48
-rw-r--r--dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp9
-rw-r--r--dom/media/standalone/moz.build3
-rw-r--r--dom/media/systemservices/LoadMonitor.cpp27
-rw-r--r--dom/media/systemservices/OSXRunLoopSingleton.cpp44
-rw-r--r--dom/media/systemservices/OSXRunLoopSingleton.h25
-rw-r--r--dom/media/systemservices/moz.build4
-rwxr-xr-xdom/media/webaudio/AudioContext.cpp2
-rw-r--r--dom/media/webrtc/MediaEngineCameraVideoSource.cpp7
-rw-r--r--dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerModule.cpp56
-rw-r--r--dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.h43
-rw-r--r--dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm498
-rw-r--r--dom/media/webspeech/synth/cocoa/moz.build11
-rw-r--r--dom/media/webspeech/synth/moz.build3
-rw-r--r--dom/moz.build2
-rw-r--r--dom/plugins/base/PluginPRLibrary.cpp18
-rw-r--r--dom/plugins/base/PluginPRLibrary.h25
-rw-r--r--dom/plugins/base/moz.build6
-rw-r--r--dom/plugins/base/npapi.h209
-rw-r--r--dom/plugins/base/npfunctions.h32
-rw-r--r--dom/plugins/base/nsNPAPIPlugin.cpp113
-rw-r--r--dom/plugins/base/nsNPAPIPlugin.h4
-rw-r--r--dom/plugins/base/nsNPAPIPluginInstance.cpp41
-rw-r--r--dom/plugins/base/nsNPAPIPluginInstance.h18
-rw-r--r--dom/plugins/base/nsPluginHost.cpp15
-rw-r--r--dom/plugins/base/nsPluginInstanceOwner.cpp896
-rw-r--r--dom/plugins/base/nsPluginInstanceOwner.h63
-rw-r--r--dom/plugins/base/nsPluginNativeWindow.cpp2
-rw-r--r--dom/plugins/base/nsPluginTags.cpp4
-rw-r--r--dom/plugins/base/nsPluginsDirDarwin.cpp572
-rw-r--r--dom/plugins/ipc/NPEventOSX.h193
-rw-r--r--dom/plugins/ipc/PluginInstanceChild.cpp389
-rw-r--r--dom/plugins/ipc/PluginInstanceChild.h17
-rw-r--r--dom/plugins/ipc/PluginInstanceParent.cpp258
-rw-r--r--dom/plugins/ipc/PluginInstanceParent.h5
-rw-r--r--dom/plugins/ipc/PluginInterposeOSX.h137
-rw-r--r--dom/plugins/ipc/PluginInterposeOSX.mm1158
-rw-r--r--dom/plugins/ipc/PluginLibrary.h9
-rw-r--r--dom/plugins/ipc/PluginMessageUtils.cpp5
-rw-r--r--dom/plugins/ipc/PluginMessageUtils.h206
-rw-r--r--dom/plugins/ipc/PluginModuleChild.cpp14
-rwxr-xr-xdom/plugins/ipc/PluginModuleParent.cpp108
-rw-r--r--dom/plugins/ipc/PluginModuleParent.h15
-rw-r--r--dom/plugins/ipc/PluginProcessChild.cpp55
-rw-r--r--dom/plugins/ipc/PluginProcessParent.cpp7
-rw-r--r--dom/plugins/ipc/PluginQuirks.cpp10
-rw-r--r--dom/plugins/ipc/PluginUtilsOSX.h92
-rw-r--r--dom/plugins/ipc/PluginUtilsOSX.mm462
-rw-r--r--dom/plugins/ipc/interpose/moz.build12
-rw-r--r--dom/plugins/ipc/interpose/plugin_child_interpose.mm134
-rw-r--r--dom/plugins/ipc/moz.build16
-rw-r--r--dom/system/OSFileConstants.cpp56
-rw-r--r--dom/system/mac/CoreLocationLocationProvider.h59
-rw-r--r--dom/system/mac/CoreLocationLocationProvider.mm268
-rw-r--r--dom/system/mac/moz.build14
-rw-r--r--dom/system/moz.build2
-rw-r--r--dom/xbl/builtin/mac/jar.mn6
-rw-r--r--dom/xbl/builtin/mac/moz.build6
-rw-r--r--dom/xbl/builtin/mac/platformHTMLBindings.xml72
-rw-r--r--dom/xbl/builtin/moz.build2
-rw-r--r--dom/xbl/nsXBLPrototypeHandler.cpp7
-rw-r--r--dom/xul/nsXULElement.cpp12
96 files changed, 7678 insertions, 109 deletions
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
index 3972abc688..28b66be3db 100644
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1388,6 +1388,12 @@ Navigator::GetPlatform(nsAString& aPlatform, bool aUsePrefOverriddenValue)
aPlatform.AssignLiteral("Win64");
#elif defined(WIN32)
aPlatform.AssignLiteral("Win32");
+#elif defined(XP_MACOSX) && defined(__ppc__)
+ aPlatform.AssignLiteral("MacPPC");
+#elif defined(XP_MACOSX) && defined(__i386__)
+ aPlatform.AssignLiteral("MacIntel");
+#elif defined(XP_MACOSX) && defined(__x86_64__)
+ aPlatform.AssignLiteral("MacIntel");
#else
// XXX Communicator uses compiled-in build-time string defines
// to indicate the platform it was compiled *for*, not what it is
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index 657c0c0fb2..fa8ba6d97d 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -6840,6 +6840,10 @@ nsContentUtils::HaveEqualPrincipals(nsIDocument* aDoc1, nsIDocument* aDoc2)
bool
nsContentUtils::HasPluginWithUncontrolledEventDispatch(nsIContent* aContent)
{
+#ifdef XP_MACOSX
+ // We control dispatch to all mac plugins.
+ return false;
+#else
if (!aContent || !aContent->IsInUncomposedDoc()) {
return false;
}
@@ -6862,6 +6866,7 @@ nsContentUtils::HasPluginWithUncontrolledEventDispatch(nsIContent* aContent)
}
return !isWindowless;
+#endif
}
/* static */
diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp
index 0c12103fba..6cc5bbd36f 100644
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -64,7 +64,9 @@
#include "nsAccessibilityService.h"
#endif
+#ifndef XP_MACOSX
#include "nsIScriptError.h"
+#endif
using namespace mozilla;
using namespace mozilla::dom;
@@ -1264,10 +1266,12 @@ nsFocusManager::SetFocusInner(nsIContent* aNewContent, int32_t aFlags,
}
}
- // Exit fullscreen if we're focusing a windowed plugin. We don't control
- // event dispatch to windowed plugins, so we can't display the <Press ESC
- // to leave fullscreen mode> warning on key input if a windowed plugin is
- // focused, so just exit fullscreen to guard against phishing.
+ // Exit fullscreen if we're focusing a windowed plugin on a non-MacOSX
+ // system. We don't control event dispatch to windowed plugins on non-MacOSX,
+ // so we can't display the "Press ESC to leave fullscreen mode" warning on
+ // key input if a windowed plugin is focused, so just exit fullscreen
+ // to guard against phishing.
+#ifndef XP_MACOSX
if (contentToFocus &&
nsContentUtils::
GetRootDocument(contentToFocus->OwnerDoc())->GetFullscreenElement() &&
@@ -1279,6 +1283,7 @@ nsFocusManager::SetFocusInner(nsIContent* aNewContent, int32_t aFlags,
"FocusedWindowedPluginWhileFullscreen");
nsIDocument::AsyncExitFullscreen(contentToFocus->OwnerDoc());
}
+#endif
// if the FLAG_NOSWITCHFRAME flag is used, only allow the focus to be
// shifted away from the current element if the new shell to focus is
diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
index ccfbf488d3..c6b35a9bf0 100644
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6222,7 +6222,18 @@ nsGlobalWindow::CheckSecurityLeftAndTop(int32_t* aLeft, int32_t* aTop, bool aCal
screen->GetAvailLeft(&screenLeft);
screen->GetAvailWidth(&screenWidth);
screen->GetAvailHeight(&screenHeight);
+#if defined(XP_MACOSX)
+ /* The mac's coordinate system is different from the assumed Windows'
+ system. It offsets by the height of the menubar so that a window
+ placed at (0,0) will be entirely visible. Unfortunately that
+ correction is made elsewhere (in Widget) and the meaning of
+ the Avail... coordinates is overloaded. Here we allow a window
+ to be placed at (0,0) because it does make sense to do so.
+ */
+ screen->GetTop(&screenTop);
+#else
screen->GetAvailTop(&screenTop);
+#endif
if (aLeft) {
if (screenLeft+screenWidth < *aLeft+winWidth)
@@ -7060,6 +7071,16 @@ nsGlobalWindow::Dump(const nsAString& aStr)
char *cstr = ToNewUTF8String(aStr);
+#if defined(XP_MACOSX)
+ // have to convert \r to \n so that printing to the console works
+ char *c = cstr, *cEnd = cstr + strlen(cstr);
+ while (c < cEnd) {
+ if (*c == '\r')
+ *c = '\n';
+ c++;
+ }
+#endif
+
if (cstr) {
MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug, ("[Window.Dump] %s", cstr));
#ifdef XP_WIN
diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp
index 15448181f7..5229948e01 100644
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -59,6 +59,10 @@
#include "nsJSPrincipals.h"
+#ifdef XP_MACOSX
+// AssertMacros.h defines 'check' and conflicts with AccessCheck.h
+#undef check
+#endif
#include "AccessCheck.h"
#include "mozilla/Logging.h"
diff --git a/dom/base/nsObjectLoadingContent.cpp b/dom/base/nsObjectLoadingContent.cpp
index f8541c61ba..4129135baa 100644
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -95,6 +95,13 @@
#endif
#endif // XP_WIN
+#ifdef XP_MACOSX
+// HandlePluginCrashed() and HandlePluginInstantiated() needed from here to
+// fix bug 1147521. Should later be replaced by proper interface methods,
+// maybe on nsIObjectLoadingContext.
+#include "mozilla/dom/HTMLObjectElement.h"
+#endif
+
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
static const char *kPrefJavaMIME = "plugin.java.mime";
@@ -909,6 +916,10 @@ nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading)
NS_LITERAL_STRING("PluginInstantiated"));
NS_DispatchToCurrentThread(ev);
+#ifdef XP_MACOSX
+ HTMLObjectElement::HandlePluginInstantiated(thisContent->AsElement());
+#endif
+
return NS_OK;
}
@@ -2907,6 +2918,10 @@ nsObjectLoadingContent::PluginCrashed(nsIPluginTag* aPluginTag,
nsCOMPtr<nsIContent> thisContent =
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+#ifdef XP_MACOSX
+ HTMLObjectElement::HandlePluginCrashed(thisContent->AsElement());
+#endif
+
PluginDestroyed();
// Switch to fallback/crashed state, notify
@@ -3145,6 +3160,10 @@ nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner,
return;
}
+#if defined(XP_MACOSX)
+ aInstanceOwner->HidePluginWindow();
+#endif
+
RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
NS_ASSERTION(pluginHost, "No plugin host?");
pluginHost->StopPluginInstance(inst);
diff --git a/dom/base/nsWindowRoot.cpp b/dom/base/nsWindowRoot.cpp
index 38348b46c2..509420f76d 100644
--- a/dom/base/nsWindowRoot.cpp
+++ b/dom/base/nsWindowRoot.cpp
@@ -34,8 +34,14 @@ nsWindowRoot::nsWindowRoot(nsPIDOMWindowOuter* aWindow)
mWindow = aWindow;
MOZ_ASSERT(mWindow->IsOuterWindow());
+ // Keyboard indicators are not shown on Mac by default.
+#if defined(XP_MACOSX)
+ mShowAccelerators = false;
+ mShowFocusRings = false;
+#else
mShowAccelerators = true;
mShowFocusRings = true;
+#endif
}
nsWindowRoot::~nsWindowRoot()
diff --git a/dom/canvas/WebGL2Context.cpp b/dom/canvas/WebGL2Context.cpp
index 09891e7b6e..8c472384e5 100644
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -125,6 +125,13 @@ WebGLContext::InitWebGL2(FailureReason* const out_failReason)
fnGatherMissing2(gl::GLFeature::occlusion_query_boolean,
gl::GLFeature::occlusion_query);
+#ifdef XP_MACOSX
+ // On OSX, GL core profile is used. This requires texture swizzle
+ // support to emulate legacy texture formats: ALPHA, LUMINANCE,
+ // and LUMINANCE_ALPHA.
+ fnGatherMissing(gl::GLFeature::texture_swizzle);
+#endif
+
fnGatherMissing2(gl::GLFeature::prim_restart_fixed,
gl::GLFeature::prim_restart);
diff --git a/dom/canvas/WebGLBuffer.cpp b/dom/canvas/WebGLBuffer.cpp
index 11d956c8a4..02a8f649fe 100644
--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -115,7 +115,7 @@ WebGLBuffer::BufferData(GLenum target, size_t size, const void* data, GLenum usa
const ScopedLazyBind lazyBind(gl, target, this);
mContext->InvalidateBufferFetching();
-#if defined(MOZ_WIDGET_GTK)
+#if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
// bug 790879
if (gl->WorkAroundDriverBugs() &&
size > INT32_MAX)
diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h
index 628d4713ca..0510e6898a 100644
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -30,6 +30,10 @@
#include "ScopedGLHelpers.h"
#include "TexUnpackBlob.h"
+#ifdef XP_MACOSX
+#include "ForceDiscreteGPUHelperCGL.h"
+#endif
+
// Local
#include "WebGLContextLossHandler.h"
#include "WebGLContextUnchecked.h"
@@ -2011,6 +2015,15 @@ protected:
JSObject* WebGLObjectAsJSObject(JSContext* cx, const WebGLObjectType*,
ErrorResult& rv) const;
+#ifdef XP_MACOSX
+ // see bug 713305. This RAII helper guarantees that we're on the discrete GPU, during its lifetime
+ // Debouncing note: we don't want to switch GPUs too frequently, so try to not create and destroy
+ // these objects at high frequency. Having WebGLContext's hold one such object seems fine,
+ // because WebGLContext objects only go away during GC, which shouldn't happen too frequently.
+ // If in the future GC becomes much more frequent, we may have to revisit then (maybe use a timer).
+ ForceDiscreteGPUHelperCGL mForceDiscreteGPUHelper;
+#endif
+
public:
// console logging helpers
void GenerateWarning(const char* fmt, ...);
diff --git a/dom/canvas/WebGLContextBuffers.cpp b/dom/canvas/WebGLContextBuffers.cpp
index 6f583c10c6..e787b99143 100644
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -295,6 +295,16 @@ WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
////
+#ifdef XP_MACOSX
+ if (buffer && buffer->Content() == WebGLBuffer::Kind::Undefined &&
+ gl->WorkAroundDriverBugs())
+ {
+ // BindBufferRange will fail if the buffer's contents is undefined.
+ // Bind so driver initializes the buffer.
+ gl->fBindBuffer(target, buffer->mGLName);
+ }
+#endif
+
gl->fBindBufferRange(target, index, buffer ? buffer->mGLName : 0, offset, size);
////
@@ -342,6 +352,12 @@ WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage)
if (!checkedSize.isValid())
return ErrorOutOfMemory("%s: Size too large for platform.", funcName);
+#if defined(XP_MACOSX)
+ if (gl->WorkAroundDriverBugs() && size > 1200000000) {
+ return ErrorOutOfMemory("Allocations larger than 1200000000 fail on MacOS.");
+ }
+#endif
+
const UniqueBuffer zeroBuffer(calloc(size, 1));
if (!zeroBuffer)
return ErrorOutOfMemory("%s: Failed to allocate zeros.", funcName);
diff --git a/dom/canvas/WebGLContextDraw.cpp b/dom/canvas/WebGLContextDraw.cpp
index c0ba20301a..6c684b2ff8 100644
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -1045,6 +1045,13 @@ WebGLContext::WhatDoesVertexAttrib0Need() const
const auto& isAttribArray0Enabled = mBoundVertexArray->mAttribs[0].mEnabled;
bool legacyAttrib0 = gl->IsCompatibilityProfile();
+#ifdef XP_MACOSX
+ if (gl->WorkAroundDriverBugs()) {
+ // Failures in conformance/attribs/gl-disabled-vertex-attrib.
+ // Even in Core profiles on NV. Sigh.
+ legacyAttrib0 |= (gl->Vendor() == gl::GLVendor::NVIDIA);
+ }
+#endif
if (!legacyAttrib0)
return WebGLVertexAttrib0Status::Default;
diff --git a/dom/canvas/WebGLContextValidate.cpp b/dom/canvas/WebGLContextValidate.cpp
index 60bb3a32cc..30d4c65221 100644
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -695,6 +695,18 @@ WebGLContext::InitAndValidateGL(FailureReason* const out_failReason)
gl->fEnable(LOCAL_GL_PROGRAM_POINT_SIZE);
}
+#ifdef XP_MACOSX
+ if (gl->WorkAroundDriverBugs() &&
+ gl->Vendor() == gl::GLVendor::ATI &&
+ !nsCocoaFeatures::IsAtLeastVersion(10,9))
+ {
+ // The Mac ATI driver, in all known OSX version up to and including
+ // 10.8, renders points sprites upside-down. (Apple bug 11778921)
+ gl->fPointParameterf(LOCAL_GL_POINT_SPRITE_COORD_ORIGIN,
+ LOCAL_GL_LOWER_LEFT);
+ }
+#endif
+
if (gl->IsSupported(gl::GLFeature::seamless_cube_map_opt_in)) {
gl->fEnable(LOCAL_GL_TEXTURE_CUBE_MAP_SEAMLESS);
}
diff --git a/dom/canvas/WebGLProgram.cpp b/dom/canvas/WebGLProgram.cpp
index 1ff27e1d2f..9b204358bb 100644
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -689,6 +689,24 @@ WebGLProgram::GetFragDataLocation(const nsAString& userName_wide) const
gl->MakeCurrent();
const NS_LossyConvertUTF16toASCII userName(userName_wide);
+#ifdef XP_MACOSX
+ if (gl->WorkAroundDriverBugs()) {
+ // OSX doesn't return locs for indexed names, just the base names.
+ // Indicated by failure in: conformance2/programs/gl-get-frag-data-location.html
+ bool isArray;
+ size_t arrayIndex;
+ nsCString baseUserName;
+ if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
+ return -1;
+
+ if (arrayIndex >= mContext->mImplMaxDrawBuffers)
+ return -1;
+
+ const auto baseLoc = GetFragDataByUserName(this, baseUserName);
+ const auto loc = baseLoc + GLint(arrayIndex);
+ return loc;
+ }
+#endif
return GetFragDataByUserName(this, userName);
}
@@ -752,6 +770,11 @@ WebGLProgram::GetProgramParameter(GLenum pname) const
return JS::BooleanValue(IsLinked());
case LOCAL_GL_VALIDATE_STATUS:
+#ifdef XP_MACOSX
+ // See comment in ValidateProgram.
+ if (gl->WorkAroundDriverBugs())
+ return JS::BooleanValue(true);
+#endif
// Todo: Implement this in our code.
return JS::BooleanValue(bool(GetProgramiv(gl, mGLName, pname)));
@@ -1354,6 +1377,17 @@ WebGLProgram::ValidateProgram() const
{
mContext->MakeContextCurrent();
gl::GLContext* gl = mContext->gl;
+
+#ifdef XP_MACOSX
+ // See bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed
+ // with Mac OS 10.6.7.
+ if (gl->WorkAroundDriverBugs()) {
+ mContext->GenerateWarning("validateProgram: Implemented as a no-op on"
+ " Mac to work around crashes.");
+ return;
+ }
+#endif
+
gl->fValidateProgram(mGLName);
}
diff --git a/dom/canvas/WebGLShaderValidator.cpp b/dom/canvas/WebGLShaderValidator.cpp
index eadeeb56b0..bf2df82f71 100644
--- a/dom/canvas/WebGLShaderValidator.cpp
+++ b/dom/canvas/WebGLShaderValidator.cpp
@@ -63,8 +63,34 @@ ChooseValidatorCompileOptions(const ShBuiltInResources& resources,
SH_REGENERATE_STRUCT_NAMES;
}
- // We want to do this everywhere.
+#ifndef XP_MACOSX
+ // We want to do this everywhere, but to do this on Mac, we need
+ // to do it only on Mac OSX > 10.6 as this causes the shader
+ // compiler in 10.6 to crash
options |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS;
+#endif
+
+#ifdef XP_MACOSX
+ if (gl->WorkAroundDriverBugs()) {
+ // Work around https://bugs.webkit.org/show_bug.cgi?id=124684,
+ // https://chromium.googlesource.com/angle/angle/+/5e70cf9d0b1bb
+ options |= SH_UNFOLD_SHORT_CIRCUIT;
+
+ // OS X 10.7/10.8 specific:
+
+ // Work around bug 665578 and bug 769810
+ if (gl->Vendor() == gl::GLVendor::ATI) {
+ options |= SH_EMULATE_BUILT_IN_FUNCTIONS;
+ }
+ // Work around bug 735560
+ if (gl->Vendor() == gl::GLVendor::Intel) {
+ options |= SH_EMULATE_BUILT_IN_FUNCTIONS;
+ }
+
+ // Work around that Mac drivers handle struct scopes incorrectly.
+ options |= SH_REGENERATE_STRUCT_NAMES;
+ }
+#endif
if (resources.MaxExpressionComplexity > 0) {
options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
@@ -161,6 +187,15 @@ WebGLContext::CreateShaderValidator(GLenum shaderType) const
// If underlying GLES doesn't have highp in frag shaders, it should complain anyways.
resources.FragmentPrecisionHigh = mDisableFragHighP ? 0 : 1;
+ if (gl->WorkAroundDriverBugs()) {
+#ifdef XP_MACOSX
+ if (gl->Vendor() == gl::GLVendor::NVIDIA) {
+ // Work around bug 890432
+ resources.MaxExpressionComplexity = 1000;
+ }
+#endif
+ }
+
int compileOptions = webgl::ChooseValidatorCompileOptions(resources, gl);
return webgl::ShaderValidator::Create(shaderType, spec, outputLanguage, resources,
compileOptions);
diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp
index 86201bdc30..9f06deb2ba 100644
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -93,6 +93,10 @@
#include "Units.h"
#include "mozilla/layers/APZCTreeManager.h"
+#ifdef XP_MACOSX
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
namespace mozilla {
using namespace dom;
@@ -1511,6 +1515,14 @@ EventStateManager::FireContextClick()
return;
}
+#ifdef XP_MACOSX
+ // Hack to ensure that we don't show a context menu when the user
+ // let go of the mouse after a long cpu-hogging operation prevented
+ // us from handling any OS events. See bug 117589.
+ if (!CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft))
+ return;
+#endif
+
nsEventStatus status = nsEventStatus_eIgnore;
// Dispatch to the DOM. We have to fake out the ESM and tell it that the
@@ -2823,6 +2835,29 @@ EventStateManager::DecideGestureEvent(WidgetGestureNotifyEvent* aEvent,
aEvent->mPanDirection = panDirection;
}
+#ifdef XP_MACOSX
+static bool
+NodeAllowsClickThrough(nsINode* aNode)
+{
+ while (aNode) {
+ if (aNode->IsXULElement()) {
+ mozilla::dom::Element* element = aNode->AsElement();
+ static nsIContent::AttrValuesArray strings[] =
+ {&nsGkAtoms::always, &nsGkAtoms::never, nullptr};
+ switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::clickthrough,
+ strings, eCaseMatters)) {
+ case 0:
+ return true;
+ case 1:
+ return false;
+ }
+ }
+ aNode = nsContentUtils::GetCrossDocParentNode(aNode);
+ }
+ return true;
+}
+#endif
+
void
EventStateManager::PostHandleKeyboardEvent(WidgetKeyboardEvent* aKeyboardEvent,
nsEventStatus& aStatus,
@@ -3055,7 +3090,10 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
// focused frame
EnsureDocument(mPresContext);
if (mDocument) {
- fm->ClearFocus(mDocument->GetWindow());
+#ifdef XP_MACOSX
+ if (!activeContent || !activeContent->IsXULElement())
+#endif
+ fm->ClearFocus(mDocument->GetWindow());
fm->SetFocusedWindow(mDocument->GetWindow());
}
}
@@ -3467,6 +3505,18 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
}
break;
+#ifdef XP_MACOSX
+ case eMouseActivate:
+ if (mCurrentTarget) {
+ nsCOMPtr<nsIContent> targetContent;
+ mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
+ if (!NodeAllowsClickThrough(targetContent)) {
+ *aStatus = nsEventStatus_eConsumeNoDefault;
+ }
+ }
+ break;
+#endif
+
default:
break;
}
diff --git a/dom/events/TextComposition.cpp b/dom/events/TextComposition.cpp
index 3b3dc6505b..bd7ebbc468 100644
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -21,8 +21,21 @@
#include "mozilla/Unused.h"
#include "mozilla/dom/TabParent.h"
+#ifdef XP_MACOSX
+// Some defiens will be conflict with OSX SDK
+#define TextRange _TextRange
+#define TextRangeArray _TextRangeArray
+#define Comment _Comment
+#endif
+
#include "nsPluginInstanceOwner.h"
+#ifdef XP_MACOSX
+#undef TextRange
+#undef TextRangeArray
+#undef Comment
+#endif
+
using namespace mozilla::widget;
namespace mozilla {
diff --git a/dom/gamepad/cocoa/CocoaGamepad.cpp b/dom/gamepad/cocoa/CocoaGamepad.cpp
new file mode 100644
index 0000000000..eb6eda9a17
--- /dev/null
+++ b/dom/gamepad/cocoa/CocoaGamepad.cpp
@@ -0,0 +1,590 @@
+/* -*- 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
diff --git a/dom/gamepad/moz.build b/dom/gamepad/moz.build
index cee84f4290..1ed5830567 100644
--- a/dom/gamepad/moz.build
+++ b/dom/gamepad/moz.build
@@ -48,6 +48,10 @@ 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'
diff --git a/dom/geolocation/moz.build b/dom/geolocation/moz.build
index 4a7d8b17be..441349e5f9 100644
--- a/dom/geolocation/moz.build
+++ b/dom/geolocation/moz.build
@@ -25,7 +25,11 @@ LOCAL_INCLUDES += [
'/dom/ipc',
]
-if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '/dom/system/mac',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
LOCAL_INCLUDES += [
'/dom/system/windows',
]
diff --git a/dom/html/HTMLButtonElement.cpp b/dom/html/HTMLButtonElement.cpp
index 80df5bc740..e493d1891d 100644
--- a/dom/html/HTMLButtonElement.cpp
+++ b/dom/html/HTMLButtonElement.cpp
@@ -156,7 +156,11 @@ HTMLButtonElement::IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, int32_t
return true;
}
- *aIsFocusable = !IsDisabled();
+ *aIsFocusable =
+#ifdef XP_MACOSX
+ (!aWithMouse || nsFocusManager::sMouseFocusesFormControl) &&
+#endif
+ !IsDisabled();
return false;
}
diff --git a/dom/html/HTMLImageElement.cpp b/dom/html/HTMLImageElement.cpp
index f7fe0fdcee..b11cd101a0 100644
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -597,6 +597,9 @@ HTMLImageElement::IsHTMLFocusable(bool aWithMouse,
}
*aIsFocusable =
+#ifdef XP_MACOSX
+ (!aWithMouse || nsFocusManager::sMouseFocusesFormControl) &&
+#endif
(tabIndex >= 0 || HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex));
return false;
diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp
index 866a5ac8c5..d9f745d4ca 100644
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -3566,6 +3566,7 @@ HTMLInputElement::Focus(ErrorResult& aError)
return;
}
+#if !defined(XP_MACOSX)
bool
HTMLInputElement::IsNodeApzAwareInternal() const
{
@@ -3574,6 +3575,7 @@ HTMLInputElement::IsNodeApzAwareInternal() const
return (mType == NS_FORM_INPUT_NUMBER) || (mType == NS_FORM_INPUT_RANGE) ||
nsINode::IsNodeApzAwareInternal();
}
+#endif
bool
HTMLInputElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
@@ -4726,6 +4728,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
}
break;
}
+#if !defined(XP_MACOSX)
case eWheel: {
// Handle wheel events as increasing / decreasing the input element's
// value when it's focused and it's type is number or range.
@@ -4759,6 +4762,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
}
break;
}
+#endif
default:
break;
}
@@ -6651,9 +6655,11 @@ FireEventForAccessibility(nsIDOMHTMLInputElement* aTarget,
void
HTMLInputElement::UpdateApzAwareFlag()
{
+#if !defined(XP_MACOSX)
if ((mType == NS_FORM_INPUT_NUMBER) || (mType == NS_FORM_INPUT_RANGE)) {
SetMayBeApzAware();
}
+#endif
}
nsresult
@@ -7223,7 +7229,11 @@ HTMLInputElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable, int32_t*
return false;
}
+#ifdef XP_MACOSX
+ const bool defaultFocusable = !aWithMouse || nsFocusManager::sMouseFocusesFormControl;
+#else
const bool defaultFocusable = true;
+#endif
if (mType == NS_FORM_INPUT_FILE ||
mType == NS_FORM_INPUT_NUMBER ||
diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h
index d7eccc0756..23c098df3b 100644
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -137,7 +137,9 @@ public:
virtual void Focus(ErrorResult& aError) override;
// nsINode
+#if !defined(XP_MACOSX)
virtual bool IsNodeApzAwareInternal() const override;
+#endif
// Element
virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override;
diff --git a/dom/html/HTMLObjectElement.cpp b/dom/html/HTMLObjectElement.cpp
index a0d06a8c65..a77cd6fe57 100644
--- a/dom/html/HTMLObjectElement.cpp
+++ b/dom/html/HTMLObjectElement.cpp
@@ -18,6 +18,11 @@
#include "nsNPAPIPluginInstance.h"
#include "nsIWidget.h"
#include "nsContentUtils.h"
+#ifdef XP_MACOSX
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/dom/Event.h"
+#include "nsFocusManager.h"
+#endif
namespace mozilla {
namespace dom {
@@ -39,6 +44,9 @@ HTMLObjectElement::HTMLObjectElement(already_AddRefed<mozilla::dom::NodeInfo>& a
HTMLObjectElement::~HTMLObjectElement()
{
+#ifdef XP_MACOSX
+ OnFocusBlurPlugin(this, false);
+#endif
UnregisterActivityObserver();
DestroyImageLoadingContent();
}
@@ -109,6 +117,131 @@ NS_IMPL_ELEMENT_CLONE(HTMLObjectElement)
// nsIConstraintValidation
NS_IMPL_NSICONSTRAINTVALIDATION(HTMLObjectElement)
+#ifdef XP_MACOSX
+
+static nsIWidget* GetWidget(Element* aElement)
+{
+ return nsContentUtils::WidgetForDocument(aElement->OwnerDoc());
+}
+
+Element* HTMLObjectElement::sLastFocused = nullptr; // Weak
+
+class PluginFocusSetter : public Runnable
+{
+public:
+ PluginFocusSetter(nsIWidget* aWidget, Element* aElement)
+ : mWidget(aWidget), mElement(aElement)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ if (mElement) {
+ HTMLObjectElement::sLastFocused = mElement;
+ bool value = true;
+ mWidget->SetPluginFocused(value);
+ } else if (!HTMLObjectElement::sLastFocused) {
+ bool value = false;
+ mWidget->SetPluginFocused(value);
+ }
+
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIWidget> mWidget;
+ nsCOMPtr<Element> mElement;
+};
+
+void
+HTMLObjectElement::OnFocusBlurPlugin(Element* aElement, bool aFocus)
+{
+ // In general we don't want to call nsIWidget::SetPluginFocused() for any
+ // Element that doesn't have a plugin running. But if SetPluginFocused(true)
+ // was just called for aElement while it had a plugin running, we want to
+ // make sure nsIWidget::SetPluginFocused(false) gets called for it now, even
+ // if aFocus is true.
+ if (aFocus) {
+ nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(aElement);
+ bool hasRunningPlugin = false;
+ if (olc) {
+ // nsIObjectLoadingContent::GetHasRunningPlugin() fails when
+ // nsContentUtils::IsCallerChrome() returns false (which it can do even
+ // when we're processing a trusted focus event). We work around this by
+ // calling nsObjectLoadingContent::HasRunningPlugin() directly.
+ hasRunningPlugin =
+ static_cast<nsObjectLoadingContent*>(olc.get())->HasRunningPlugin();
+ }
+ if (!hasRunningPlugin) {
+ aFocus = false;
+ }
+ }
+
+ if (aFocus || aElement == sLastFocused) {
+ if (!aFocus) {
+ sLastFocused = nullptr;
+ }
+ nsIWidget* widget = GetWidget(aElement);
+ if (widget) {
+ nsContentUtils::AddScriptRunner(
+ new PluginFocusSetter(widget, aFocus ? aElement : nullptr));
+ }
+ }
+}
+
+void
+HTMLObjectElement::HandlePluginCrashed(Element* aElement)
+{
+ OnFocusBlurPlugin(aElement, false);
+}
+
+void
+HTMLObjectElement::HandlePluginInstantiated(Element* aElement)
+{
+ // If aElement is already focused when a plugin is instantiated, we need
+ // to initiate a call to nsIWidget::SetPluginFocused(true). Otherwise
+ // keyboard input won't work in a click-to-play plugin until aElement
+ // loses focus and regains it.
+ nsIContent* focusedContent = nullptr;
+ nsFocusManager *fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ focusedContent = fm->GetFocusedContent();
+ }
+ if (SameCOMIdentity(focusedContent, aElement)) {
+ OnFocusBlurPlugin(aElement, true);
+ }
+}
+
+void
+HTMLObjectElement::HandleFocusBlurPlugin(Element* aElement,
+ WidgetEvent* aEvent)
+{
+ if (!aEvent->IsTrusted()) {
+ return;
+ }
+ switch (aEvent->mMessage) {
+ case eFocus: {
+ OnFocusBlurPlugin(aElement, true);
+ break;
+ }
+ case eBlur: {
+ OnFocusBlurPlugin(aElement, false);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+NS_IMETHODIMP
+HTMLObjectElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
+{
+ HandleFocusBlurPlugin(this, aVisitor.mEvent);
+ return NS_OK;
+}
+
+#endif // #ifdef XP_MACOSX
+
NS_IMETHODIMP
HTMLObjectElement::GetForm(nsIDOMHTMLFormElement **aForm)
{
@@ -149,6 +282,14 @@ void
HTMLObjectElement::UnbindFromTree(bool aDeep,
bool aNullParent)
{
+#ifdef XP_MACOSX
+ // When a page is reloaded (when an nsIDocument's content is removed), the
+ // focused element isn't necessarily sent an eBlur event. See
+ // nsFocusManager::ContentRemoved(). This means that a widget may think it
+ // still contains a focused plugin when it doesn't -- which in turn can
+ // disable text input in the browser window. See bug 1137229.
+ OnFocusBlurPlugin(this, false);
+#endif
nsObjectLoadingContent::UnbindFromTree(aDeep, aNullParent);
nsGenericHTMLFormElement::UnbindFromTree(aDeep, aNullParent);
}
diff --git a/dom/html/HTMLObjectElement.h b/dom/html/HTMLObjectElement.h
index 0e69ef5faa..6f0990918f 100644
--- a/dom/html/HTMLObjectElement.h
+++ b/dom/html/HTMLObjectElement.h
@@ -32,6 +32,18 @@ public:
NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLObjectElement, object)
virtual int32_t TabIndexDefault() override;
+#ifdef XP_MACOSX
+ // nsIDOMEventTarget
+ NS_IMETHOD PostHandleEvent(EventChainPostVisitor& aVisitor) override;
+ // Helper methods
+ static void OnFocusBlurPlugin(Element* aElement, bool aFocus);
+ static void HandleFocusBlurPlugin(Element* aElement, WidgetEvent* aEvent);
+ static void HandlePluginCrashed(Element* aElement);
+ static void HandlePluginInstantiated(Element* aElement);
+ // Weak pointer. Null if last action was blur.
+ static Element* sLastFocused;
+#endif
+
// Element
virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override;
diff --git a/dom/html/HTMLSharedObjectElement.cpp b/dom/html/HTMLSharedObjectElement.cpp
index d7eec215a0..2aeb51573a 100644
--- a/dom/html/HTMLSharedObjectElement.cpp
+++ b/dom/html/HTMLSharedObjectElement.cpp
@@ -16,6 +16,10 @@
#include "nsIScriptError.h"
#include "nsIWidget.h"
#include "nsContentUtils.h"
+#ifdef XP_MACOSX
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/dom/Event.h"
+#endif
#include "mozilla/dom/HTMLObjectElement.h"
@@ -38,6 +42,9 @@ HTMLSharedObjectElement::HTMLSharedObjectElement(already_AddRefed<mozilla::dom::
HTMLSharedObjectElement::~HTMLSharedObjectElement()
{
+#ifdef XP_MACOSX
+ HTMLObjectElement::OnFocusBlurPlugin(this, false);
+#endif
UnregisterActivityObserver();
DestroyImageLoadingContent();
}
@@ -89,6 +96,17 @@ NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
NS_IMPL_ELEMENT_CLONE(HTMLSharedObjectElement)
+#ifdef XP_MACOSX
+
+NS_IMETHODIMP
+HTMLSharedObjectElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
+{
+ HTMLObjectElement::HandleFocusBlurPlugin(this, aVisitor.mEvent);
+ return NS_OK;
+}
+
+#endif // #ifdef XP_MACOSX
+
void
HTMLSharedObjectElement::AsyncEventRunning(AsyncEventDispatcher* aEvent)
{
@@ -130,6 +148,14 @@ void
HTMLSharedObjectElement::UnbindFromTree(bool aDeep,
bool aNullParent)
{
+#ifdef XP_MACOSX
+ // When a page is reloaded (when an nsIDocument's content is removed), the
+ // focused element isn't necessarily sent an eBlur event. See
+ // nsFocusManager::ContentRemoved(). This means that a widget may think it
+ // still contains a focused plugin when it doesn't -- which in turn can
+ // disable text input in the browser window. See bug 1137229.
+ HTMLObjectElement::OnFocusBlurPlugin(this, false);
+#endif
nsObjectLoadingContent::UnbindFromTree(aDeep, aNullParent);
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
}
diff --git a/dom/html/HTMLSharedObjectElement.h b/dom/html/HTMLSharedObjectElement.h
index 09b44fa115..e550b9927c 100644
--- a/dom/html/HTMLSharedObjectElement.h
+++ b/dom/html/HTMLSharedObjectElement.h
@@ -31,6 +31,11 @@ public:
virtual int32_t TabIndexDefault() override;
+#ifdef XP_MACOSX
+ // nsIDOMEventTarget
+ NS_IMETHOD PostHandleEvent(EventChainPostVisitor& aVisitor) override;
+#endif
+
// nsIDOMHTMLAppletElement
NS_DECL_NSIDOMHTMLAPPLETELEMENT
diff --git a/dom/html/HTMLSummaryElement.cpp b/dom/html/HTMLSummaryElement.cpp
index b9de03fd11..ee3c07b20b 100644
--- a/dom/html/HTMLSummaryElement.cpp
+++ b/dom/html/HTMLSummaryElement.cpp
@@ -116,8 +116,15 @@ HTMLSummaryElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
return disallowOverridingFocusability;
}
- // The main summary element is focusable on all supported platforms.
+#ifdef XP_MACOSX
+ // The parent does not have strong opinion about the focusability of this main
+ // summary element, but we'd like to override it when mouse clicking on Mac OS
+ // like other form elements.
+ *aIsFocusable = !aWithMouse || nsFocusManager::sMouseFocusesFormControl;
+#else
+ // The main summary element is focusable on other platforms.
*aIsFocusable = true;
+#endif
// Give a chance to allow the subclass to override aIsFocusable.
return false;
diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp
index f7340ed7be..0f32d8fb42 100644
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -2170,6 +2170,10 @@ nsGenericHTMLFormElement::IsHTMLFocusable(bool aWithMouse,
return true;
}
+#ifdef XP_MACOSX
+ *aIsFocusable =
+ (!aWithMouse || nsFocusManager::sMouseFocusesFormControl) && *aIsFocusable;
+#endif
return false;
}
diff --git a/dom/media/GraphDriver.cpp b/dom/media/GraphDriver.cpp
index daf1ccc989..c4ee2ee2b1 100644
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -14,6 +14,10 @@
#include "webrtc/MediaEngineWebRTC.h"
#endif
+#ifdef XP_MACOSX
+#include <sys/sysctl.h>
+#endif
+
extern mozilla::LazyLogModule gMediaStreamGraphLog;
#define STREAM_LOG(type, msg) MOZ_LOG(gMediaStreamGraphLog, type, msg)
@@ -577,6 +581,32 @@ AudioCallbackDriver::~AudioCallbackDriver()
MOZ_ASSERT(mPromisesForOperation.IsEmpty());
}
+bool IsMacbookOrMacbookAir()
+{
+#ifdef XP_MACOSX
+ size_t len = 0;
+ sysctlbyname("hw.model", NULL, &len, NULL, 0);
+ if (len) {
+ UniquePtr<char[]> model(new char[len]);
+ // This string can be
+ // MacBook%d,%d for a normal MacBook
+ // MacBookPro%d,%d for a MacBook Pro
+ // MacBookAir%d,%d for a Macbook Air
+ sysctlbyname("hw.model", model.get(), &len, NULL, 0);
+ char* substring = strstr(model.get(), "MacBook");
+ if (substring) {
+ const size_t offset = strlen("MacBook");
+ if (strncmp(model.get() + offset, "Air", len - offset) ||
+ isdigit(model[offset + 1])) {
+ return true;
+ }
+ }
+ return false;
+ }
+#endif
+ return false;
+}
+
void
AudioCallbackDriver::Init()
{
@@ -613,6 +643,13 @@ AudioCallbackDriver::Init()
}
}
+ // Macbook and MacBook air don't have enough CPU to run very low latency
+ // MediaStreamGraphs, cap the minimal latency to 512 frames int this case.
+ if (IsMacbookOrMacbookAir()) {
+ latency_frames = std::max((uint32_t) 512, latency_frames);
+ }
+
+
input = output;
input.channels = mInputChannels; // change to support optional stereo capture
@@ -1026,6 +1063,44 @@ AudioCallbackDriver::MixerCallback(AudioDataValue* aMixedBuffer,
NS_WARNING_ASSERTION(written == aFrames - toWrite, "Dropping frames.");
};
+void AudioCallbackDriver::PanOutputIfNeeded(bool aMicrophoneActive)
+{
+#ifdef XP_MACOSX
+ cubeb_device* out;
+ int rv;
+ char name[128];
+ size_t length = sizeof(name);
+
+ rv = sysctlbyname("hw.model", name, &length, NULL, 0);
+ if (rv) {
+ return;
+ }
+
+ if (!strncmp(name, "MacBookPro", 10)) {
+ if (cubeb_stream_get_current_device(mAudioStream, &out) == CUBEB_OK) {
+ // Check if we are currently outputing sound on external speakers.
+ if (!strcmp(out->output_name, "ispk")) {
+ // Pan everything to the right speaker.
+ if (aMicrophoneActive) {
+ if (cubeb_stream_set_panning(mAudioStream, 1.0) != CUBEB_OK) {
+ NS_WARNING("Could not pan audio output to the right.");
+ }
+ } else {
+ if (cubeb_stream_set_panning(mAudioStream, 0.0) != CUBEB_OK) {
+ NS_WARNING("Could not pan audio output to the center.");
+ }
+ }
+ } else {
+ if (cubeb_stream_set_panning(mAudioStream, 0.0) != CUBEB_OK) {
+ NS_WARNING("Could not pan audio output to the center.");
+ }
+ }
+ cubeb_stream_device_destroy(mAudioStream, out);
+ }
+ }
+#endif
+}
+
void
AudioCallbackDriver::DeviceChangedCallback() {
// Tell the audio engine the device has changed, it might want to reset some
@@ -1034,6 +1109,9 @@ AudioCallbackDriver::DeviceChangedCallback() {
if (mAudioInput) {
mAudioInput->DeviceChanged();
}
+#ifdef XP_MACOSX
+ PanOutputIfNeeded(mMicrophoneActive);
+#endif
}
void
@@ -1042,6 +1120,10 @@ AudioCallbackDriver::SetMicrophoneActive(bool aActive)
MonitorAutoLock mon(mGraphImpl->GetMonitor());
mMicrophoneActive = aActive;
+
+#ifdef XP_MACOSX
+ PanOutputIfNeeded(mMicrophoneActive);
+#endif
}
uint32_t
diff --git a/dom/media/GraphDriver.h b/dom/media/GraphDriver.h
index f2a514b328..bb4f2689b8 100644
--- a/dom/media/GraphDriver.h
+++ b/dom/media/GraphDriver.h
@@ -457,6 +457,11 @@ public:
void CompleteAudioContextOperations(AsyncCubebOperation aOperation);
private:
/**
+ * On certain MacBookPro, the microphone is located near the left speaker.
+ * We need to pan the sound output to the right speaker if we are using the
+ * mic and the built-in speaker, or we will have terrible echo. */
+ void PanOutputIfNeeded(bool aMicrophoneActive);
+ /**
* This is called when the output device used by the cubeb stream changes. */
void DeviceChangedCallback();
/* Start the cubeb stream */
diff --git a/dom/media/eme/MediaKeySystemAccessManager.cpp b/dom/media/eme/MediaKeySystemAccessManager.cpp
index a1e1254ad4..ed31059e22 100644
--- a/dom/media/eme/MediaKeySystemAccessManager.cpp
+++ b/dom/media/eme/MediaKeySystemAccessManager.cpp
@@ -14,6 +14,9 @@
#ifdef XP_WIN
#include "mozilla/WindowsVersion.h"
#endif
+#ifdef XP_MACOSX
+#include "nsCocoaFeatures.h"
+#endif
#include "nsPrintfCString.h"
namespace mozilla {
diff --git a/dom/media/gmp/GMPChild.cpp b/dom/media/gmp/GMPChild.cpp
index eb18037364..fa6f2f4c83 100644
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -112,12 +112,14 @@ GetPluginFile(const nsAString& aPluginPath,
nsAutoString baseName;
GetFileBase(aPluginPath, aLibDirectory, aLibFile, baseName);
-#if defined(OS_POSIX)
+#if defined(XP_MACOSX)
+ nsAutoString binaryName = NS_LITERAL_STRING("lib") + baseName + NS_LITERAL_STRING(".dylib");
+#elif defined(OS_POSIX)
nsAutoString binaryName = NS_LITERAL_STRING("lib") + baseName + NS_LITERAL_STRING(".so");
#elif defined(XP_WIN)
nsAutoString binaryName = baseName + NS_LITERAL_STRING(".dll");
#else
-#error Unsupported O.S.
+#error not defined
#endif
aLibFile->AppendRelativePath(binaryName);
return true;
diff --git a/dom/media/gmp/rlz/GMPDeviceBinding.cpp b/dom/media/gmp/rlz/GMPDeviceBinding.cpp
index 0871d2e4ee..04def8e8e0 100644
--- a/dom/media/gmp/rlz/GMPDeviceBinding.cpp
+++ b/dom/media/gmp/rlz/GMPDeviceBinding.cpp
@@ -32,6 +32,14 @@
#include "windows.h"
#endif
+#ifdef XP_MACOSX
+#include <assert.h>
+#ifdef HASH_NODE_ID_WITH_DEVICE_ID
+#include <unistd.h>
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#endif
+#endif
#endif // HASH_NODE_ID_WITH_DEVICE_ID
@@ -75,6 +83,46 @@ GetStackAfterCurrentFrame(uint8_t** aOutTop, uint8_t** aOutBottom)
}
#endif
+#if defined(XP_MACOSX) && defined(HASH_NODE_ID_WITH_DEVICE_ID)
+static mach_vm_address_t
+RegionContainingAddress(mach_vm_address_t aAddress)
+{
+ mach_port_t task;
+ kern_return_t kr = task_for_pid(mach_task_self(), getpid(), &task);
+ if (kr != KERN_SUCCESS) {
+ return 0;
+ }
+
+ mach_vm_address_t address = aAddress;
+ mach_vm_size_t size;
+ vm_region_basic_info_data_64_t info;
+ mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
+ mach_port_t object_name;
+ kr = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO_64,
+ reinterpret_cast<vm_region_info_t>(&info), &count,
+ &object_name);
+ if (kr != KERN_SUCCESS || size == 0
+ || address > aAddress || address + size <= aAddress) {
+ // mach_vm_region failed, or couldn't find region at given address.
+ return 0;
+ }
+
+ return address;
+}
+
+MOZ_NEVER_INLINE
+static bool
+GetStackAfterCurrentFrame(uint8_t** aOutTop, uint8_t** aOutBottom)
+{
+ mach_vm_address_t stackFrame =
+ reinterpret_cast<mach_vm_address_t>(__builtin_frame_address(0));
+ *aOutTop = reinterpret_cast<uint8_t*>(stackFrame);
+ // Kernel code shows that stack is always a single region.
+ *aOutBottom = reinterpret_cast<uint8_t*>(RegionContainingAddress(stackFrame));
+ return *aOutBottom && (*aOutBottom < *aOutTop);
+}
+#endif
+
#ifdef HASH_NODE_ID_WITH_DEVICE_ID
static void SecureMemset(void* start, uint8_t value, size_t size)
{
diff --git a/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp b/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
index 513a8998b8..4671499e5d 100644
--- a/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
@@ -26,6 +26,14 @@ public:
static FFmpegLibWrapper sLibAV;
static const char* sLibs[] = {
+#if defined(XP_DARWIN)
+ "libavcodec.58.dylib",
+ "libavcodec.57.dylib",
+ "libavcodec.56.dylib",
+ "libavcodec.55.dylib",
+ "libavcodec.54.dylib",
+ "libavcodec.53.dylib",
+#else
"libavcodec.so.58",
"libavcodec-ffmpeg.so.58",
"libavcodec-ffmpeg.so.57",
@@ -35,6 +43,7 @@ static const char* sLibs[] = {
"libavcodec.so.55",
"libavcodec.so.54",
"libavcodec.so.53",
+#endif
};
/* static */ bool
diff --git a/dom/media/standalone/moz.build b/dom/media/standalone/moz.build
index 5ec7e82c20..7ef15adaa9 100644
--- a/dom/media/standalone/moz.build
+++ b/dom/media/standalone/moz.build
@@ -13,6 +13,9 @@ SOURCES += [
'../VideoSegment.cpp',
]
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ UNIFIED_SOURCES += ['../systemservices/OSXRunLoopSingleton.cpp']
+
LOCAL_INCLUDES += [
'/caps',
'/dom/base',
diff --git a/dom/media/systemservices/LoadMonitor.cpp b/dom/media/systemservices/LoadMonitor.cpp
index 9ef01999c0..c05d43b1e5 100644
--- a/dom/media/systemservices/LoadMonitor.cpp
+++ b/dom/media/systemservices/LoadMonitor.cpp
@@ -31,6 +31,12 @@
#include <unistd.h>
#endif
+#ifdef XP_MACOSX
+#include <mach/mach_host.h>
+#include <mach/mach_init.h>
+#include <mach/host_info.h>
+#endif
+
#if defined(__DragonFly__) || defined(__FreeBSD__) \
|| defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/sysctl.h>
@@ -407,6 +413,27 @@ nsresult RTCLoadInfo::UpdateSystemLoad()
cpu_times,
&mSystemLoad);
return NS_OK;
+#elif defined(XP_MACOSX)
+ mach_msg_type_number_t info_cnt = HOST_CPU_LOAD_INFO_COUNT;
+ host_cpu_load_info_data_t load_info;
+ kern_return_t rv = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO,
+ (host_info_t)(&load_info), &info_cnt);
+
+ if (rv != KERN_SUCCESS || info_cnt != HOST_CPU_LOAD_INFO_COUNT) {
+ LOG(("Error from mach/host_statistics call"));
+ return NS_ERROR_FAILURE;
+ }
+
+ const uint64_t cpu_times = load_info.cpu_ticks[CPU_STATE_NICE]
+ + load_info.cpu_ticks[CPU_STATE_SYSTEM]
+ + load_info.cpu_ticks[CPU_STATE_USER];
+ const uint64_t total_times = cpu_times + load_info.cpu_ticks[CPU_STATE_IDLE];
+
+ UpdateCpuLoad(mTicksPerInterval,
+ total_times,
+ cpu_times,
+ &mSystemLoad);
+ return NS_OK;
#elif defined(__DragonFly__) || defined(__FreeBSD__) \
|| defined(__NetBSD__) || defined(__OpenBSD__)
#if defined(__NetBSD__)
diff --git a/dom/media/systemservices/OSXRunLoopSingleton.cpp b/dom/media/systemservices/OSXRunLoopSingleton.cpp
new file mode 100644
index 0000000000..6211d5c127
--- /dev/null
+++ b/dom/media/systemservices/OSXRunLoopSingleton.cpp
@@ -0,0 +1,44 @@
+/* -*- 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;
+}
diff --git a/dom/media/systemservices/OSXRunLoopSingleton.h b/dom/media/systemservices/OSXRunLoopSingleton.h
new file mode 100644
index 0000000000..d06365e146
--- /dev/null
+++ b/dom/media/systemservices/OSXRunLoopSingleton.h
@@ -0,0 +1,25 @@
+/* -*- 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_
diff --git a/dom/media/systemservices/moz.build b/dom/media/systemservices/moz.build
index 8fb5e54a19..402a6988c0 100644
--- a/dom/media/systemservices/moz.build
+++ b/dom/media/systemservices/moz.build
@@ -29,6 +29,10 @@ if CONFIG['OS_TARGET'] == 'WINNT':
else:
DEFINES['WEBRTC_POSIX'] = True
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += ['OSXRunLoopSingleton.cpp']
+ EXPORTS += ['OSXRunLoopSingleton.h']
+
if CONFIG['_MSC_VER']:
DEFINES['__PRETTY_FUNCTION__'] = '__FUNCSIG__'
diff --git a/dom/media/webaudio/AudioContext.cpp b/dom/media/webaudio/AudioContext.cpp
index 3c7958349b..75f57a630b 100755
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -873,6 +873,7 @@ AudioContext::OnStateChanged(void* aPromise, AudioContextState aNewState)
}
#ifndef WIN32 // Bug 1170547
+#ifndef XP_MACOSX
#ifdef DEBUG
if (!((mAudioContextState == AudioContextState::Suspended &&
@@ -891,6 +892,7 @@ AudioContext::OnStateChanged(void* aPromise, AudioContextState aNewState)
}
#endif // DEBUG
+#endif // XP_MACOSX
#endif // WIN32
MOZ_ASSERT(
diff --git a/dom/media/webrtc/MediaEngineCameraVideoSource.cpp b/dom/media/webrtc/MediaEngineCameraVideoSource.cpp
index e63a9afded..e1e572724f 100644
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.cpp
@@ -339,6 +339,13 @@ MediaEngineCameraVideoSource::SetName(nsString aName)
facingMode = VideoFacingModeEnum::User;
}
#endif // ANDROID
+#ifdef XP_MACOSX
+ // Kludge to test user-facing cameras on OSX.
+ if (aName.Find(NS_LITERAL_STRING("Face")) != -1) {
+ hasFacingMode = true;
+ facingMode = VideoFacingModeEnum::User;
+ }
+#endif
#ifdef XP_WIN
// The cameras' name of Surface book are "Microsoft Camera Front" and
// "Microsoft Camera Rear" respectively.
diff --git a/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerModule.cpp b/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerModule.cpp
new file mode 100644
index 0000000000..ef69170003
--- /dev/null
+++ b/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerModule.cpp
@@ -0,0 +1,56 @@
+/* -*- 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;
diff --git a/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.h b/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.h
new file mode 100644
index 0000000000..ba04f0fecb
--- /dev/null
+++ b/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.h
@@ -0,0 +1,43 @@
+/* -*- 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
diff --git a/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm b/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm
new file mode 100644
index 0000000000..ec752e00f0
--- /dev/null
+++ b/dom/media/webspeech/synth/cocoa/OSXSpeechSynthesizerService.mm
@@ -0,0 +1,498 @@
+/* -*- 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
diff --git a/dom/media/webspeech/synth/cocoa/moz.build b/dom/media/webspeech/synth/cocoa/moz.build
new file mode 100644
index 0000000000..6953a81e95
--- /dev/null
+++ b/dom/media/webspeech/synth/cocoa/moz.build
@@ -0,0 +1,11 @@
+# -*- 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'
diff --git a/dom/media/webspeech/synth/moz.build b/dom/media/webspeech/synth/moz.build
index 9784686dfe..6603689261 100644
--- a/dom/media/webspeech/synth/moz.build
+++ b/dom/media/webspeech/synth/moz.build
@@ -32,6 +32,9 @@ SOURCES += [
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
DIRS += ['windows']
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ DIRS += ['cocoa']
+
if CONFIG['MOZ_SYNTH_SPEECHD']:
DIRS += ['speechd']
diff --git a/dom/moz.build b/dom/moz.build
index 17b0fed15b..ad278beb9c 100644
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -111,6 +111,6 @@ TEST_DIRS += [
'imptests',
]
-if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'windows'):
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'cocoa', 'windows'):
TEST_DIRS += ['plugins/test']
diff --git a/dom/plugins/base/PluginPRLibrary.cpp b/dom/plugins/base/PluginPRLibrary.cpp
index e6a202c244..57c6c57ab3 100644
--- a/dom/plugins/base/PluginPRLibrary.cpp
+++ b/dom/plugins/base/PluginPRLibrary.cpp
@@ -24,7 +24,7 @@ static int gNotOptimized;
using namespace mozilla::layers;
namespace mozilla {
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
nsresult
PluginPRLibrary::NP_Initialize(NPNetscapeFuncs* bFuncs,
NPPluginFuncs* pFuncs, NPError* error)
@@ -110,7 +110,7 @@ nsresult
PluginPRLibrary::NP_GetValue(void *future, NPPVariable aVariable,
void *aValue, NPError* error)
{
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
if (mNP_GetValue) {
*error = mNP_GetValue(future, aVariable, aValue);
} else {
@@ -125,7 +125,7 @@ PluginPRLibrary::NP_GetValue(void *future, NPPVariable aVariable,
#endif
}
-#if defined(XP_WIN)
+#if defined(XP_WIN) || defined(XP_MACOSX)
nsresult
PluginPRLibrary::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
{
@@ -231,7 +231,17 @@ PluginPRLibrary::GetImageContainer(NPP instance, ImageContainer** aContainer)
return NS_ERROR_NOT_IMPLEMENTED;
}
-#if defined(XP_WIN)
+#if defined(XP_MACOSX)
+nsresult
+PluginPRLibrary::IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing)
+{
+ nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+ NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER);
+ *aDrawing = false;
+ return NS_OK;
+}
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
nsresult
PluginPRLibrary::ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor)
{
diff --git a/dom/plugins/base/PluginPRLibrary.h b/dom/plugins/base/PluginPRLibrary.h
index 4934b37bbb..ffd3de934e 100644
--- a/dom/plugins/base/PluginPRLibrary.h
+++ b/dom/plugins/base/PluginPRLibrary.h
@@ -17,17 +17,17 @@ class PluginPRLibrary : public PluginLibrary
{
public:
PluginPRLibrary(const char* aFilePath, PRLibrary* aLibrary) :
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
mNP_Initialize(nullptr),
#else
mNP_Initialize(nullptr),
#endif
mNP_Shutdown(nullptr),
mNP_GetMIMEDescription(nullptr),
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
mNP_GetValue(nullptr),
#endif
-#if defined(XP_WIN)
+#if defined(XP_WIN) || defined(XP_MACOSX)
mNP_GetEntryPoints(nullptr),
#endif
mNPP_New(nullptr),
@@ -60,17 +60,19 @@ public:
mNP_GetMIMEDescription = (NP_GetMIMEDescriptionFunc)
PR_FindFunctionSymbol(mLibrary, "NP_GetMIMEDescription");
+#ifndef XP_MACOSX
if (!mNP_GetMIMEDescription)
return false;
+#endif
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
mNP_GetValue = (NP_GetValueFunc)
PR_FindFunctionSymbol(mLibrary, "NP_GetValue");
if (!mNP_GetValue)
return false;
#endif
-#if defined(XP_WIN)
+#if defined(XP_WIN) || defined(XP_MACOSX)
mNP_GetEntryPoints = (NP_GetEntryPointsFunc)
PR_FindFunctionSymbol(mLibrary, "NP_GetEntryPoints");
if (!mNP_GetEntryPoints)
@@ -79,7 +81,7 @@ public:
return true;
}
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
virtual nsresult NP_Initialize(NPNetscapeFuncs* aNetscapeFuncs,
NPPluginFuncs* aFuncs, NPError* aError) override;
#else
@@ -93,7 +95,7 @@ public:
virtual nsresult NP_GetValue(void* aFuture, NPPVariable aVariable,
void* aValue, NPError* aError) override;
-#if defined(XP_WIN)
+#if defined(XP_WIN) || defined(XP_MACOSX)
virtual nsresult NP_GetEntryPoints(NPPluginFuncs* aFuncs, NPError* aError) override;
#endif
@@ -110,7 +112,10 @@ public:
virtual nsresult GetImageContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) override;
virtual nsresult GetImageSize(NPP aInstance, nsIntSize* aSize) override;
virtual bool IsOOP() override { return false; }
-#if defined(XP_WIN)
+#if defined(XP_MACOSX)
+ virtual nsresult IsRemoteDrawingCoreAnimation(NPP aInstance, bool* aDrawing) override;
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
virtual nsresult ContentsScaleFactorChanged(NPP aInstance, double aContentsScaleFactor) override;
#endif
virtual nsresult SetBackgroundUnknown(NPP instance) override;
@@ -134,10 +139,10 @@ private:
NP_InitializeFunc mNP_Initialize;
NP_ShutdownFunc mNP_Shutdown;
NP_GetMIMEDescriptionFunc mNP_GetMIMEDescription;
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
NP_GetValueFunc mNP_GetValue;
#endif
-#if defined(XP_WIN)
+#if defined(XP_WIN) || defined(XP_MACOSX)
NP_GetEntryPointsFunc mNP_GetEntryPoints;
#endif
NPP_NewProcPtr mNPP_New;
diff --git a/dom/plugins/base/moz.build b/dom/plugins/base/moz.build
index 11649a8626..08f87d56a3 100644
--- a/dom/plugins/base/moz.build
+++ b/dom/plugins/base/moz.build
@@ -56,6 +56,11 @@ if CONFIG['OS_ARCH'] == 'WINNT':
'nsPluginNativeWindowWin.cpp',
'nsPluginsDirWin.cpp',
]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'nsPluginNativeWindow.cpp',
+ 'nsPluginsDirDarwin.cpp',
+ ]
else:
SOURCES += [
'nsPluginsDirUnix.cpp',
@@ -77,6 +82,7 @@ LOCAL_INCLUDES += [
'/layout/xul',
'/netwerk/base',
'/widget',
+ '/widget/cocoa',
'/xpcom/base',
]
diff --git a/dom/plugins/base/npapi.h b/dom/plugins/base/npapi.h
index de58f818c4..aa6a66b2bd 100644
--- a/dom/plugins/base/npapi.h
+++ b/dom/plugins/base/npapi.h
@@ -22,6 +22,25 @@
#endif
#endif
+#if defined(__APPLE_CC__) && !defined(XP_UNIX)
+#ifndef XP_MACOSX
+#define XP_MACOSX 1
+#endif
+#endif
+
+#if defined(XP_MACOSX) && defined(__LP64__)
+#define NP_NO_QUICKDRAW
+#define NP_NO_CARBON
+#endif
+
+#if defined(XP_MACOSX)
+#include <ApplicationServices/ApplicationServices.h>
+#include <OpenGL/OpenGL.h>
+#ifndef NP_NO_CARBON
+#include <Carbon/Carbon.h>
+#endif
+#endif
+
#if defined(XP_UNIX)
#include <stdio.h>
#if defined(MOZ_X11)
@@ -98,6 +117,12 @@ typedef char* NPMIMEType;
/* Structures and definitions */
/*----------------------------------------------------------------------*/
+#if !defined(__LP64__)
+#if defined(XP_MACOSX)
+#pragma options align=mac68k
+#endif
+#endif /* __LP64__ */
+
/*
* NPP is a plug-in's opaque instance handle
*/
@@ -255,6 +280,15 @@ typedef struct _NPAudioDeviceChangeDetails
typedef enum {
NPDrawingModelDUMMY
+#if defined(XP_MACOSX)
+#ifndef NP_NO_QUICKDRAW
+ , NPDrawingModelQuickDraw = 0
+#endif
+ , NPDrawingModelCoreGraphics = 1
+ , NPDrawingModelOpenGL = 2
+ , NPDrawingModelCoreAnimation = 3
+ , NPDrawingModelInvalidatingCoreAnimation = 4
+#endif
#if defined(XP_WIN)
, NPDrawingModelSyncWin = 5
#endif
@@ -267,6 +301,15 @@ typedef enum {
#endif
} NPDrawingModel;
+#ifdef XP_MACOSX
+typedef enum {
+#ifndef NP_NO_CARBON
+ NPEventModelCarbon = 0,
+#endif
+ NPEventModelCocoa = 1
+} NPEventModel;
+#endif
+
/*
* The following masks are applied on certain platforms to NPNV and
* NPPV selectors that pass around pointers to COM interfaces. Newer
@@ -294,7 +337,12 @@ typedef enum {
#define _NP_ABI_MIXIN_FOR_GCC3 0
#endif
+#if defined(XP_MACOSX)
+#define NP_ABI_MACHO_MASK 0x01000000
+#define _NP_ABI_MIXIN_FOR_MACHO NP_ABI_MACHO_MASK
+#else
#define _NP_ABI_MIXIN_FOR_MACHO 0
+#endif
#define NP_ABI_MASK (_NP_ABI_MIXIN_FOR_GCC3 | _NP_ABI_MIXIN_FOR_MACHO)
@@ -345,6 +393,12 @@ typedef enum {
/* Used for negotiating drawing models */
NPPVpluginDrawingModel = 1000
+#if defined(XP_MACOSX)
+ /* Used for negotiating event models */
+ , NPPVpluginEventModel = 1001
+ /* In the NPDrawingModelCoreAnimation drawing model, the browser asks the plug-in for a Core Animation layer. */
+ , NPPVpluginCoreAnimationLayer = 1003
+#endif
/* Notification that the plugin just started or stopped playing audio */
, NPPVpluginIsPlayingAudio = 4000
#if defined(XP_WIN)
@@ -388,18 +442,39 @@ typedef enum {
NPNVCSSZoomFactor = 23,
NPNVpluginDrawingModel = 1000 /* Get the current drawing model (NPDrawingModel) */
-#if defined(XP_WIN)
+#if defined(XP_MACOSX) || defined(XP_WIN)
, NPNVcontentsScaleFactor = 1001
#endif
+#if defined(XP_MACOSX)
+#ifndef NP_NO_QUICKDRAW
+ , NPNVsupportsQuickDrawBool = 2000
+#endif
+ , NPNVsupportsCoreGraphicsBool = 2001
+ , NPNVsupportsOpenGLBool = 2002
+ , NPNVsupportsCoreAnimationBool = 2003
+ , NPNVsupportsInvalidatingCoreAnimationBool = 2004
+#endif
, NPNVsupportsAsyncBitmapSurfaceBool = 2007
#if defined(XP_WIN)
, NPNVsupportsAsyncWindowsDXGISurfaceBool = 2008
, NPNVpreferredDXGIAdapter = 2009
#endif
+#if defined(XP_MACOSX)
+#ifndef NP_NO_CARBON
+ , NPNVsupportsCarbonBool = 3000 /* TRUE if the browser supports the Carbon event model */
+#endif
+ , NPNVsupportsCocoaBool = 3001 /* TRUE if the browser supports the Cocoa event model */
+ , NPNVsupportsUpdatedCocoaTextInputBool = 3002 /* TRUE if the browser supports the updated
+ Cocoa text input specification. */
+#endif
, NPNVmuteAudioBool = 4000 /* Request that the browser wants to mute or unmute the plugin */
#if defined(XP_WIN)
, NPNVaudioDeviceChangeDetails = 4001 /* Provides information about the new default audio device */
#endif
+#if defined(XP_MACOSX)
+ , NPNVsupportsCompositingCoreAnimationPluginsBool = 74656 /* TRUE if the browser supports
+ CA model compositing */
+#endif
} NPNVariable;
typedef enum {
@@ -434,7 +509,7 @@ typedef struct _NPWindow
uint32_t width; /* Maximum window size */
uint32_t height;
NPRect clipRect; /* Clipping rectangle in port coordinates */
-#if defined(XP_UNIX) || defined(XP_SYMBIAN)
+#if (defined(XP_UNIX) || defined(XP_SYMBIAN)) && !defined(XP_MACOSX)
void * ws_info; /* Platform-dependent additional data */
#endif /* XP_UNIX */
NPWindowType type; /* Is this a window or a drawable? */
@@ -480,7 +555,11 @@ typedef struct _NPPrint
} print;
} NPPrint;
-#if defined(XP_SYMBIAN)
+#if defined(XP_MACOSX)
+#ifndef NP_NO_CARBON
+typedef EventRecord NPEvent;
+#endif
+#elif defined(XP_SYMBIAN)
typedef QEvent NPEvent;
#elif defined(XP_WIN)
typedef struct _NPEvent
@@ -495,7 +574,13 @@ typedef XEvent NPEvent;
typedef void* NPEvent;
#endif
-#if defined(XP_WIN)
+#if defined(XP_MACOSX)
+typedef void* NPRegion;
+#ifndef NP_NO_QUICKDRAW
+typedef RgnHandle NPQDRegion;
+#endif
+typedef CGPathRef NPCGRegion;
+#elif defined(XP_WIN)
typedef HRGN NPRegion;
#elif defined(XP_UNIX) && defined(MOZ_X11)
typedef Region NPRegion;
@@ -509,7 +594,11 @@ typedef struct _NPNSString NPNSString;
typedef struct _NPNSWindow NPNSWindow;
typedef struct _NPNSMenu NPNSMenu;
+#if defined(XP_MACOSX)
+typedef NPNSMenu NPMenu;
+#else
typedef void *NPMenu;
+#endif
typedef enum {
NPCoordinateSpacePlugin = 1,
@@ -519,6 +608,112 @@ typedef enum {
NPCoordinateSpaceFlippedScreen
} NPCoordinateSpace;
+#if defined(XP_MACOSX)
+
+#ifndef NP_NO_QUICKDRAW
+typedef struct NP_Port
+{
+ CGrafPtr port;
+ int32_t portx; /* position inside the topmost window */
+ int32_t porty;
+} NP_Port;
+#endif /* NP_NO_QUICKDRAW */
+
+/*
+ * NP_CGContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelCoreGraphics
+ * as its drawing model.
+ */
+
+typedef struct NP_CGContext
+{
+ CGContextRef context;
+ void *window; /* A WindowRef under the Carbon event model. */
+} NP_CGContext;
+
+/*
+ * NP_GLContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelOpenGL as its
+ * drawing model.
+ */
+
+typedef struct NP_GLContext
+{
+ CGLContextObj context;
+#ifdef NP_NO_CARBON
+ NPNSWindow *window;
+#else
+ void *window; /* Can be either an NSWindow or a WindowRef depending on the event model */
+#endif
+} NP_GLContext;
+
+typedef enum {
+ NPCocoaEventDrawRect = 1,
+ NPCocoaEventMouseDown,
+ NPCocoaEventMouseUp,
+ NPCocoaEventMouseMoved,
+ NPCocoaEventMouseEntered,
+ NPCocoaEventMouseExited,
+ NPCocoaEventMouseDragged,
+ NPCocoaEventKeyDown,
+ NPCocoaEventKeyUp,
+ NPCocoaEventFlagsChanged,
+ NPCocoaEventFocusChanged,
+ NPCocoaEventWindowFocusChanged,
+ NPCocoaEventScrollWheel,
+ NPCocoaEventTextInput
+} NPCocoaEventType;
+
+typedef struct _NPCocoaEvent {
+ NPCocoaEventType type;
+ uint32_t version;
+ union {
+ struct {
+ uint32_t modifierFlags;
+ double pluginX;
+ double pluginY;
+ int32_t buttonNumber;
+ int32_t clickCount;
+ double deltaX;
+ double deltaY;
+ double deltaZ;
+ } mouse;
+ struct {
+ uint32_t modifierFlags;
+ NPNSString *characters;
+ NPNSString *charactersIgnoringModifiers;
+ NPBool isARepeat;
+ uint16_t keyCode;
+ } key;
+ struct {
+ CGContextRef context;
+ double x;
+ double y;
+ double width;
+ double height;
+ } draw;
+ struct {
+ NPBool hasFocus;
+ } focus;
+ struct {
+ NPNSString *text;
+ } text;
+ } data;
+} NPCocoaEvent;
+
+#ifndef NP_NO_CARBON
+/* Non-standard event types that can be passed to HandleEvent */
+enum NPEventType {
+ NPEventType_GetFocusEvent = (osEvt + 16),
+ NPEventType_LoseFocusEvent,
+ NPEventType_AdjustCursorEvent,
+ NPEventType_MenuCommandEvent,
+ NPEventType_ClippingChangedEvent,
+ NPEventType_ScrollingBeginsEvent = 1000,
+ NPEventType_ScrollingEndsEvent
+};
+#endif /* NP_NO_CARBON */
+
+#endif /* XP_MACOSX */
+
/*
* Values for mode passed to NPP_New:
*/
@@ -541,6 +736,12 @@ typedef enum {
#define NP_CLEAR_ALL 0
#define NP_CLEAR_CACHE (1 << 0)
+#if !defined(__LP64__)
+#if defined(XP_MACOSX)
+#pragma options align=reset
+#endif
+#endif /* __LP64__ */
+
/*----------------------------------------------------------------------*/
/* Error and Reason Code definitions */
/*----------------------------------------------------------------------*/
diff --git a/dom/plugins/base/npfunctions.h b/dom/plugins/base/npfunctions.h
index 83c8a9762c..dca542b620 100644
--- a/dom/plugins/base/npfunctions.h
+++ b/dom/plugins/base/npfunctions.h
@@ -182,6 +182,31 @@ typedef struct _NPNetscapeFuncs {
NPN_SetCurrentAsyncSurfacePtr setcurrentasyncsurface;
} NPNetscapeFuncs;
+#ifdef XP_MACOSX
+/*
+ * Mac OS X version(s) of NP_GetMIMEDescription(const char *)
+ * These can be called to retreive MIME information from the plugin dynamically
+ *
+ * Note: For compatibility with Quicktime, BPSupportedMIMEtypes is another way
+ * to get mime info from the plugin only on OSX and may not be supported
+ * in furture version -- use NP_GetMIMEDescription instead
+ */
+enum
+{
+ kBPSupportedMIMETypesStructVers_1 = 1
+};
+typedef struct _BPSupportedMIMETypes
+{
+ SInt32 structVersion; /* struct version */
+ Handle typeStrings; /* STR# formated handle, allocated by plug-in */
+ Handle infoStrings; /* STR# formated handle, allocated by plug-in */
+} BPSupportedMIMETypes;
+OSErr BP_GetSupportedMIMETypes(BPSupportedMIMETypes *mimeInfo, UInt32 flags);
+#define NP_GETMIMEDESCRIPTION_NAME "NP_GetMIMEDescription"
+typedef const char* (*NP_GetMIMEDescriptionProcPtr)(void);
+typedef OSErr (*BP_GetSupportedMIMETypesProcPtr)(BPSupportedMIMETypes*, UInt32);
+#endif
+
#if defined(_WIN32)
#define OSCALL WINAPI
#else
@@ -226,8 +251,15 @@ typedef char* (*NP_GetPluginVersionFunc)(void);
NP_EXPORT(char*) NP_GetPluginVersion(void);
typedef const char* (*NP_GetMIMEDescriptionFunc)(void);
NP_EXPORT(const char*) NP_GetMIMEDescription(void);
+#ifdef XP_MACOSX
+typedef NPError (*NP_InitializeFunc)(NPNetscapeFuncs*);
+NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs);
+typedef NPError (*NP_GetEntryPointsFunc)(NPPluginFuncs*);
+NP_EXPORT(NPError) NP_GetEntryPoints(NPPluginFuncs* pFuncs);
+#else
typedef NPError (*NP_InitializeFunc)(NPNetscapeFuncs*, NPPluginFuncs*);
NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs);
+#endif
typedef NPError (*NP_ShutdownFunc)(void);
NP_EXPORT(NPError) NP_Shutdown(void);
typedef NPError (*NP_GetValueFunc)(void *, NPPVariable, void *);
diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp
index b2e931483f..da4f09914f 100644
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -269,6 +269,14 @@ nsNPAPIPlugin::CreatePlugin(nsPluginTag *aPluginTag, nsNPAPIPlugin** aResult)
return NS_ERROR_FAILURE;
}
+#ifdef XP_MACOSX
+ if (!pluginLib->HasRequiredFunctions()) {
+ NS_WARNING("Not all necessary functions exposed by plugin, it will not load.");
+ delete pluginLib;
+ return NS_ERROR_FAILURE;
+ }
+#endif
+
plugin->mLibrary = pluginLib;
pluginLib->SetPlugin(plugin);
@@ -286,6 +294,19 @@ nsNPAPIPlugin::CreatePlugin(nsPluginTag *aPluginTag, nsNPAPIPlugin** aResult)
if (rv != NS_OK || pluginCallError != NPERR_NO_ERROR) {
return NS_ERROR_FAILURE;
}
+#elif defined(XP_MACOSX)
+ // NP_Initialize must be called before NP_GetEntryPoints on Mac OS X.
+ // We need to match WebKit's behavior.
+ NPError pluginCallError;
+ nsresult rv = pluginLib->NP_Initialize(&sBrowserFuncs, &pluginCallError);
+ if (rv != NS_OK || pluginCallError != NPERR_NO_ERROR) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = pluginLib->NP_GetEntryPoints(&plugin->mPluginFuncs, &pluginCallError);
+ if (rv != NS_OK || pluginCallError != NPERR_NO_ERROR) {
+ return NS_ERROR_FAILURE;
+ }
#else
NPError pluginCallError;
nsresult rv = pluginLib->NP_Initialize(&sBrowserFuncs, &plugin->mPluginFuncs, &pluginCallError);
@@ -1708,7 +1729,7 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
// cases for android_npapi.h's non-standard ANPInterface values.
switch (static_cast<int>(variable)) {
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
case NPNVxDisplay : {
#if defined(MOZ_X11)
if (npp) {
@@ -1833,7 +1854,8 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
}
case NPNVSupportsWindowless: {
-#if defined(XP_WIN) || (defined(MOZ_X11) && defined(MOZ_WIDGET_GTK))
+#if defined(XP_WIN) || defined(XP_MACOSX) || \
+ (defined(MOZ_X11) && defined(MOZ_WIDGET_GTK))
*(NPBool*)result = true;
#else
*(NPBool*)result = false;
@@ -1901,7 +1923,72 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
return *(char**)result ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
}
-#if defined(XP_WIN)
+#ifdef XP_MACOSX
+ case NPNVpluginDrawingModel: {
+ if (npp) {
+ nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance*)npp->ndata;
+ if (inst) {
+ NPDrawingModel drawingModel;
+ inst->GetDrawingModel((int32_t*)&drawingModel);
+ *(NPDrawingModel*)result = drawingModel;
+ return NPERR_NO_ERROR;
+ }
+ }
+ return NPERR_GENERIC_ERROR;
+ }
+
+#ifndef NP_NO_QUICKDRAW
+ case NPNVsupportsQuickDrawBool: {
+ *(NPBool*)result = false;
+
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+ case NPNVsupportsCoreGraphicsBool: {
+ *(NPBool*)result = true;
+
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsCoreAnimationBool: {
+ *(NPBool*)result = true;
+
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsInvalidatingCoreAnimationBool: {
+ *(NPBool*)result = true;
+
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsCompositingCoreAnimationPluginsBool: {
+ *(NPBool*)result = PR_TRUE;
+
+ return NPERR_NO_ERROR;
+ }
+
+#ifndef NP_NO_CARBON
+ case NPNVsupportsCarbonBool: {
+ *(NPBool*)result = false;
+
+ return NPERR_NO_ERROR;
+ }
+#endif
+ case NPNVsupportsCocoaBool: {
+ *(NPBool*)result = true;
+
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsUpdatedCocoaTextInputBool: {
+ *(NPBool*)result = true;
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+#if defined(XP_MACOSX) || defined(XP_WIN)
case NPNVcontentsScaleFactor: {
nsNPAPIPluginInstance *inst =
(nsNPAPIPluginInstance *) (npp ? npp->ndata : nullptr);
@@ -1964,8 +2051,16 @@ _setvalue(NPP npp, NPPVariable variable, void *result)
// actual pointer value is checked rather than its content
// when passing booleans
case NPPVpluginWindowBool: {
+#ifdef XP_MACOSX
+ // This setting doesn't apply to OS X (only to Windows and Unix/Linux).
+ // See https://developer.mozilla.org/En/NPN_SetValue#section_5. Return
+ // NPERR_NO_ERROR here to conform to other browsers' behavior on OS X
+ // (e.g. Safari and Opera).
+ return NPERR_NO_ERROR;
+#else
NPBool bWindowless = (result == nullptr);
return inst->SetWindowless(bWindowless);
+#endif
}
case NPPVpluginTransparentBool: {
NPBool bTransparent = (result != nullptr);
@@ -2050,6 +2145,18 @@ _setvalue(NPP npp, NPPVariable variable, void *result)
}
}
+#ifdef XP_MACOSX
+ case NPPVpluginEventModel: {
+ if (inst) {
+ inst->SetEventModel((NPEventModel)NS_PTR_TO_INT32(result));
+ return NPERR_NO_ERROR;
+ }
+ else {
+ return NPERR_GENERIC_ERROR;
+ }
+ }
+#endif
+
default:
return NPERR_GENERIC_ERROR;
}
diff --git a/dom/plugins/base/nsNPAPIPlugin.h b/dom/plugins/base/nsNPAPIPlugin.h
index 7727bf77fb..96e630b629 100644
--- a/dom/plugins/base/nsNPAPIPlugin.h
+++ b/dom/plugins/base/nsNPAPIPlugin.h
@@ -45,6 +45,10 @@ public:
// PluginFuncs() can't fail but results are only valid if GetLibrary() succeeds
NPPluginFuncs* PluginFuncs();
+#if defined(XP_MACOSX) && !defined(__LP64__)
+ void SetPluginRefNum(short aRefNum);
+#endif
+
// The IPC mechanism notifies the nsNPAPIPlugin if the plugin
// crashes and is no longer usable. pluginDumpID/browserDumpID are
// the IDs of respective minidumps that were written, or empty if no
diff --git a/dom/plugins/base/nsNPAPIPluginInstance.cpp b/dom/plugins/base/nsNPAPIPluginInstance.cpp
index 6e35bf3662..170f42b28e 100644
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -60,6 +60,9 @@ nsNPAPIPluginInstance::nsNPAPIPluginInstance()
, mPlugin(nullptr)
, mMIMEType(nullptr)
, mOwner(nullptr)
+#ifdef XP_MACOSX
+ , mCurrentPluginEvent(nullptr)
+#endif
, mHaveJavaC2PJSObjectQuirk(false)
, mCachedParamLength(0)
, mCachedParamNames(nullptr)
@@ -500,6 +503,9 @@ nsresult nsNPAPIPluginInstance::HandleEvent(void* event, int16_t* result,
int16_t tmpResult = kNPEventNotHandled;
if (pluginFunctions->event) {
+#ifdef XP_MACOSX
+ mCurrentPluginEvent = event;
+#endif
#if defined(XP_WIN)
NS_TRY_SAFE_CALL_RETURN(tmpResult, (*pluginFunctions->event)(&mNPP, event), this,
aSafeToReenterGecko);
@@ -513,6 +519,9 @@ nsresult nsNPAPIPluginInstance::HandleEvent(void* event, int16_t* result,
if (result)
*result = tmpResult;
+#ifdef XP_MACOSX
+ mCurrentPluginEvent = nullptr;
+#endif
}
return NS_OK;
@@ -608,6 +617,19 @@ void nsNPAPIPluginInstance::RedrawPlugin()
mOwner->RedrawPlugin();
}
+#if defined(XP_MACOSX)
+void nsNPAPIPluginInstance::SetEventModel(NPEventModel aModel)
+{
+ // the event model needs to be set for the object frame immediately
+ if (!mOwner) {
+ NS_WARNING("Trying to set event model without a plugin instance owner!");
+ return;
+ }
+
+ mOwner->SetEventModel(aModel);
+}
+#endif
+
nsresult nsNPAPIPluginInstance::GetDrawingModel(int32_t* aModel)
{
*aModel = (int32_t)mDrawingModel;
@@ -616,14 +638,24 @@ nsresult nsNPAPIPluginInstance::GetDrawingModel(int32_t* aModel)
nsresult nsNPAPIPluginInstance::IsRemoteDrawingCoreAnimation(bool* aDrawing)
{
- /** Mac Stub **/
+#ifdef XP_MACOSX
+ if (!mPlugin)
+ return NS_ERROR_FAILURE;
+
+ PluginLibrary* library = mPlugin->GetLibrary();
+ if (!library)
+ return NS_ERROR_FAILURE;
+
+ return library->IsRemoteDrawingCoreAnimation(&mNPP, aDrawing);
+#else
return NS_ERROR_FAILURE;
+#endif
}
nsresult
nsNPAPIPluginInstance::ContentsScaleFactorChanged(double aContentsScaleFactor)
{
-#if defined(XP_WIN)
+#if defined(XP_MACOSX) || defined(XP_WIN)
if (!mPlugin)
return NS_ERROR_FAILURE;
@@ -700,7 +732,12 @@ nsNPAPIPluginInstance::ShouldCache()
nsresult
nsNPAPIPluginInstance::IsWindowless(bool* isWindowless)
{
+#ifdef XP_MACOSX
+ // All OS X plugins are windowless.
+ *isWindowless = true;
+#else
*isWindowless = mWindowless;
+#endif
return NS_OK;
}
diff --git a/dom/plugins/base/nsNPAPIPluginInstance.h b/dom/plugins/base/nsNPAPIPluginInstance.h
index c3272e0a87..48e62517de 100644
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -34,6 +34,12 @@ class nsPluginInstanceOwner;
const NPDrawingModel kDefaultDrawingModel = NPDrawingModelSyncWin;
#elif defined(MOZ_X11)
const NPDrawingModel kDefaultDrawingModel = NPDrawingModelSyncX;
+#elif defined(XP_MACOSX)
+#ifndef NP_NO_QUICKDRAW
+const NPDrawingModel kDefaultDrawingModel = NPDrawingModelQuickDraw; // Not supported
+#else
+const NPDrawingModel kDefaultDrawingModel = NPDrawingModelCoreGraphics;
+#endif
#else
const NPDrawingModel kDefaultDrawingModel = static_cast<NPDrawingModel>(0);
#endif
@@ -140,6 +146,13 @@ public:
void SetDrawingModel(NPDrawingModel aModel);
void RedrawPlugin();
+#ifdef XP_MACOSX
+ void SetEventModel(NPEventModel aModel);
+
+ void* GetCurrentEvent() {
+ return mCurrentPluginEvent;
+ }
+#endif
nsresult NewStreamListener(const char* aURL, void* notifyData,
nsNPAPIPluginStreamListener** listener);
@@ -278,6 +291,11 @@ private:
nsTArray<nsNPAPITimer*> mTimers;
+#ifdef XP_MACOSX
+ // non-null during a HandleEvent call
+ void* mCurrentPluginEvent;
+#endif
+
// Timestamp for the last time this plugin was stopped.
// This is only valid when the plugin is actually stopped!
mozilla::TimeStamp mStopTime;
diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp
index b36bf96331..c9c1b71fe0 100644
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -33,6 +33,9 @@
#include "nsProtocolProxyService.h"
#include "nsIStreamConverterService.h"
#include "nsIFile.h"
+#if defined(XP_MACOSX)
+#include "nsILocalFileMac.h"
+#endif
#include "nsISeekableStream.h"
#include "nsNetUtil.h"
#include "nsIFileStreams.h"
@@ -1921,7 +1924,19 @@ int64_t GetPluginLastModifiedTime(const nsCOMPtr<nsIFile>& localfile)
{
PRTime fileModTime = 0;
+#if defined(XP_MACOSX)
+ // On OS X the date of a bundle's "contents" (i.e. of its Info.plist file)
+ // is a much better guide to when it was last modified than the date of
+ // its package directory. See bug 313700.
+ nsCOMPtr<nsILocalFileMac> localFileMac = do_QueryInterface(localfile);
+ if (localFileMac) {
+ localFileMac->GetBundleContentsLastModifiedTime(&fileModTime);
+ } else {
+ localfile->GetLastModifiedTime(&fileModTime);
+ }
+#else
localfile->GetLastModifiedTime(&fileModTime);
+#endif
return fileModTime;
}
diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp
index 60a9bc0caa..0d4dc68ccc 100644
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -77,6 +77,12 @@ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
#include "mozilla/widget/WinMessages.h"
#endif // #ifdef XP_WIN
+#ifdef XP_MACOSX
+#include "ComplexTextInputPanel.h"
+#include "nsIDOMXULDocument.h"
+#include "nsIDOMXULCommandDispatcher.h"
+#endif
+
#ifdef MOZ_WIDGET_GTK
#include <gdk/gdk.h>
#include <gtk/gtk.h>
@@ -223,13 +229,27 @@ nsPluginInstanceOwner::EndUpdateBackground(const nsIntRect& aRect)
bool
nsPluginInstanceOwner::UseAsyncRendering()
{
+#ifdef XP_MACOSX
+ if (mUseAsyncRendering) {
+ return true;
+ }
+#endif
+
bool isOOP;
bool result = (mInstance &&
NS_SUCCEEDED(mInstance->GetIsOOP(&isOOP)) && isOOP
+#ifndef XP_MACOSX
&& (!mPluginWindow ||
mPluginWindow->type == NPWindowTypeDrawable)
+#endif
);
+#ifdef XP_MACOSX
+ if (result) {
+ mUseAsyncRendering = true;
+ }
+#endif
+
return result;
}
@@ -255,6 +275,13 @@ nsPluginInstanceOwner::nsPluginInstanceOwner()
mPluginFrame = nullptr;
mWidgetCreationComplete = false;
+#ifdef XP_MACOSX
+ mSentInitialTopLevelWindowEvent = false;
+ mLastWindowIsActive = false;
+ mLastContentFocused = false;
+ mLastScaleFactor = 1.0;
+ mShouldBlurOnActivate = false;
+#endif
mLastCSSZoomFactor = 1.0;
mContentFocused = false;
mWidgetVisible = true;
@@ -262,6 +289,16 @@ nsPluginInstanceOwner::nsPluginInstanceOwner()
mPluginDocumentActiveState = true;
mLastMouseDownButtonType = -1;
+#ifdef XP_MACOSX
+#ifndef NP_NO_CARBON
+ // We don't support Carbon, but it is still the default model for i386 NPAPI.
+ mEventModel = NPEventModelCarbon;
+#else
+ mEventModel = NPEventModelCocoa;
+#endif
+ mUseAsyncRendering = false;
+#endif
+
mWaitingForPaint = false;
#ifdef XP_WIN
@@ -494,6 +531,15 @@ NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRect(NPRect *invalidRect)
if (!mPluginFrame || !invalidRect || !mWidgetVisible)
return NS_ERROR_FAILURE;
+#ifdef XP_MACOSX
+ // Each time an asynchronously-drawing plugin sends a new surface to display,
+ // the image in the ImageContainer is updated and InvalidateRect is called.
+ // There are different side effects for (sync) Android plugins.
+ RefPtr<ImageContainer> container;
+ mInstance->GetImageContainer(getter_AddRefs(container));
+#endif
+
+#ifndef XP_MACOSX
// Silverlight calls invalidate for windowed plugins so this needs to work.
if (mWidget) {
mWidget->Invalidate(
@@ -507,6 +553,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRect(NPRect *invalidRect)
return NS_OK;
}
}
+#endif
nsIntRect rect(invalidRect->left,
invalidRect->top,
@@ -917,14 +964,252 @@ nsPluginInstanceOwner::OnWindowedPluginKeyEvent(
NS_IMETHODIMP nsPluginInstanceOwner::SetEventModel(int32_t eventModel)
{
+#ifdef XP_MACOSX
+ mEventModel = static_cast<NPEventModel>(eventModel);
+ return NS_OK;
+#else
return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+#ifdef XP_MACOSX
+NPBool nsPluginInstanceOwner::ConvertPointPuppet(PuppetWidget *widget,
+ nsPluginFrame* pluginFrame,
+ double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace,
+ double *destX, double *destY,
+ NPCoordinateSpace destSpace)
+{
+ NS_ENSURE_TRUE(widget && widget->GetOwningTabChild() && pluginFrame, false);
+ // Caller has to want a result.
+ NS_ENSURE_TRUE(destX || destY, false);
+
+ if (sourceSpace == destSpace) {
+ if (destX) {
+ *destX = sourceX;
+ }
+ if (destY) {
+ *destY = sourceY;
+ }
+ return true;
+ }
+
+ nsPresContext* presContext = pluginFrame->PresContext();
+ double scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel())/
+ presContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
+
+ PuppetWidget *puppetWidget = static_cast<PuppetWidget*>(widget);
+ PuppetWidget *rootWidget = static_cast<PuppetWidget*>(widget->GetTopLevelWidget());
+ if (!rootWidget) {
+ return false;
+ }
+ nsPoint chromeSize = AsNsPoint(rootWidget->GetChromeDimensions()) / scaleFactor;
+ nsIntSize intScreenDims = rootWidget->GetScreenDimensions();
+ nsSize screenDims = nsSize(intScreenDims.width / scaleFactor,
+ intScreenDims.height / scaleFactor);
+ int32_t screenH = screenDims.height;
+ nsPoint windowPosition = AsNsPoint(rootWidget->GetWindowPosition()) / scaleFactor;
+
+ // Window size is tab size + chrome size.
+ LayoutDeviceIntRect tabContentBounds = puppetWidget->GetBounds();
+ tabContentBounds.ScaleInverseRoundOut(scaleFactor);
+ int32_t windowH = tabContentBounds.height + int(chromeSize.y);
+
+ nsPoint pluginPosition = AsNsPoint(pluginFrame->GetScreenRect().TopLeft());
+
+ // Convert (sourceX, sourceY) to 'real' (not PuppetWidget) screen space.
+ // In OSX, the Y-axis increases upward, which is the reverse of ours.
+ // We want OSX coordinates for window and screen so those equations are swapped.
+ nsPoint sourcePoint(sourceX, sourceY);
+ nsPoint screenPoint;
+ switch (sourceSpace) {
+ case NPCoordinateSpacePlugin:
+ screenPoint = sourcePoint + pluginPosition +
+ pluginFrame->GetContentRectRelativeToSelf().TopLeft() / nsPresContext::AppUnitsPerCSSPixel();
+ break;
+ case NPCoordinateSpaceWindow:
+ screenPoint = nsPoint(sourcePoint.x, windowH-sourcePoint.y) +
+ windowPosition;
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ screenPoint = sourcePoint + windowPosition;
+ break;
+ case NPCoordinateSpaceScreen:
+ screenPoint = nsPoint(sourcePoint.x, screenH-sourcePoint.y);
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ screenPoint = sourcePoint;
+ break;
+ default:
+ return false;
+ }
+
+ // Convert from screen to dest space.
+ nsPoint destPoint;
+ switch (destSpace) {
+ case NPCoordinateSpacePlugin:
+ destPoint = screenPoint - pluginPosition -
+ pluginFrame->GetContentRectRelativeToSelf().TopLeft() / nsPresContext::AppUnitsPerCSSPixel();
+ break;
+ case NPCoordinateSpaceWindow:
+ destPoint = screenPoint - windowPosition;
+ destPoint.y = windowH - destPoint.y;
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ destPoint = screenPoint - windowPosition;
+ break;
+ case NPCoordinateSpaceScreen:
+ destPoint = nsPoint(screenPoint.x, screenH-screenPoint.y);
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ destPoint = screenPoint;
+ break;
+ default:
+ return false;
+ }
+
+ if (destX) {
+ *destX = destPoint.x;
+ }
+ if (destY) {
+ *destY = destPoint.y;
+ }
+
+ return true;
}
+NPBool nsPluginInstanceOwner::ConvertPointNoPuppet(nsIWidget *widget,
+ nsPluginFrame* pluginFrame,
+ double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace,
+ double *destX, double *destY,
+ NPCoordinateSpace destSpace)
+{
+ NS_ENSURE_TRUE(widget && pluginFrame, false);
+ // Caller has to want a result.
+ NS_ENSURE_TRUE(destX || destY, false);
+
+ if (sourceSpace == destSpace) {
+ if (destX) {
+ *destX = sourceX;
+ }
+ if (destY) {
+ *destY = sourceY;
+ }
+ return true;
+ }
+
+ nsPresContext* presContext = pluginFrame->PresContext();
+ double scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel())/
+ presContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
+
+ nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
+ if (!screenMgr) {
+ return false;
+ }
+ nsCOMPtr<nsIScreen> screen;
+ screenMgr->ScreenForNativeWidget(widget->GetNativeData(NS_NATIVE_WINDOW), getter_AddRefs(screen));
+ if (!screen) {
+ return false;
+ }
+
+ int32_t screenX, screenY, screenWidth, screenHeight;
+ screen->GetRect(&screenX, &screenY, &screenWidth, &screenHeight);
+ screenHeight /= scaleFactor;
+
+ LayoutDeviceIntRect windowScreenBounds = widget->GetScreenBounds();
+ windowScreenBounds.ScaleInverseRoundOut(scaleFactor);
+ int32_t windowX = windowScreenBounds.x;
+ int32_t windowY = windowScreenBounds.y;
+ int32_t windowHeight = windowScreenBounds.height;
+
+ nsIntRect pluginScreenRect = pluginFrame->GetScreenRect();
+
+ double screenXGecko, screenYGecko;
+ switch (sourceSpace) {
+ case NPCoordinateSpacePlugin:
+ screenXGecko = pluginScreenRect.x + sourceX;
+ screenYGecko = pluginScreenRect.y + sourceY;
+ break;
+ case NPCoordinateSpaceWindow:
+ screenXGecko = windowX + sourceX;
+ screenYGecko = windowY + (windowHeight - sourceY);
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ screenXGecko = windowX + sourceX;
+ screenYGecko = windowY + sourceY;
+ break;
+ case NPCoordinateSpaceScreen:
+ screenXGecko = sourceX;
+ screenYGecko = screenHeight - sourceY;
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ screenXGecko = sourceX;
+ screenYGecko = sourceY;
+ break;
+ default:
+ return false;
+ }
+
+ double destXCocoa, destYCocoa;
+ switch (destSpace) {
+ case NPCoordinateSpacePlugin:
+ destXCocoa = screenXGecko - pluginScreenRect.x;
+ destYCocoa = screenYGecko - pluginScreenRect.y;
+ break;
+ case NPCoordinateSpaceWindow:
+ destXCocoa = screenXGecko - windowX;
+ destYCocoa = windowHeight - (screenYGecko - windowY);
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ destXCocoa = screenXGecko - windowX;
+ destYCocoa = screenYGecko - windowY;
+ break;
+ case NPCoordinateSpaceScreen:
+ destXCocoa = screenXGecko;
+ destYCocoa = screenHeight - screenYGecko;
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ destXCocoa = screenXGecko;
+ destYCocoa = screenYGecko;
+ break;
+ default:
+ return false;
+ }
+
+ if (destX) {
+ *destX = destXCocoa;
+ }
+ if (destY) {
+ *destY = destYCocoa;
+ }
+
+ return true;
+}
+#endif // XP_MACOSX
+
NPBool nsPluginInstanceOwner::ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
double *destX, double *destY, NPCoordinateSpace destSpace)
{
- /** Mac Stub **/
+#ifdef XP_MACOSX
+ if (!mPluginFrame) {
+ return false;
+ }
+
+ MOZ_ASSERT(mPluginFrame->GetNearestWidget());
+
+ if (nsIWidget::UsePuppetWidgets()) {
+ return ConvertPointPuppet(static_cast<PuppetWidget*>(mPluginFrame->GetNearestWidget()),
+ mPluginFrame, sourceX, sourceY, sourceSpace,
+ destX, destY, destSpace);
+ }
+
+ return ConvertPointNoPuppet(mPluginFrame->GetNearestWidget(),
+ mPluginFrame, sourceX, sourceY, sourceSpace,
+ destX, destY, destSpace);
+#else
return false;
+#endif
}
NPError nsPluginInstanceOwner::InitAsyncSurface(NPSize *size, NPImageFormat format,
@@ -968,7 +1253,133 @@ void nsPluginInstanceOwner::GetParameters(nsTArray<MozPluginParameter>& paramete
loadingContent->GetPluginParameters(parameters);
}
-#if defined(XP_WIN)
+#ifdef XP_MACOSX
+
+static void InitializeNPCocoaEvent(NPCocoaEvent* event)
+{
+ memset(event, 0, sizeof(NPCocoaEvent));
+}
+
+NPDrawingModel nsPluginInstanceOwner::GetDrawingModel()
+{
+#ifndef NP_NO_QUICKDRAW
+ // We don't support the Quickdraw drawing model any more but it's still
+ // the default model for i386 per NPAPI.
+ NPDrawingModel drawingModel = NPDrawingModelQuickDraw;
+#else
+ NPDrawingModel drawingModel = NPDrawingModelCoreGraphics;
+#endif
+
+ if (!mInstance)
+ return drawingModel;
+
+ mInstance->GetDrawingModel((int32_t*)&drawingModel);
+ return drawingModel;
+}
+
+bool nsPluginInstanceOwner::IsRemoteDrawingCoreAnimation()
+{
+ if (!mInstance)
+ return false;
+
+ bool coreAnimation;
+ if (!NS_SUCCEEDED(mInstance->IsRemoteDrawingCoreAnimation(&coreAnimation)))
+ return false;
+
+ return coreAnimation;
+}
+
+NPEventModel nsPluginInstanceOwner::GetEventModel()
+{
+ return mEventModel;
+}
+
+#define DEFAULT_REFRESH_RATE 20 // 50 FPS
+
+nsCOMPtr<nsITimer> *nsPluginInstanceOwner::sCATimer = nullptr;
+nsTArray<nsPluginInstanceOwner*> *nsPluginInstanceOwner::sCARefreshListeners = nullptr;
+
+void nsPluginInstanceOwner::CARefresh(nsITimer *aTimer, void *aClosure) {
+ if (!sCARefreshListeners) {
+ return;
+ }
+ for (size_t i = 0; i < sCARefreshListeners->Length(); i++) {
+ nsPluginInstanceOwner* instanceOwner = (*sCARefreshListeners)[i];
+ NPWindow *window;
+ instanceOwner->GetWindow(window);
+ if (!window) {
+ continue;
+ }
+ NPRect r;
+ r.left = 0;
+ r.top = 0;
+ r.right = window->width;
+ r.bottom = window->height;
+ instanceOwner->InvalidateRect(&r);
+ }
+}
+
+void nsPluginInstanceOwner::AddToCARefreshTimer() {
+ if (!mInstance) {
+ return;
+ }
+
+ // Flash invokes InvalidateRect for us.
+ const char* mime = nullptr;
+ if (NS_SUCCEEDED(mInstance->GetMIMEType(&mime)) && mime &&
+ nsPluginHost::GetSpecialType(nsDependentCString(mime)) ==
+ nsPluginHost::eSpecialType_Flash) {
+ return;
+ }
+
+ if (!sCARefreshListeners) {
+ sCARefreshListeners = new nsTArray<nsPluginInstanceOwner*>();
+ }
+
+ if (sCARefreshListeners->Contains(this)) {
+ return;
+ }
+
+ sCARefreshListeners->AppendElement(this);
+
+ if (!sCATimer) {
+ sCATimer = new nsCOMPtr<nsITimer>();
+ }
+
+ if (sCARefreshListeners->Length() == 1) {
+ *sCATimer = do_CreateInstance("@mozilla.org/timer;1");
+ (*sCATimer)->InitWithFuncCallback(CARefresh, nullptr,
+ DEFAULT_REFRESH_RATE, nsITimer::TYPE_REPEATING_SLACK);
+ }
+}
+
+void nsPluginInstanceOwner::RemoveFromCARefreshTimer() {
+ if (!sCARefreshListeners || sCARefreshListeners->Contains(this) == false) {
+ return;
+ }
+
+ sCARefreshListeners->RemoveElement(this);
+
+ if (sCARefreshListeners->Length() == 0) {
+ if (sCATimer) {
+ (*sCATimer)->Cancel();
+ delete sCATimer;
+ sCATimer = nullptr;
+ }
+ delete sCARefreshListeners;
+ sCARefreshListeners = nullptr;
+ }
+}
+
+void nsPluginInstanceOwner::SetPluginPort()
+{
+ void* pluginPort = GetPluginPort();
+ if (!pluginPort || !mPluginWindow)
+ return;
+ mPluginWindow->window = pluginPort;
+}
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
nsresult nsPluginInstanceOwner::ContentsScaleFactorChanged(double aContentsScaleFactor)
{
if (!mInstance) {
@@ -987,6 +1398,11 @@ nsPluginInstanceOwner::GetEventloopNestingLevel()
uint32_t currentLevel = 0;
if (appShell) {
appShell->GetEventloopNestingLevel(&currentLevel);
+#ifdef XP_MACOSX
+ // Cocoa widget code doesn't process UI events through the normal
+ // appshell event loop, so it needs an additional count here.
+ currentLevel++;
+#endif
}
// No idea how this happens... but Linux doesn't consistently
@@ -1011,11 +1427,15 @@ void
nsPluginInstanceOwner::NotifyHostCreateWidget()
{
mPluginHost->CreateWidget(this);
+#ifdef XP_MACOSX
+ FixUpPluginWindow(ePluginPaintEnable);
+#else
if (mPluginFrame) {
mPluginFrame->InvalidateFrame();
} else {
CallSetWindow();
}
+#endif
}
void
@@ -1037,10 +1457,12 @@ nsPluginInstanceOwner::NotifyDestroyPending()
nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent)
{
+#ifndef XP_MACOSX
if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow)) {
// continue only for cases without child window
return aFocusEvent->PreventDefault(); // consume event
}
+#endif
WidgetEvent* theEvent = aFocusEvent->WidgetEventPtr();
if (theEvent) {
@@ -1058,6 +1480,9 @@ nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent)
nsresult nsPluginInstanceOwner::ProcessKeyPress(nsIDOMEvent* aKeyEvent)
{
+#ifdef XP_MACOSX
+ return DispatchKeyToPlugin(aKeyEvent);
+#else
if (SendNativeEvents())
DispatchKeyToPlugin(aKeyEvent);
@@ -1068,13 +1493,16 @@ nsresult nsPluginInstanceOwner::ProcessKeyPress(nsIDOMEvent* aKeyEvent)
aKeyEvent->StopPropagation();
}
return NS_OK;
+#endif
}
nsresult nsPluginInstanceOwner::DispatchKeyToPlugin(nsIDOMEvent* aKeyEvent)
{
+#if !defined(XP_MACOSX)
if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow))
return aKeyEvent->PreventDefault(); // consume event
// continue only for cases without child window
+#endif
if (mInstance) {
WidgetKeyboardEvent* keyEvent =
@@ -1094,9 +1522,11 @@ nsresult nsPluginInstanceOwner::DispatchKeyToPlugin(nsIDOMEvent* aKeyEvent)
nsresult
nsPluginInstanceOwner::ProcessMouseDown(nsIDOMEvent* aMouseEvent)
{
+#if !defined(XP_MACOSX)
if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow))
return aMouseEvent->PreventDefault(); // consume event
// continue only for cases without child window
+#endif
// if the plugin is windowless, we need to set focus ourselves
// otherwise, we might not get key events
@@ -1126,9 +1556,11 @@ nsPluginInstanceOwner::ProcessMouseDown(nsIDOMEvent* aMouseEvent)
nsresult nsPluginInstanceOwner::DispatchMouseToPlugin(nsIDOMEvent* aMouseEvent,
bool aAllowPropagate)
{
+#if !defined(XP_MACOSX)
if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow))
return aMouseEvent->PreventDefault(); // consume event
// continue only for cases without child window
+#endif
// don't send mouse events if we are hidden
if (!mWidgetVisible)
return NS_OK;
@@ -1338,6 +1770,22 @@ nsPluginInstanceOwner::HandleEvent(nsIDOMEvent* aEvent)
nsAutoString eventType;
aEvent->GetType(eventType);
+#ifdef XP_MACOSX
+ if (eventType.EqualsLiteral("activate") ||
+ eventType.EqualsLiteral("deactivate")) {
+ WindowFocusMayHaveChanged();
+ return NS_OK;
+ }
+ if (eventType.EqualsLiteral("MozPerformDelayedBlur")) {
+ if (mShouldBlurOnActivate) {
+ WidgetGUIEvent blurEvent(true, eBlur, nullptr);
+ ProcessEvent(blurEvent);
+ mShouldBlurOnActivate = false;
+ }
+ return NS_OK;
+ }
+#endif
+
if (eventType.EqualsLiteral("focus")) {
mContentFocused = true;
return DispatchFocusToPlugin(aEvent);
@@ -1400,6 +1848,191 @@ static unsigned int XInputEventState(const WidgetInputEvent& anEvent)
}
#endif
+#ifdef XP_MACOSX
+
+// Returns whether or not content is the content that is or would be
+// focused if the top-level chrome window was active.
+static bool
+ContentIsFocusedWithinWindow(nsIContent* aContent)
+{
+ nsPIDOMWindowOuter* outerWindow = aContent->OwnerDoc()->GetWindow();
+ if (!outerWindow) {
+ return false;
+ }
+
+ nsPIDOMWindowOuter* rootWindow = outerWindow->GetPrivateRoot();
+ if (!rootWindow) {
+ return false;
+ }
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (!fm) {
+ return false;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> focusedFrame;
+ nsCOMPtr<nsIContent> focusedContent = fm->GetFocusedDescendant(rootWindow, true, getter_AddRefs(focusedFrame));
+ return (focusedContent.get() == aContent);
+}
+
+static NPCocoaEventType
+CocoaEventTypeForEvent(const WidgetGUIEvent& anEvent, nsIFrame* aObjectFrame)
+{
+ const NPCocoaEvent* event = static_cast<const NPCocoaEvent*>(anEvent.mPluginEvent);
+ if (event) {
+ return event->type;
+ }
+
+ switch (anEvent.mMessage) {
+ case eMouseOver:
+ return NPCocoaEventMouseEntered;
+ case eMouseOut:
+ return NPCocoaEventMouseExited;
+ case eMouseMove: {
+ // We don't know via information on events from the widget code whether or not
+ // we're dragging. The widget code just generates mouse move events from native
+ // drag events. If anybody is capturing, this is a drag event.
+ if (nsIPresShell::GetCapturingContent()) {
+ return NPCocoaEventMouseDragged;
+ }
+
+ return NPCocoaEventMouseMoved;
+ }
+ case eMouseDown:
+ return NPCocoaEventMouseDown;
+ case eMouseUp:
+ return NPCocoaEventMouseUp;
+ case eKeyDown:
+ return NPCocoaEventKeyDown;
+ case eKeyUp:
+ return NPCocoaEventKeyUp;
+ case eFocus:
+ case eBlur:
+ return NPCocoaEventFocusChanged;
+ case eLegacyMouseLineOrPageScroll:
+ return NPCocoaEventScrollWheel;
+ default:
+ return (NPCocoaEventType)0;
+ }
+}
+
+static NPCocoaEvent
+TranslateToNPCocoaEvent(WidgetGUIEvent* anEvent, nsIFrame* aObjectFrame)
+{
+ NPCocoaEvent cocoaEvent;
+ InitializeNPCocoaEvent(&cocoaEvent);
+ cocoaEvent.type = CocoaEventTypeForEvent(*anEvent, aObjectFrame);
+
+ if (anEvent->mMessage == eMouseMove ||
+ anEvent->mMessage == eMouseDown ||
+ anEvent->mMessage == eMouseUp ||
+ anEvent->mMessage == eLegacyMouseLineOrPageScroll ||
+ anEvent->mMessage == eMouseOver ||
+ anEvent->mMessage == eMouseOut)
+ {
+ nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(anEvent, aObjectFrame) -
+ aObjectFrame->GetContentRectRelativeToSelf().TopLeft();
+ nsPresContext* presContext = aObjectFrame->PresContext();
+ // Plugin event coordinates need to be translated from device pixels
+ // into "display pixels" in HiDPI modes.
+ double scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel())/
+ aObjectFrame->PresContext()->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
+ size_t intScaleFactor = ceil(scaleFactor);
+ nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x) / intScaleFactor,
+ presContext->AppUnitsToDevPixels(pt.y) / intScaleFactor);
+ cocoaEvent.data.mouse.pluginX = double(ptPx.x);
+ cocoaEvent.data.mouse.pluginY = double(ptPx.y);
+ }
+
+ switch (anEvent->mMessage) {
+ case eMouseDown:
+ case eMouseUp: {
+ WidgetMouseEvent* mouseEvent = anEvent->AsMouseEvent();
+ if (mouseEvent) {
+ switch (mouseEvent->button) {
+ case WidgetMouseEvent::eLeftButton:
+ cocoaEvent.data.mouse.buttonNumber = 0;
+ break;
+ case WidgetMouseEvent::eRightButton:
+ cocoaEvent.data.mouse.buttonNumber = 1;
+ break;
+ case WidgetMouseEvent::eMiddleButton:
+ cocoaEvent.data.mouse.buttonNumber = 2;
+ break;
+ default:
+ NS_WARNING("Mouse button we don't know about?");
+ }
+ cocoaEvent.data.mouse.clickCount = mouseEvent->mClickCount;
+ } else {
+ NS_WARNING("eMouseUp/DOWN is not a WidgetMouseEvent?");
+ }
+ break;
+ }
+ case eLegacyMouseLineOrPageScroll: {
+ WidgetWheelEvent* wheelEvent = anEvent->AsWheelEvent();
+ if (wheelEvent) {
+ cocoaEvent.data.mouse.deltaX = wheelEvent->mLineOrPageDeltaX;
+ cocoaEvent.data.mouse.deltaY = wheelEvent->mLineOrPageDeltaY;
+ } else {
+ NS_WARNING("eLegacyMouseLineOrPageScroll is not a WidgetWheelEvent? "
+ "(could be, haven't checked)");
+ }
+ break;
+ }
+ case eKeyDown:
+ case eKeyUp:
+ {
+ WidgetKeyboardEvent* keyEvent = anEvent->AsKeyboardEvent();
+
+ // That keyEvent->mPluginTextEventString is non-empty is a signal that we should
+ // create a text event for the plugin, instead of a key event.
+ if (anEvent->mMessage == eKeyDown &&
+ !keyEvent->mPluginTextEventString.IsEmpty()) {
+ cocoaEvent.type = NPCocoaEventTextInput;
+ const char16_t* pluginTextEventString = keyEvent->mPluginTextEventString.get();
+ cocoaEvent.data.text.text = (NPNSString*)
+ ::CFStringCreateWithCharacters(NULL,
+ reinterpret_cast<const UniChar*>(pluginTextEventString),
+ keyEvent->mPluginTextEventString.Length());
+ } else {
+ cocoaEvent.data.key.keyCode = keyEvent->mNativeKeyCode;
+ cocoaEvent.data.key.isARepeat = keyEvent->mIsRepeat;
+ cocoaEvent.data.key.modifierFlags = keyEvent->mNativeModifierFlags;
+ const char16_t* nativeChars = keyEvent->mNativeCharacters.get();
+ cocoaEvent.data.key.characters = (NPNSString*)
+ ::CFStringCreateWithCharacters(NULL,
+ reinterpret_cast<const UniChar*>(nativeChars),
+ keyEvent->mNativeCharacters.Length());
+ const char16_t* nativeCharsIgnoringModifiers = keyEvent->mNativeCharactersIgnoringModifiers.get();
+ cocoaEvent.data.key.charactersIgnoringModifiers = (NPNSString*)
+ ::CFStringCreateWithCharacters(NULL,
+ reinterpret_cast<const UniChar*>(nativeCharsIgnoringModifiers),
+ keyEvent->mNativeCharactersIgnoringModifiers.Length());
+ }
+ break;
+ }
+ case eFocus:
+ case eBlur:
+ cocoaEvent.data.focus.hasFocus = (anEvent->mMessage == eFocus);
+ break;
+ default:
+ break;
+ }
+ return cocoaEvent;
+}
+
+void nsPluginInstanceOwner::PerformDelayedBlurs()
+{
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ nsCOMPtr<EventTarget> windowRoot = content->OwnerDoc()->GetWindow()->GetTopWindowRoot();
+ nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(),
+ windowRoot,
+ NS_LITERAL_STRING("MozPerformDelayedBlur"),
+ false, false, nullptr);
+}
+
+#endif
+
nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent)
{
nsEventStatus rv = nsEventStatus_eIgnore;
@@ -1408,6 +2041,85 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent)
return nsEventStatus_eIgnore;
}
+#ifdef XP_MACOSX
+ NPEventModel eventModel = GetEventModel();
+ if (eventModel != NPEventModelCocoa) {
+ return nsEventStatus_eIgnore;
+ }
+
+ // In the Cocoa event model, focus is per-window. Don't tell a plugin it lost
+ // focus unless it lost focus within the window. For example, ignore a blur
+ // event if it's coming due to the plugin's window deactivating.
+ nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
+ if (anEvent.mMessage == eBlur && ContentIsFocusedWithinWindow(content)) {
+ mShouldBlurOnActivate = true;
+ return nsEventStatus_eIgnore;
+ }
+
+ // Also, don't tell the plugin it gained focus again after we've already given
+ // it focus. This might happen if it has focus, its window is blurred, then the
+ // window is made active again. The plugin never lost in-window focus, so it
+ // shouldn't get a focus event again.
+ if (anEvent.mMessage == eFocus && mLastContentFocused == true) {
+ mShouldBlurOnActivate = false;
+ return nsEventStatus_eIgnore;
+ }
+
+ // Now, if we're going to send a focus event, update mLastContentFocused and
+ // tell any plugins in our window that we have taken focus, so they should
+ // perform any delayed blurs.
+ if (anEvent.mMessage == eFocus || anEvent.mMessage == eBlur) {
+ mLastContentFocused = (anEvent.mMessage == eFocus);
+ mShouldBlurOnActivate = false;
+ PerformDelayedBlurs();
+ }
+
+ NPCocoaEvent cocoaEvent = TranslateToNPCocoaEvent(const_cast<WidgetGUIEvent*>(&anEvent), mPluginFrame);
+ if (cocoaEvent.type == (NPCocoaEventType)0) {
+ return nsEventStatus_eIgnore;
+ }
+
+ if (cocoaEvent.type == NPCocoaEventTextInput) {
+ mInstance->HandleEvent(&cocoaEvent, nullptr);
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
+ int16_t response = kNPEventNotHandled;
+ mInstance->HandleEvent(&cocoaEvent,
+ &response,
+ NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
+ if ((response == kNPEventStartIME) && (cocoaEvent.type == NPCocoaEventKeyDown)) {
+ nsIWidget* widget = mPluginFrame->GetNearestWidget();
+ if (widget) {
+ const WidgetKeyboardEvent* keyEvent = anEvent.AsKeyboardEvent();
+ double screenX, screenY;
+ ConvertPoint(0.0, mPluginFrame->GetScreenRect().height,
+ NPCoordinateSpacePlugin, &screenX, &screenY,
+ NPCoordinateSpaceScreen);
+ nsAutoString outText;
+ if (NS_SUCCEEDED(widget->StartPluginIME(*keyEvent, screenX, screenY, outText)) &&
+ !outText.IsEmpty()) {
+ CFStringRef cfString =
+ ::CFStringCreateWithCharacters(kCFAllocatorDefault,
+ reinterpret_cast<const UniChar*>(outText.get()),
+ outText.Length());
+ NPCocoaEvent textEvent;
+ InitializeNPCocoaEvent(&textEvent);
+ textEvent.type = NPCocoaEventTextInput;
+ textEvent.data.text.text = (NPNSString*)cfString;
+ mInstance->HandleEvent(&textEvent, nullptr);
+ }
+ }
+ }
+
+ bool handled = (response == kNPEventHandled || response == kNPEventStartIME);
+ bool leftMouseButtonDown = (anEvent.mMessage == eMouseDown) &&
+ (anEvent.AsMouseEvent()->button == WidgetMouseEvent::eLeftButton);
+ if (handled && !(leftMouseButtonDown && !mContentFocused)) {
+ rv = nsEventStatus_eConsumeNoDefault;
+ }
+#endif
+
#ifdef XP_WIN
// this code supports windowless plugins
const NPEvent *pPluginEvent = static_cast<const NPEvent*>(anEvent.mPluginEvent);
@@ -1813,6 +2525,10 @@ nsPluginInstanceOwner::Destroy()
{
SetFrame(nullptr);
+#ifdef XP_MACOSX
+ RemoveFromCARefreshTimer();
+#endif
+
nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
// unregister context menu listener
@@ -1864,6 +2580,44 @@ nsPluginInstanceOwner::Destroy()
// Paints are handled differently, so we just simulate an update event.
+#ifdef XP_MACOSX
+void nsPluginInstanceOwner::Paint(const gfxRect& aDirtyRect, CGContextRef cgContext)
+{
+ if (!mInstance || !mPluginFrame)
+ return;
+
+ gfxRect dirtyRectCopy = aDirtyRect;
+ double scaleFactor = 1.0;
+ GetContentsScaleFactor(&scaleFactor);
+ if (scaleFactor != 1.0) {
+ ::CGContextScaleCTM(cgContext, scaleFactor, scaleFactor);
+ // Convert aDirtyRect from device pixels to "display pixels"
+ // for HiDPI modes
+ dirtyRectCopy.ScaleRoundOut(1.0 / scaleFactor);
+ }
+
+ DoCocoaEventDrawRect(dirtyRectCopy, cgContext);
+}
+
+void nsPluginInstanceOwner::DoCocoaEventDrawRect(const gfxRect& aDrawRect, CGContextRef cgContext)
+{
+ if (!mInstance || !mPluginFrame)
+ return;
+
+ // The context given here is only valid during the HandleEvent call.
+ NPCocoaEvent updateEvent;
+ InitializeNPCocoaEvent(&updateEvent);
+ updateEvent.type = NPCocoaEventDrawRect;
+ updateEvent.data.draw.context = cgContext;
+ updateEvent.data.draw.x = aDrawRect.X();
+ updateEvent.data.draw.y = aDrawRect.Y();
+ updateEvent.data.draw.width = aDrawRect.Width();
+ updateEvent.data.draw.height = aDrawRect.Height();
+
+ mInstance->HandleEvent(&updateEvent, nullptr);
+}
+#endif
+
#ifdef XP_WIN
void nsPluginInstanceOwner::Paint(const RECT& aDirty, HDC aDC)
{
@@ -2180,6 +2934,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void)
if (content) {
doc = content->OwnerDoc();
parentWidget = nsContentUtils::WidgetForDocument(doc);
+#ifndef XP_MACOSX
// If we're running in the content process, we need a remote widget created in chrome.
if (XRE_IsContentProcess()) {
if (nsCOMPtr<nsPIDOMWindowOuter> window = doc->GetWindow()) {
@@ -2195,13 +2950,16 @@ NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void)
}
}
}
+#endif // XP_MACOSX
}
+#ifndef XP_MACOSX
// A failure here is terminal since we can't fall back on the non-e10s code
// path below.
if (!mWidget && XRE_IsContentProcess()) {
return NS_ERROR_UNEXPECTED;
}
+#endif // XP_MACOSX
if (!mWidget) {
// native (single process)
@@ -2264,11 +3022,136 @@ NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void)
}
}
+#ifdef XP_MACOSX
+ if (GetDrawingModel() == NPDrawingModelCoreAnimation) {
+ AddToCARefreshTimer();
+ }
+#endif
+
mWidgetCreationComplete = true;
return NS_OK;
}
+// Mac specific code to fix up the port location and clipping region
+#ifdef XP_MACOSX
+
+void nsPluginInstanceOwner::FixUpPluginWindow(int32_t inPaintState)
+{
+ if (!mPluginWindow || !mInstance || !mPluginFrame) {
+ return;
+ }
+
+ SetPluginPort();
+
+ LayoutDeviceIntSize widgetClip = mPluginFrame->GetWidgetlessClipRect().Size();
+
+ mPluginWindow->x = 0;
+ mPluginWindow->y = 0;
+
+ NPRect oldClipRect = mPluginWindow->clipRect;
+
+ // fix up the clipping region
+ mPluginWindow->clipRect.top = 0;
+ mPluginWindow->clipRect.left = 0;
+
+ if (inPaintState == ePluginPaintDisable) {
+ mPluginWindow->clipRect.bottom = mPluginWindow->clipRect.top;
+ mPluginWindow->clipRect.right = mPluginWindow->clipRect.left;
+ }
+ else if (inPaintState == ePluginPaintEnable)
+ {
+ mPluginWindow->clipRect.bottom = mPluginWindow->clipRect.top + widgetClip.height;
+ mPluginWindow->clipRect.right = mPluginWindow->clipRect.left + widgetClip.width;
+ }
+
+ // if the clip rect changed, call SetWindow()
+ // (RealPlayer needs this to draw correctly)
+ if (mPluginWindow->clipRect.left != oldClipRect.left ||
+ mPluginWindow->clipRect.top != oldClipRect.top ||
+ mPluginWindow->clipRect.right != oldClipRect.right ||
+ mPluginWindow->clipRect.bottom != oldClipRect.bottom)
+ {
+ if (UseAsyncRendering()) {
+ mInstance->AsyncSetWindow(mPluginWindow);
+ }
+ else {
+ mPluginWindow->CallSetWindow(mInstance);
+ }
+ }
+
+ // After the first NPP_SetWindow call we need to send an initial
+ // top-level window focus event.
+ if (!mSentInitialTopLevelWindowEvent) {
+ // Set this before calling ProcessEvent to avoid endless recursion.
+ mSentInitialTopLevelWindowEvent = true;
+
+ bool isActive = WindowIsActive();
+ SendWindowFocusChanged(isActive);
+ mLastWindowIsActive = isActive;
+ }
+}
+
+void
+nsPluginInstanceOwner::WindowFocusMayHaveChanged()
+{
+ if (!mSentInitialTopLevelWindowEvent) {
+ return;
+ }
+
+ bool isActive = WindowIsActive();
+ if (isActive != mLastWindowIsActive) {
+ SendWindowFocusChanged(isActive);
+ mLastWindowIsActive = isActive;
+ }
+}
+
+bool
+nsPluginInstanceOwner::WindowIsActive()
+{
+ if (!mPluginFrame) {
+ return false;
+ }
+
+ EventStates docState = mPluginFrame->GetContent()->OwnerDoc()->GetDocumentState();
+ return !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
+}
+
+void
+nsPluginInstanceOwner::SendWindowFocusChanged(bool aIsActive)
+{
+ if (!mInstance) {
+ return;
+ }
+
+ NPCocoaEvent cocoaEvent;
+ InitializeNPCocoaEvent(&cocoaEvent);
+ cocoaEvent.type = NPCocoaEventWindowFocusChanged;
+ cocoaEvent.data.focus.hasFocus = aIsActive;
+ mInstance->HandleEvent(&cocoaEvent,
+ nullptr,
+ NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
+}
+
+void
+nsPluginInstanceOwner::HidePluginWindow()
+{
+ if (!mPluginWindow || !mInstance) {
+ return;
+ }
+
+ mPluginWindow->clipRect.bottom = mPluginWindow->clipRect.top;
+ mPluginWindow->clipRect.right = mPluginWindow->clipRect.left;
+ mWidgetVisible = false;
+ if (UseAsyncRendering()) {
+ mInstance->AsyncSetWindow(mPluginWindow);
+ } else {
+ mInstance->SetWindow(mPluginWindow);
+ }
+}
+
+#else // XP_MACOSX
+
void nsPluginInstanceOwner::UpdateWindowPositionAndClipRect(bool aSetWindow)
{
if (!mPluginWindow)
@@ -2318,11 +3201,12 @@ nsPluginInstanceOwner::UpdateWindowVisibility(bool aVisible)
mPluginWindowVisible = aVisible;
UpdateWindowPositionAndClipRect(true);
}
+#endif // XP_MACOSX
void
nsPluginInstanceOwner::ResolutionMayHaveChanged()
{
-#if defined(XP_WIN)
+#if defined(XP_MACOSX) || defined(XP_WIN)
double scaleFactor = 1.0;
GetContentsScaleFactor(&scaleFactor);
if (scaleFactor != mLastScaleFactor) {
@@ -2347,6 +3231,7 @@ nsPluginInstanceOwner::UpdateDocumentActiveState(bool aIsActive)
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
mPluginDocumentActiveState = aIsActive;
+#ifndef XP_MACOSX
UpdateWindowPositionAndClipRect(true);
// We don't have a connection to PluginWidgetParent in the chrome
@@ -2358,6 +3243,7 @@ nsPluginInstanceOwner::UpdateDocumentActiveState(bool aIsActive)
mWidget->Show(aIsActive);
mWidget->Enable(aIsActive);
}
+#endif // #ifndef XP_MACOSX
}
NS_IMETHODIMP
@@ -2385,10 +3271,10 @@ nsPluginInstanceOwner::GetContentsScaleFactor(double *result)
{
NS_ENSURE_ARG_POINTER(result);
double scaleFactor = 1.0;
- // On Windows, device pixels need to be translated to (and from) "display pixels"
+ // On Mac, device pixels need to be translated to (and from) "display pixels"
// for plugins. On other platforms, plugin coordinates are always in device
// pixels.
-#if defined(XP_WIN)
+#if defined(XP_MACOSX) || defined(XP_WIN)
nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
nsIPresShell* presShell = nsContentUtils::FindPresShellForDocument(content->OwnerDoc());
if (presShell) {
diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h
index ba16cf288c..2fa67c86e0 100644
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -19,6 +19,11 @@
#include "nsWeakReference.h"
#include "gfxRect.h"
+#ifdef XP_MACOSX
+#include "mozilla/gfx/QuartzSupport.h"
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
class nsIInputStream;
class nsPluginDOMContextMenuListener;
class nsPluginFrame;
@@ -99,6 +104,10 @@ public:
#ifdef XP_WIN
void Paint(const RECT& aDirty, HDC aDC);
+#elif defined(XP_MACOSX)
+ void Paint(const gfxRect& aDirtyRect, CGContextRef cgContext);
+ void RenderCoreAnimation(CGContextRef aCGContext, int aWidth, int aHeight);
+ void DoCocoaEventDrawRect(const gfxRect& aDrawRect, CGContextRef cgContext);
#elif defined(MOZ_X11)
void Paint(gfxContext* aContext,
const gfxRect& aFrameRect,
@@ -123,11 +132,33 @@ public:
nsresult SetNetscapeWindowAsParent(HWND aWindowToAdopt);
#endif
+#ifdef XP_MACOSX
+ enum { ePluginPaintEnable, ePluginPaintDisable };
+
+ void WindowFocusMayHaveChanged();
+
+ bool WindowIsActive();
+ void SendWindowFocusChanged(bool aIsActive);
+ NPDrawingModel GetDrawingModel();
+ bool IsRemoteDrawingCoreAnimation();
+
+ NPEventModel GetEventModel();
+ static void CARefresh(nsITimer *aTimer, void *aClosure);
+ void AddToCARefreshTimer();
+ void RemoveFromCARefreshTimer();
+ // This calls into the plugin (NPP_SetWindow) and can run script.
+ void FixUpPluginWindow(int32_t inPaintState);
+ void HidePluginWindow();
+ // Set plugin port info in the plugin (in the 'window' member of the
+ // NPWindow structure passed to the plugin by SetWindow()).
+ void SetPluginPort();
+#else // XP_MACOSX
void UpdateWindowPositionAndClipRect(bool aSetWindow);
void UpdateWindowVisibility(bool aVisible);
+#endif // XP_MACOSX
void ResolutionMayHaveChanged();
-#if defined(XP_WIN)
+#if defined(XP_MACOSX) || defined(XP_WIN)
nsresult ContentsScaleFactorChanged(double aContentsScaleFactor);
#endif
@@ -183,7 +214,7 @@ public:
return mPluginWindow->type == NPWindowTypeDrawable &&
(MatchPluginName("Shockwave Flash") ||
MatchPluginName("Test Plug-in"));
-#elif defined(MOZ_X11)
+#elif defined(MOZ_X11) || defined(XP_MACOSX)
return true;
#else
return false;
@@ -280,6 +311,15 @@ private:
nsCOMPtr<nsIWidget> mWidget;
RefPtr<nsPluginHost> mPluginHost;
+#ifdef XP_MACOSX
+ static nsCOMPtr<nsITimer> *sCATimer;
+ static nsTArray<nsPluginInstanceOwner*> *sCARefreshListeners;
+ bool mSentInitialTopLevelWindowEvent;
+ bool mLastWindowIsActive;
+ bool mLastContentFocused;
+ // True if, the next time the window is activated, we should blur ourselves.
+ bool mShouldBlurOnActivate;
+#endif
double mLastScaleFactor;
double mLastCSSZoomFactor;
// Initially, the event loop nesting level we were created on, it's updated
@@ -295,6 +335,15 @@ private:
bool mPluginWindowVisible;
bool mPluginDocumentActiveState;
+#ifdef XP_MACOSX
+ NPEventModel mEventModel;
+ // This is a hack! UseAsyncRendering() can incorrectly return false
+ // when we don't have an object frame (possible as of bug 90268).
+ // We hack around this by always returning true if we've ever
+ // returned true.
+ bool mUseAsyncRendering;
+#endif
+
// pointer to wrapper for nsIDOMContextMenuListener
RefPtr<nsPluginDOMContextMenuListener> mCXMenuListener;
@@ -308,6 +357,16 @@ private:
void CallDefaultProc(const mozilla::WidgetGUIEvent* aEvent);
#endif
+#ifdef XP_MACOSX
+ static NPBool ConvertPointPuppet(PuppetWidget *widget, nsPluginFrame* pluginFrame,
+ double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
+ double *destX, double *destY, NPCoordinateSpace destSpace);
+ static NPBool ConvertPointNoPuppet(nsIWidget *widget, nsPluginFrame* pluginFrame,
+ double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
+ double *destX, double *destY, NPCoordinateSpace destSpace);
+ void PerformDelayedBlurs();
+#endif // XP_MACOSX
+
int mLastMouseDownButtonType;
#ifdef MOZ_X11
diff --git a/dom/plugins/base/nsPluginNativeWindow.cpp b/dom/plugins/base/nsPluginNativeWindow.cpp
index 1c251936a5..f9baf5b813 100644
--- a/dom/plugins/base/nsPluginNativeWindow.cpp
+++ b/dom/plugins/base/nsPluginNativeWindow.cpp
@@ -27,7 +27,7 @@ nsPluginNativeWindowPLATFORM::nsPluginNativeWindowPLATFORM() : nsPluginNativeWin
width = 0;
height = 0;
memset(&clipRect, 0, sizeof(clipRect));
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
ws_info = nullptr;
#endif
type = NPWindowTypeWindow;
diff --git a/dom/plugins/base/nsPluginTags.cpp b/dom/plugins/base/nsPluginTags.cpp
index ae5ba87586..99e243a203 100644
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -424,7 +424,7 @@ void nsPluginTag::InitMime(const char* const* aMimeTypes,
}
}
-#if !defined(XP_WIN)
+#if !defined(XP_WIN) && !defined(XP_MACOSX)
static nsresult ConvertToUTF8(nsIUnicodeDecoder *aUnicodeDecoder,
nsAFlatCString& aString)
{
@@ -448,7 +448,7 @@ static nsresult ConvertToUTF8(nsIUnicodeDecoder *aUnicodeDecoder,
nsresult nsPluginTag::EnsureMembersAreUTF8()
{
-#if defined(XP_WIN)
+#if defined(XP_WIN) || defined(XP_MACOSX)
return NS_OK;
#else
nsresult rv;
diff --git a/dom/plugins/base/nsPluginsDirDarwin.cpp b/dom/plugins/base/nsPluginsDirDarwin.cpp
new file mode 100644
index 0000000000..0085eec0d6
--- /dev/null
+++ b/dom/plugins/base/nsPluginsDirDarwin.cpp
@@ -0,0 +1,572 @@
+/* -*- 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;
+}
diff --git a/dom/plugins/ipc/NPEventOSX.h b/dom/plugins/ipc/NPEventOSX.h
new file mode 100644
index 0000000000..efb6845d04
--- /dev/null
+++ b/dom/plugins/ipc/NPEventOSX.h
@@ -0,0 +1,193 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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_plugins_NPEventOSX_h
+#define mozilla_dom_plugins_NPEventOSX_h 1
+
+
+#include "npapi.h"
+
+namespace mozilla {
+
+namespace plugins {
+
+struct NPRemoteEvent {
+ NPCocoaEvent event;
+ double contentsScaleFactor;
+};
+
+} // namespace plugins
+
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::plugins::NPRemoteEvent>
+{
+ typedef mozilla::plugins::NPRemoteEvent paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ aMsg->WriteInt(aParam.event.type);
+ aMsg->WriteUInt32(aParam.event.version);
+ switch (aParam.event.type) {
+ case NPCocoaEventMouseDown:
+ case NPCocoaEventMouseUp:
+ case NPCocoaEventMouseMoved:
+ case NPCocoaEventMouseEntered:
+ case NPCocoaEventMouseExited:
+ case NPCocoaEventMouseDragged:
+ case NPCocoaEventScrollWheel:
+ aMsg->WriteUInt32(aParam.event.data.mouse.modifierFlags);
+ aMsg->WriteDouble(aParam.event.data.mouse.pluginX);
+ aMsg->WriteDouble(aParam.event.data.mouse.pluginY);
+ aMsg->WriteInt32(aParam.event.data.mouse.buttonNumber);
+ aMsg->WriteInt32(aParam.event.data.mouse.clickCount);
+ aMsg->WriteDouble(aParam.event.data.mouse.deltaX);
+ aMsg->WriteDouble(aParam.event.data.mouse.deltaY);
+ aMsg->WriteDouble(aParam.event.data.mouse.deltaZ);
+ break;
+ case NPCocoaEventKeyDown:
+ case NPCocoaEventKeyUp:
+ case NPCocoaEventFlagsChanged:
+ aMsg->WriteUInt32(aParam.event.data.key.modifierFlags);
+ WriteParam(aMsg, aParam.event.data.key.characters);
+ WriteParam(aMsg, aParam.event.data.key.charactersIgnoringModifiers);
+ aMsg->WriteUnsignedChar(aParam.event.data.key.isARepeat);
+ aMsg->WriteUInt16(aParam.event.data.key.keyCode);
+ break;
+ case NPCocoaEventFocusChanged:
+ case NPCocoaEventWindowFocusChanged:
+ aMsg->WriteUnsignedChar(aParam.event.data.focus.hasFocus);
+ break;
+ case NPCocoaEventDrawRect:
+ // We don't write out the context pointer, it would always be
+ // nullptr and is just filled in as such on the read.
+ aMsg->WriteDouble(aParam.event.data.draw.x);
+ aMsg->WriteDouble(aParam.event.data.draw.y);
+ aMsg->WriteDouble(aParam.event.data.draw.width);
+ aMsg->WriteDouble(aParam.event.data.draw.height);
+ break;
+ case NPCocoaEventTextInput:
+ WriteParam(aMsg, aParam.event.data.text.text);
+ break;
+ default:
+ NS_NOTREACHED("Attempted to serialize unknown event type.");
+ return;
+ }
+ aMsg->WriteDouble(aParam.contentsScaleFactor);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ int type = 0;
+ if (!aMsg->ReadInt(aIter, &type)) {
+ return false;
+ }
+ aResult->event.type = static_cast<NPCocoaEventType>(type);
+
+ if (!aMsg->ReadUInt32(aIter, &aResult->event.version)) {
+ return false;
+ }
+
+ switch (aResult->event.type) {
+ case NPCocoaEventMouseDown:
+ case NPCocoaEventMouseUp:
+ case NPCocoaEventMouseMoved:
+ case NPCocoaEventMouseEntered:
+ case NPCocoaEventMouseExited:
+ case NPCocoaEventMouseDragged:
+ case NPCocoaEventScrollWheel:
+ if (!aMsg->ReadUInt32(aIter, &aResult->event.data.mouse.modifierFlags)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.mouse.pluginX)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.mouse.pluginY)) {
+ return false;
+ }
+ if (!aMsg->ReadInt32(aIter, &aResult->event.data.mouse.buttonNumber)) {
+ return false;
+ }
+ if (!aMsg->ReadInt32(aIter, &aResult->event.data.mouse.clickCount)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.mouse.deltaX)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.mouse.deltaY)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.mouse.deltaZ)) {
+ return false;
+ }
+ break;
+ case NPCocoaEventKeyDown:
+ case NPCocoaEventKeyUp:
+ case NPCocoaEventFlagsChanged:
+ if (!aMsg->ReadUInt32(aIter, &aResult->event.data.key.modifierFlags)) {
+ return false;
+ }
+ if (!ReadParam(aMsg, aIter, &aResult->event.data.key.characters)) {
+ return false;
+ }
+ if (!ReadParam(aMsg, aIter, &aResult->event.data.key.charactersIgnoringModifiers)) {
+ return false;
+ }
+ if (!aMsg->ReadUnsignedChar(aIter, &aResult->event.data.key.isARepeat)) {
+ return false;
+ }
+ if (!aMsg->ReadUInt16(aIter, &aResult->event.data.key.keyCode)) {
+ return false;
+ }
+ break;
+ case NPCocoaEventFocusChanged:
+ case NPCocoaEventWindowFocusChanged:
+ if (!aMsg->ReadUnsignedChar(aIter, &aResult->event.data.focus.hasFocus)) {
+ return false;
+ }
+ break;
+ case NPCocoaEventDrawRect:
+ aResult->event.data.draw.context = nullptr;
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.draw.x)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.draw.y)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.draw.width)) {
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->event.data.draw.height)) {
+ return false;
+ }
+ break;
+ case NPCocoaEventTextInput:
+ if (!ReadParam(aMsg, aIter, &aResult->event.data.text.text)) {
+ return false;
+ }
+ break;
+ default:
+ NS_NOTREACHED("Attempted to de-serialize unknown event type.");
+ return false;
+ }
+ if (!aMsg->ReadDouble(aIter, &aResult->contentsScaleFactor)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ aLog->append(L"(NPCocoaEvent)");
+ }
+};
+
+} // namespace IPC
+
+#endif // ifndef mozilla_dom_plugins_NPEventOSX_h
diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp
index 03f7fa1611..a4f6b6b517 100644
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -103,7 +103,11 @@ using mozilla::gfx::SharedDIB;
const int kFlashWMUSERMessageThrottleDelayMs = 5;
static const TCHAR kPluginIgnoreSubclassProperty[] = TEXT("PluginIgnoreSubclassProperty");
-#endif
+
+#elif defined(XP_MACOSX)
+#include <ApplicationServices/ApplicationServices.h>
+#include "PluginUtilsOSX.h"
+#endif // defined(XP_MACOSX)
/**
* We can't use gfxPlatform::CreateDrawTargetForSurface() because calling
@@ -137,7 +141,7 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
, mMode(aMode)
, mNames(aNames)
, mValues(aValues)
-#if defined (XP_WIN)
+#if defined(XP_DARWIN) || defined (XP_WIN)
, mContentsScaleFactor(1.0)
#endif
, mPostingKeyEvents(0)
@@ -194,7 +198,7 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
mWindow.type = NPWindowTypeWindow;
mData.ndata = (void*) this;
mData.pdata = nullptr;
-#if defined(MOZ_X11) && defined(XP_UNIX)
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
mWindow.ws_info = &mWsInfo;
memset(&mWsInfo, 0, sizeof(mWsInfo));
#ifdef MOZ_WIDGET_GTK
@@ -203,7 +207,7 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
#else
mWsInfo.display = DefaultXDisplay();
#endif
-#endif // MOZ_X11 && XP_UNIX
+#endif // MOZ_X11 && XP_UNIX && !XP_MACOSX
#if defined(OS_WIN)
InitPopupMenuHook();
if (GetQuirks() & QUIRK_UNITY_FIXUP_MOUSE_CAPTURE) {
@@ -270,6 +274,23 @@ PluginInstanceChild::DoNPP_New()
Initialize();
+#if defined(XP_MACOSX) && defined(__i386__)
+ // If an i386 Mac OS X plugin has selected the Carbon event model then
+ // we have to fail. We do not support putting Carbon event model plugins
+ // out of process. Note that Carbon is the default model so out of process
+ // plugins need to actively negotiate something else in order to work
+ // out of process.
+ if (EventModel() == NPEventModelCarbon) {
+ // Send notification that a plugin tried to negotiate Carbon NPAPI so that
+ // users can be notified that restarting the browser in i386 mode may allow
+ // them to use the plugin.
+ SendNegotiatedCarbon();
+
+ // Fail to instantiate.
+ rv = NPERR_MODULE_LOAD_FAILED_ERROR;
+ }
+#endif
+
return rv;
}
@@ -477,12 +498,58 @@ PluginInstanceChild::NPN_GetValue(NPNVariable aVar,
}
#endif
-#if defined(XP_WIN)
+#ifdef XP_MACOSX
+ case NPNVsupportsCoreGraphicsBool: {
+ *((NPBool*)aValue) = true;
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsCoreAnimationBool: {
+ *((NPBool*)aValue) = true;
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsInvalidatingCoreAnimationBool: {
+ *((NPBool*)aValue) = true;
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsCompositingCoreAnimationPluginsBool: {
+ *((NPBool*)aValue) = true;
+ return NPERR_NO_ERROR;
+ }
+
+ case NPNVsupportsCocoaBool: {
+ *((NPBool*)aValue) = true;
+ return NPERR_NO_ERROR;
+ }
+
+#ifndef NP_NO_CARBON
+ case NPNVsupportsCarbonBool: {
+ *((NPBool*)aValue) = false;
+ return NPERR_NO_ERROR;
+ }
+#endif
+
+ case NPNVsupportsUpdatedCocoaTextInputBool: {
+ *static_cast<NPBool*>(aValue) = true;
+ return NPERR_NO_ERROR;
+ }
+
+#ifndef NP_NO_QUICKDRAW
+ case NPNVsupportsQuickDrawBool: {
+ *((NPBool*)aValue) = false;
+ return NPERR_NO_ERROR;
+ }
+#endif /* NP_NO_QUICKDRAW */
+#endif /* XP_MACOSX */
+
+#if defined(XP_MACOSX) || defined(XP_WIN)
case NPNVcontentsScaleFactor: {
*static_cast<double*>(aValue) = mContentsScaleFactor;
return NPERR_NO_ERROR;
}
-#endif /* defined(XP_WIN) */
+#endif /* defined(XP_MACOSX) || defined(XP_WIN) */
case NPNVCSSZoomFactor: {
*static_cast<double*>(aValue) = mCSSZoomFactor;
@@ -587,12 +654,36 @@ PluginInstanceChild::NPN_SetValue(NPPVariable aVar, void* aValue)
mDrawingModel = drawingModel;
+#ifdef XP_MACOSX
+ if (drawingModel == NPDrawingModelCoreAnimation) {
+ mCARefreshTimer = ScheduleTimer(DEFAULT_REFRESH_MS, true, CAUpdate);
+ }
+#endif
+
PLUGIN_LOG_DEBUG((" Plugin requested drawing model id #%i\n",
mDrawingModel));
return rv;
}
+#ifdef XP_MACOSX
+ case NPPVpluginEventModel: {
+ NPError rv;
+ int eventModel = (int16_t) (intptr_t) aValue;
+
+ if (!CallNPN_SetValue_NPPVpluginEventModel(eventModel, &rv))
+ return NPERR_GENERIC_ERROR;
+#if defined(__i386__)
+ mEventModel = static_cast<NPEventModel>(eventModel);
+#endif
+
+ PLUGIN_LOG_DEBUG((" Plugin requested event model id # %i\n",
+ eventModel));
+
+ return rv;
+ }
+#endif
+
case NPPVpluginIsPlayingAudio: {
NPError rv = NPERR_GENERIC_ERROR;
if (!CallNPN_SetValue_NPPVpluginIsPlayingAudio((NPBool)(intptr_t)aValue, &rv)) {
@@ -816,10 +907,21 @@ PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
event.event.xgraphicsexpose.drawable));
#endif
+#ifdef XP_MACOSX
+ // Mac OS X does not define an NPEvent structure. It defines more specific types.
+ NPCocoaEvent evcopy = event.event;
+
+ // Make sure we reset mCurrentEvent in case of an exception
+ AutoRestore<const NPCocoaEvent*> savePreviousEvent(mCurrentEvent);
+
+ // Track the current event for NPN_PopUpContextMenu.
+ mCurrentEvent = &event.event;
+#else
// Make a copy since we may modify values.
NPEvent evcopy = event.event;
+#endif
-#if defined(XP_WIN)
+#if defined(XP_MACOSX) || defined(XP_WIN)
// event.contentsScaleFactor <= 0 is a signal we shouldn't use it,
// for example when AnswerNPP_HandleEvent() is called from elsewhere
// in the child process (not via rpc code from the parent process).
@@ -845,6 +947,18 @@ PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
else
*handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
+#ifdef XP_MACOSX
+ // Release any reference counted objects created in the child process.
+ if (evcopy.type == NPCocoaEventKeyDown ||
+ evcopy.type == NPCocoaEventKeyUp) {
+ ::CFRelease((CFStringRef)evcopy.data.key.characters);
+ ::CFRelease((CFStringRef)evcopy.data.key.charactersIgnoringModifiers);
+ }
+ else if (evcopy.type == NPCocoaEventTextInput) {
+ ::CFRelease((CFStringRef)evcopy.data.text.text);
+ }
+#endif
+
#ifdef MOZ_X11
if (GraphicsExpose == event.event.type) {
// Make sure the X server completes the drawing before the parent
@@ -861,6 +975,73 @@ PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
return true;
}
+#ifdef XP_MACOSX
+
+bool
+PluginInstanceChild::AnswerNPP_HandleEvent_Shmem(const NPRemoteEvent& event,
+ Shmem&& mem,
+ int16_t* handled,
+ Shmem* rtnmem)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ AssertPluginThread();
+ AutoStackHelper guard(this);
+
+ PaintTracker pt;
+
+ NPCocoaEvent evcopy = event.event;
+ mContentsScaleFactor = event.contentsScaleFactor;
+
+ if (evcopy.type == NPCocoaEventDrawRect) {
+ int scaleFactor = ceil(mContentsScaleFactor);
+ if (!mShColorSpace) {
+ mShColorSpace = CreateSystemColorSpace();
+ if (!mShColorSpace) {
+ PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
+ *handled = false;
+ *rtnmem = mem;
+ return true;
+ }
+ }
+ if (!mShContext) {
+ void* cgContextByte = mem.get<char>();
+ mShContext = ::CGBitmapContextCreate(cgContextByte,
+ mWindow.width * scaleFactor,
+ mWindow.height * scaleFactor, 8,
+ mWindow.width * 4 * scaleFactor, mShColorSpace,
+ kCGImageAlphaPremultipliedFirst |
+ kCGBitmapByteOrder32Host);
+
+ if (!mShContext) {
+ PLUGIN_LOG_DEBUG(("Could not allocate CGBitmapContext."));
+ *handled = false;
+ *rtnmem = mem;
+ return true;
+ }
+ }
+ CGRect clearRect = ::CGRectMake(0, 0, mWindow.width, mWindow.height);
+ ::CGContextClearRect(mShContext, clearRect);
+ evcopy.data.draw.context = mShContext;
+ } else {
+ PLUGIN_LOG_DEBUG(("Invalid event type for AnswerNNP_HandleEvent_Shmem."));
+ *handled = false;
+ *rtnmem = mem;
+ return true;
+ }
+
+ if (!mPluginIface->event) {
+ *handled = false;
+ } else {
+ ::CGContextSaveGState(evcopy.data.draw.context);
+ *handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
+ ::CGContextRestoreGState(evcopy.data.draw.context);
+ }
+
+ *rtnmem = mem;
+ return true;
+}
+
+#else
bool
PluginInstanceChild::AnswerNPP_HandleEvent_Shmem(const NPRemoteEvent& event,
Shmem&& mem,
@@ -871,7 +1052,101 @@ PluginInstanceChild::AnswerNPP_HandleEvent_Shmem(const NPRemoteEvent& event,
*rtnmem = mem;
return true;
}
+#endif
+
+#ifdef XP_MACOSX
+
+void CallCGDraw(CGContextRef ref, void* aPluginInstance, nsIntRect aUpdateRect) {
+ PluginInstanceChild* pluginInstance = (PluginInstanceChild*)aPluginInstance;
+
+ pluginInstance->CGDraw(ref, aUpdateRect);
+}
+
+bool
+PluginInstanceChild::CGDraw(CGContextRef ref, nsIntRect aUpdateRect) {
+
+ NPCocoaEvent drawEvent;
+ drawEvent.type = NPCocoaEventDrawRect;
+ drawEvent.version = 0;
+ drawEvent.data.draw.x = aUpdateRect.x;
+ drawEvent.data.draw.y = aUpdateRect.y;
+ drawEvent.data.draw.width = aUpdateRect.width;
+ drawEvent.data.draw.height = aUpdateRect.height;
+ drawEvent.data.draw.context = ref;
+
+ NPRemoteEvent remoteDrawEvent = {drawEvent};
+ // Signal to AnswerNPP_HandleEvent() not to use this value
+ remoteDrawEvent.contentsScaleFactor = -1.0;
+
+ int16_t handled;
+ AnswerNPP_HandleEvent(remoteDrawEvent, &handled);
+ return handled == true;
+}
+
+bool
+PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(const NPRemoteEvent& event,
+ const uint32_t &surfaceid,
+ int16_t* handled)
+{
+ PLUGIN_LOG_DEBUG_FUNCTION;
+ AssertPluginThread();
+ AutoStackHelper guard(this);
+
+ PaintTracker pt;
+
+ NPCocoaEvent evcopy = event.event;
+ mContentsScaleFactor = event.contentsScaleFactor;
+ RefPtr<MacIOSurface> surf = MacIOSurface::LookupSurface(surfaceid,
+ mContentsScaleFactor);
+ if (!surf) {
+ NS_ERROR("Invalid IOSurface.");
+ *handled = false;
+ return false;
+ }
+
+ if (!mCARenderer) {
+ mCARenderer = new nsCARenderer();
+ }
+
+ if (evcopy.type == NPCocoaEventDrawRect) {
+ mCARenderer->AttachIOSurface(surf);
+ if (!mCARenderer->isInit()) {
+ void *caLayer = nullptr;
+ NPError result = mPluginIface->getvalue(GetNPP(),
+ NPPVpluginCoreAnimationLayer,
+ &caLayer);
+
+ if (result != NPERR_NO_ERROR || !caLayer) {
+ PLUGIN_LOG_DEBUG(("Plugin requested CoreAnimation but did not "
+ "provide CALayer."));
+ *handled = false;
+ return false;
+ }
+
+ mCARenderer->SetupRenderer(caLayer, mWindow.width, mWindow.height,
+ mContentsScaleFactor,
+ GetQuirks() & QUIRK_ALLOW_OFFLINE_RENDERER ?
+ ALLOW_OFFLINE_RENDERER : DISALLOW_OFFLINE_RENDERER);
+
+ // Flash needs to have the window set again after this step
+ if (mPluginIface->setwindow)
+ (void) mPluginIface->setwindow(&mData, &mWindow);
+ }
+ } else {
+ PLUGIN_LOG_DEBUG(("Invalid event type for "
+ "AnswerNNP_HandleEvent_IOSurface."));
+ *handled = false;
+ return false;
+ }
+
+ mCARenderer->Render(mWindow.width, mWindow.height,
+ mContentsScaleFactor, nullptr);
+
+ return true;
+
+}
+#else
bool
PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(const NPRemoteEvent& event,
const uint32_t &surfaceid,
@@ -880,6 +1155,7 @@ PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(const NPRemoteEvent& event,
NS_RUNTIMEABORT("NPP_HandleEvent_IOSurface is a OSX-only message");
return false;
}
+#endif
bool
PluginInstanceChild::RecvWindowPosChanged(const NPRemoteEvent& event)
@@ -899,16 +1175,24 @@ PluginInstanceChild::RecvWindowPosChanged(const NPRemoteEvent& event)
bool
PluginInstanceChild::RecvContentsScaleFactorChanged(const double& aContentsScaleFactor)
{
-#if defined(XP_WIN)
+#if defined(XP_MACOSX) || defined(XP_WIN)
mContentsScaleFactor = aContentsScaleFactor;
+#if defined(XP_MACOSX)
+ if (mShContext) {
+ // Release the shared context so that it is reallocated
+ // with the new size.
+ ::CGContextRelease(mShContext);
+ mShContext = nullptr;
+ }
+#endif
return true;
#else
- NS_RUNTIMEABORT("ContentsScaleFactorChanged is a Windows-only message");
+ NS_RUNTIMEABORT("ContentsScaleFactorChanged is an Windows or OSX only message");
return false;
#endif
}
-#if defined(MOZ_X11) && defined(XP_UNIX)
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
// Create a new window from NPWindow
bool PluginInstanceChild::CreateWindow(const NPRemoteWindow& aWindow)
{
@@ -1006,7 +1290,7 @@ PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
AssertPluginThread();
AutoStackHelper guard(this);
-#if defined(MOZ_X11) && defined(XP_UNIX)
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
NS_ASSERTION(mWsInfo.display, "We should have a valid display!");
// The minimum info is sent over IPC to allow this
@@ -1123,6 +1407,26 @@ PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
break;
}
+#elif defined(XP_MACOSX)
+
+ mWindow.x = aWindow.x;
+ mWindow.y = aWindow.y;
+ mWindow.width = aWindow.width;
+ mWindow.height = aWindow.height;
+ mWindow.clipRect = aWindow.clipRect;
+ mWindow.type = aWindow.type;
+ mContentsScaleFactor = aWindow.contentsScaleFactor;
+
+ if (mShContext) {
+ // Release the shared context so that it is reallocated
+ // with the new size.
+ ::CGContextRelease(mShContext);
+ mShContext = nullptr;
+ }
+
+ if (mPluginIface->setwindow)
+ (void) mPluginIface->setwindow(&mData, &mWindow);
+
#elif defined(MOZ_WIDGET_UIKIT)
// Don't care
#else
@@ -3068,7 +3372,7 @@ PluginInstanceChild::DoAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
mWindow.height = aWindow.height;
mWindow.clipRect = aWindow.clipRect;
mWindow.type = aWindow.type;
-#if defined(XP_WIN)
+#if defined(XP_MACOSX) || defined(XP_WIN)
mContentsScaleFactor = aWindow.contentsScaleFactor;
#endif
@@ -3216,6 +3520,7 @@ PluginInstanceChild::MaybeCreatePlatformHelperSurface(void)
bool
PluginInstanceChild::EnsureCurrentBuffer(void)
{
+#ifndef XP_DARWIN
nsIntRect toInvalidate(0, 0, 0, 0);
IntSize winSize = IntSize(mWindow.width, mWindow.height);
@@ -3259,6 +3564,61 @@ PluginInstanceChild::EnsureCurrentBuffer(void)
NS_ERROR("Cannot create helper surface");
return false;
}
+
+ return true;
+#elif defined(XP_MACOSX)
+
+ if (!mDoubleBufferCARenderer.HasCALayer()) {
+ void *caLayer = nullptr;
+ if (mDrawingModel == NPDrawingModelCoreGraphics) {
+ if (!mCGLayer) {
+ caLayer = mozilla::plugins::PluginUtilsOSX::GetCGLayer(CallCGDraw,
+ this,
+ mContentsScaleFactor);
+
+ if (!caLayer) {
+ PLUGIN_LOG_DEBUG(("GetCGLayer failed."));
+ return false;
+ }
+ }
+ mCGLayer = caLayer;
+ } else {
+ NPError result = mPluginIface->getvalue(GetNPP(),
+ NPPVpluginCoreAnimationLayer,
+ &caLayer);
+ if (result != NPERR_NO_ERROR || !caLayer) {
+ PLUGIN_LOG_DEBUG(("Plugin requested CoreAnimation but did not "
+ "provide CALayer."));
+ return false;
+ }
+ }
+ mDoubleBufferCARenderer.SetCALayer(caLayer);
+ }
+
+ if (mDoubleBufferCARenderer.HasFrontSurface() &&
+ (mDoubleBufferCARenderer.GetFrontSurfaceWidth() != mWindow.width ||
+ mDoubleBufferCARenderer.GetFrontSurfaceHeight() != mWindow.height ||
+ mDoubleBufferCARenderer.GetContentsScaleFactor() != mContentsScaleFactor)) {
+ mDoubleBufferCARenderer.ClearFrontSurface();
+ }
+
+ if (!mDoubleBufferCARenderer.HasFrontSurface()) {
+ bool allocSurface = mDoubleBufferCARenderer.InitFrontSurface(
+ mWindow.width, mWindow.height, mContentsScaleFactor,
+ GetQuirks() & QUIRK_ALLOW_OFFLINE_RENDERER ?
+ ALLOW_OFFLINE_RENDERER : DISALLOW_OFFLINE_RENDERER);
+ if (!allocSurface) {
+ PLUGIN_LOG_DEBUG(("Fail to allocate front IOSurface"));
+ return false;
+ }
+
+ if (mPluginIface->setwindow)
+ (void) mPluginIface->setwindow(&mData, &mWindow);
+
+ nsIntRect toInvalidate(0, 0, mWindow.width, mWindow.height);
+ mAccumulatedInvalidRect.UnionRect(mAccumulatedInvalidRect, toInvalidate);
+ }
+#endif
return true;
}
@@ -3302,6 +3662,8 @@ PluginInstanceChild::UpdateWindowAttributes(bool aForceSetWindow)
return;
}
+#ifndef XP_MACOSX
+ // Adjusting the window isn't needed for OSX
#ifndef XP_WIN
// On Windows, we translate the device context, in order for the window
// origin to be correct.
@@ -3322,6 +3684,7 @@ PluginInstanceChild::UpdateWindowAttributes(bool aForceSetWindow)
mWindow.clipRect.right = clipRect.XMost();
mWindow.clipRect.bottom = clipRect.YMost();
}
+#endif // XP_MACOSX
#ifdef XP_WIN
// Windowless plugins on Windows need a WM_WINDOWPOSCHANGED event to update
@@ -4292,7 +4655,7 @@ PluginInstanceChild::Destroy()
xt_client_xloop_destroy();
}
#endif
-#if defined(MOZ_X11) && defined(XP_UNIX)
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
DeleteWindow();
#endif
}
diff --git a/dom/plugins/ipc/PluginInstanceChild.h b/dom/plugins/ipc/PluginInstanceChild.h
index 627d37c367..0ad6e145d7 100644
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -206,7 +206,7 @@ protected:
virtual bool
RecvNPP_DidComposite() override;
-#if defined(MOZ_X11) && defined(XP_UNIX)
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
bool CreateWindow(const NPRemoteWindow& aWindow);
void DeleteWindow();
#endif
@@ -414,7 +414,7 @@ private:
InfallibleTArray<nsCString> mValues;
NPP_t mData;
NPWindow mWindow;
-#if defined(XP_WIN)
+#if defined(XP_DARWIN) || defined(XP_WIN)
double mContentsScaleFactor;
#endif
double mCSSZoomFactor;
@@ -456,7 +456,7 @@ private:
PluginScriptableObjectChild* mCachedWindowActor;
PluginScriptableObjectChild* mCachedElementActor;
-#if defined(MOZ_X11) && defined(XP_UNIX)
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
NPSetWindowCallbackStruct mWsInfo;
#ifdef MOZ_WIDGET_GTK
bool mXEmbed;
@@ -520,10 +520,15 @@ private:
bool CanPaintOnBackground();
bool IsVisible() {
+#ifdef XP_MACOSX
+ return mWindow.clipRect.top != mWindow.clipRect.bottom &&
+ mWindow.clipRect.left != mWindow.clipRect.right;
+#else
return mWindow.clipRect.top != 0 ||
mWindow.clipRect.left != 0 ||
mWindow.clipRect.bottom != 0 ||
mWindow.clipRect.right != 0;
+#endif
}
// ShowPluginFrame - in general does four things:
@@ -604,6 +609,12 @@ private:
// surface which is on ParentProcess side
RefPtr<gfxASurface> mBackSurface;
+#ifdef XP_MACOSX
+ // Current IOSurface available for rendering
+ // We can't use thebes gfxASurface like other platforms.
+ PluginUtilsOSX::nsDoubleBufferCARenderer mDoubleBufferCARenderer;
+#endif
+
// (Not to be confused with mBackSurface). This is a recent copy
// of the opaque pixels under our object frame, if
// |mIsTransparent|. We ask the plugin render directly onto a
diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp
index d72c9de49b..372a7a238b 100644
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -50,6 +50,10 @@
# include "mozilla/layers/TextureD3D11.h"
#endif
+#ifdef XP_MACOSX
+#include "MacIOSurfaceImage.h"
+#endif
+
#if defined(OS_WIN)
#include <windowsx.h>
#include "gfxWindowsPlatform.h"
@@ -63,7 +67,9 @@ extern const wchar_t* kFlashFullscreenClass;
#elif defined(MOZ_WIDGET_GTK)
#include "mozilla/dom/ContentChild.h"
#include <gdk/gdk.h>
-#endif
+#elif defined(XP_MACOSX)
+#include <ApplicationServices/ApplicationServices.h>
+#endif // defined(XP_MACOSX)
using namespace mozilla;
using namespace mozilla::plugins;
@@ -128,6 +134,11 @@ PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent,
, mPluginWndProc(nullptr)
, mNestedEventState(false)
#endif // defined(XP_WIN)
+#if defined(XP_MACOSX)
+ , mShWidth(0)
+ , mShHeight(0)
+ , mShColorSpace(nullptr)
+#endif
{
#if defined(OS_WIN)
if (!sPluginInstanceList) {
@@ -256,6 +267,8 @@ PluginInstanceParent::AnswerNPN_GetValue_NPNVnetscapeWindow(NativeWindowHandle*
HWND id;
#elif defined(MOZ_X11)
XID id;
+#elif defined(XP_DARWIN)
+ intptr_t id;
#else
#warning Implement me
#endif
@@ -445,7 +458,14 @@ PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginDrawingModel(
bool allowed = false;
switch (drawingModel) {
-#if defined(XP_WIN)
+#if defined(XP_MACOSX)
+ case NPDrawingModelCoreAnimation:
+ case NPDrawingModelInvalidatingCoreAnimation:
+ case NPDrawingModelOpenGL:
+ case NPDrawingModelCoreGraphics:
+ allowed = true;
+ break;
+#elif defined(XP_WIN)
case NPDrawingModelSyncWin:
allowed = true;
break;
@@ -474,6 +494,16 @@ PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginDrawingModel(
int requestModel = drawingModel;
+#ifdef XP_MACOSX
+ if (drawingModel == NPDrawingModelCoreAnimation ||
+ drawingModel == NPDrawingModelInvalidatingCoreAnimation) {
+ // We need to request CoreGraphics otherwise
+ // the nsPluginFrame will try to draw a CALayer
+ // that can not be shared across process.
+ requestModel = NPDrawingModelCoreGraphics;
+ }
+#endif
+
*result = mNPNIface->setvalue(mNPP, NPPVpluginDrawingModel,
(void*)(intptr_t)requestModel);
@@ -484,8 +514,14 @@ bool
PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginEventModel(
const int& eventModel, NPError* result)
{
+#ifdef XP_MACOSX
+ *result = mNPNIface->setvalue(mNPP, NPPVpluginEventModel,
+ (void*)(intptr_t)eventModel);
+ return true;
+#else
*result = NPERR_GENERIC_ERROR;
return true;
+#endif
}
bool
@@ -839,6 +875,35 @@ PluginInstanceParent::RecvShow(const NPRect& updatedRect,
}
surface = gfxSharedImageSurface::Open(newSurface.get_Shmem());
}
+#ifdef XP_MACOSX
+ else if (newSurface.type() == SurfaceDescriptor::TIOSurfaceDescriptor) {
+ IOSurfaceDescriptor iodesc = newSurface.get_IOSurfaceDescriptor();
+
+ RefPtr<MacIOSurface> newIOSurface =
+ MacIOSurface::LookupSurface(iodesc.surfaceId(),
+ iodesc.contentsScaleFactor());
+
+ if (!newIOSurface) {
+ NS_WARNING("Got bad IOSurfaceDescriptor in RecvShow");
+ return false;
+ }
+
+ if (mFrontIOSurface)
+ *prevSurface = IOSurfaceDescriptor(mFrontIOSurface->GetIOSurfaceID(),
+ mFrontIOSurface->GetContentsScaleFactor());
+ else
+ *prevSurface = null_t();
+
+ mFrontIOSurface = newIOSurface;
+
+ RecvNPN_InvalidateRect(updatedRect);
+
+ PLUGIN_LOG_DEBUG((" (RecvShow invalidated for surface %p)",
+ mFrontSurface.get()));
+
+ return true;
+ }
+#endif
#ifdef MOZ_X11
else if (newSurface.type() == SurfaceDescriptor::TSurfaceDescriptorX11) {
surface = newSurface.get_SurfaceDescriptorX11().OpenForeign();
@@ -920,7 +985,7 @@ PluginInstanceParent::AsyncSetWindow(NPWindow* aWindow)
window.height = aWindow->height;
window.clipRect = aWindow->clipRect;
window.type = aWindow->type;
-#if defined(XP_WIN)
+#if defined(XP_MACOSX) || defined(XP_WIN)
double scaleFactor = 1.0;
mNPNIface->getvalue(mNPP, NPNVcontentsScaleFactor, &scaleFactor);
window.contentsScaleFactor = scaleFactor;
@@ -950,7 +1015,19 @@ PluginInstanceParent::GetImageContainer(ImageContainer** aContainer)
return NS_OK;
}
+#ifdef XP_MACOSX
+ MacIOSurface* ioSurface = nullptr;
+
+ if (mFrontIOSurface) {
+ ioSurface = mFrontIOSurface;
+ } else if (mIOSurface) {
+ ioSurface = mIOSurface;
+ }
+
+ if (!mFrontSurface && !ioSurface)
+#else
if (!mFrontSurface)
+#endif
return NS_ERROR_NOT_AVAILABLE;
ImageContainer *container = GetImageContainer();
@@ -959,6 +1036,17 @@ PluginInstanceParent::GetImageContainer(ImageContainer** aContainer)
return NS_ERROR_FAILURE;
}
+#ifdef XP_MACOSX
+ if (ioSurface) {
+ RefPtr<Image> image = new MacIOSurfaceImage(ioSurface);
+ container->SetCurrentImageInTransaction(image);
+
+ NS_IF_ADDREF(container);
+ *aContainer = container;
+ return NS_OK;
+ }
+#endif
+
NS_IF_ADDREF(container);
*aContainer = container;
return NS_OK;
@@ -981,6 +1069,16 @@ PluginInstanceParent::GetImageSize(nsIntSize* aSize)
return NS_OK;
}
+#ifdef XP_MACOSX
+ if (mFrontIOSurface) {
+ *aSize = nsIntSize(mFrontIOSurface->GetWidth(), mFrontIOSurface->GetHeight());
+ return NS_OK;
+ } else if (mIOSurface) {
+ *aSize = nsIntSize(mIOSurface->GetWidth(), mIOSurface->GetHeight());
+ return NS_OK;
+ }
+#endif
+
return NS_ERROR_NOT_AVAILABLE;
}
@@ -993,14 +1091,23 @@ PluginInstanceParent::DidComposite()
Unused << SendNPP_DidComposite();
}
-#if defined(XP_WIN)
+#ifdef XP_MACOSX
+nsresult
+PluginInstanceParent::IsRemoteDrawingCoreAnimation(bool *aDrawing)
+{
+ *aDrawing = (NPDrawingModelCoreAnimation == (NPDrawingModel)mDrawingModel ||
+ NPDrawingModelInvalidatingCoreAnimation == (NPDrawingModel)mDrawingModel);
+ return NS_OK;
+}
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
nsresult
PluginInstanceParent::ContentsScaleFactorChanged(double aContentsScaleFactor)
{
bool rv = SendContentsScaleFactorChanged(aContentsScaleFactor);
return rv ? NS_OK : NS_ERROR_FAILURE;
}
-#endif // #ifdef XP_WIN
+#endif // #ifdef XP_MACOSX
nsresult
PluginInstanceParent::SetBackgroundUnknown()
@@ -1252,14 +1359,40 @@ PluginInstanceParent::NPP_SetWindow(const NPWindow* aWindow)
window.type = aWindow->type;
#endif
-#if defined(XP_WIN)
+#if defined(XP_MACOSX) || defined(XP_WIN)
double floatScaleFactor = 1.0;
mNPNIface->getvalue(mNPP, NPNVcontentsScaleFactor, &floatScaleFactor);
int scaleFactor = ceil(floatScaleFactor);
window.contentsScaleFactor = floatScaleFactor;
#endif
+#if defined(XP_MACOSX)
+ if (mShWidth != window.width * scaleFactor || mShHeight != window.height * scaleFactor) {
+ if (mDrawingModel == NPDrawingModelCoreAnimation ||
+ mDrawingModel == NPDrawingModelInvalidatingCoreAnimation) {
+ mIOSurface = MacIOSurface::CreateIOSurface(window.width, window.height,
+ floatScaleFactor);
+ } else if (uint32_t(mShWidth * mShHeight) !=
+ window.width * scaleFactor * window.height * scaleFactor) {
+ if (mShWidth != 0 && mShHeight != 0) {
+ DeallocShmem(mShSurface);
+ mShWidth = 0;
+ mShHeight = 0;
+ }
+
+ if (window.width != 0 && window.height != 0) {
+ if (!AllocShmem(window.width * scaleFactor * window.height*4 * scaleFactor,
+ SharedMemory::TYPE_BASIC, &mShSurface)) {
+ PLUGIN_LOG_DEBUG(("Shared memory could not be allocated."));
+ return NPERR_GENERIC_ERROR;
+ }
+ }
+ }
+ mShWidth = window.width * scaleFactor;
+ mShHeight = window.height * scaleFactor;
+ }
+#endif
-#if defined(MOZ_X11) && defined(XP_UNIX)
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
const NPSetWindowCallbackStruct* ws_info =
static_cast<NPSetWindowCallbackStruct*>(aWindow->ws_info);
window.visualID = ws_info->visual ? ws_info->visual->visualid : 0;
@@ -1419,10 +1552,14 @@ PluginInstanceParent::NPP_HandleEvent(void* event)
{
PLUGIN_LOG_DEBUG_FUNCTION;
+#if defined(XP_MACOSX)
+ NPCocoaEvent* npevent = reinterpret_cast<NPCocoaEvent*>(event);
+#else
NPEvent* npevent = reinterpret_cast<NPEvent*>(event);
+#endif
NPRemoteEvent npremoteevent;
npremoteevent.event = *npevent;
-#if defined(XP_WIN)
+#if defined(XP_MACOSX) || defined(XP_WIN)
double scaleFactor = 1.0;
mNPNIface->getvalue(mNPP, NPNVcontentsScaleFactor, &scaleFactor);
npremoteevent.contentsScaleFactor = scaleFactor;
@@ -1512,6 +1649,111 @@ PluginInstanceParent::NPP_HandleEvent(void* event)
}
#endif
+#ifdef XP_MACOSX
+ if (npevent->type == NPCocoaEventDrawRect) {
+ if (mDrawingModel == NPDrawingModelCoreAnimation ||
+ mDrawingModel == NPDrawingModelInvalidatingCoreAnimation) {
+ if (!mIOSurface) {
+ NS_ERROR("No IOSurface allocated.");
+ return false;
+ }
+ if (!CallNPP_HandleEvent_IOSurface(npremoteevent,
+ mIOSurface->GetIOSurfaceID(),
+ &handled))
+ return false; // no good way to handle errors here...
+
+ CGContextRef cgContext = npevent->data.draw.context;
+ if (!mShColorSpace) {
+ mShColorSpace = CreateSystemColorSpace();
+ }
+ if (!mShColorSpace) {
+ PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
+ return false;
+ }
+ if (cgContext) {
+ nsCARenderer::DrawSurfaceToCGContext(cgContext, mIOSurface,
+ mShColorSpace,
+ npevent->data.draw.x,
+ npevent->data.draw.y,
+ npevent->data.draw.width,
+ npevent->data.draw.height);
+ }
+ return true;
+ } else if (mFrontIOSurface) {
+ CGContextRef cgContext = npevent->data.draw.context;
+ if (!mShColorSpace) {
+ mShColorSpace = CreateSystemColorSpace();
+ }
+ if (!mShColorSpace) {
+ PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
+ return false;
+ }
+ if (cgContext) {
+ nsCARenderer::DrawSurfaceToCGContext(cgContext, mFrontIOSurface,
+ mShColorSpace,
+ npevent->data.draw.x,
+ npevent->data.draw.y,
+ npevent->data.draw.width,
+ npevent->data.draw.height);
+ }
+ return true;
+ } else {
+ if (mShWidth == 0 && mShHeight == 0) {
+ PLUGIN_LOG_DEBUG(("NPCocoaEventDrawRect on window of size 0."));
+ return false;
+ }
+ if (!mShSurface.IsReadable()) {
+ PLUGIN_LOG_DEBUG(("Shmem is not readable."));
+ return false;
+ }
+
+ if (!CallNPP_HandleEvent_Shmem(npremoteevent, mShSurface,
+ &handled, &mShSurface))
+ return false; // no good way to handle errors here...
+
+ if (!mShSurface.IsReadable()) {
+ PLUGIN_LOG_DEBUG(("Shmem not returned. Either the plugin crashed "
+ "or we have a bug."));
+ return false;
+ }
+
+ char* shContextByte = mShSurface.get<char>();
+
+ if (!mShColorSpace) {
+ mShColorSpace = CreateSystemColorSpace();
+ }
+ if (!mShColorSpace) {
+ PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
+ return false;
+ }
+ CGContextRef shContext = ::CGBitmapContextCreate(shContextByte,
+ mShWidth, mShHeight, 8,
+ mShWidth*4, mShColorSpace,
+ kCGImageAlphaPremultipliedFirst |
+ kCGBitmapByteOrder32Host);
+ if (!shContext) {
+ PLUGIN_LOG_DEBUG(("Could not allocate CGBitmapContext."));
+ return false;
+ }
+
+ CGImageRef shImage = ::CGBitmapContextCreateImage(shContext);
+ if (shImage) {
+ CGContextRef cgContext = npevent->data.draw.context;
+
+ ::CGContextDrawImage(cgContext,
+ CGRectMake(0,0,mShWidth,mShHeight),
+ shImage);
+ ::CGImageRelease(shImage);
+ } else {
+ ::CGContextRelease(shContext);
+ return false;
+ }
+ ::CGContextRelease(shContext);
+ return true;
+ }
+ }
+#endif
+
if (!CallNPP_HandleEvent(npremoteevent, &handled))
return 0; // no good way to handle errors here...
diff --git a/dom/plugins/ipc/PluginInstanceParent.h b/dom/plugins/ipc/PluginInstanceParent.h
index 7c9febb242..cb85378db6 100644
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -321,7 +321,10 @@ public:
nsresult AsyncSetWindow(NPWindow* window);
nsresult GetImageContainer(mozilla::layers::ImageContainer** aContainer);
nsresult GetImageSize(nsIntSize* aSize);
-#if defined(XP_WIN)
+#ifdef XP_MACOSX
+ nsresult IsRemoteDrawingCoreAnimation(bool *aDrawing);
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
nsresult ContentsScaleFactorChanged(double aContentsScaleFactor);
#endif
nsresult SetBackgroundUnknown();
diff --git a/dom/plugins/ipc/PluginInterposeOSX.h b/dom/plugins/ipc/PluginInterposeOSX.h
new file mode 100644
index 0000000000..2a742b8aa2
--- /dev/null
+++ b/dom/plugins/ipc/PluginInterposeOSX.h
@@ -0,0 +1,137 @@
+// vim:set ts=2 sts=2 sw=2 et cin:
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 THE COPYRIGHT
+// OWNER 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.
+
+#ifndef DOM_PLUGINS_IPC_PLUGININTERPOSEOSX_H
+#define DOM_PLUGINS_IPC_PLUGININTERPOSEOSX_H
+
+#include "base/basictypes.h"
+#include "nsPoint.h"
+#include "npapi.h"
+
+// Make this includable from non-Objective-C code.
+#ifndef __OBJC__
+class NSCursor;
+#else
+#import <Cocoa/Cocoa.h>
+#endif
+
+// The header file QuickdrawAPI.h is missing on OS X 10.7 and up (though the
+// QuickDraw APIs defined in it are still present) -- so we need to supply the
+// relevant parts of its contents here. It's likely that Apple will eventually
+// remove the APIs themselves (probably in OS X 10.8), so we need to make them
+// weak imports, and test for their presence before using them.
+#if !defined(__QUICKDRAWAPI__)
+
+typedef short Bits16[16];
+struct Cursor {
+ Bits16 data;
+ Bits16 mask;
+ Point hotSpot;
+};
+typedef struct Cursor Cursor;
+
+#endif /* __QUICKDRAWAPI__ */
+
+namespace mac_plugin_interposing {
+
+// Class used to serialize NSCursor objects over IPC between processes.
+class NSCursorInfo {
+public:
+ enum Type {
+ TypeCustom,
+ TypeArrow,
+ TypeClosedHand,
+ TypeContextualMenu, // Only supported on OS X 10.6 and up
+ TypeCrosshair,
+ TypeDisappearingItem,
+ TypeDragCopy, // Only supported on OS X 10.6 and up
+ TypeDragLink, // Only supported on OS X 10.6 and up
+ TypeIBeam,
+ TypeNotAllowed, // Only supported on OS X 10.6 and up
+ TypeOpenHand,
+ TypePointingHand,
+ TypeResizeDown,
+ TypeResizeLeft,
+ TypeResizeLeftRight,
+ TypeResizeRight,
+ TypeResizeUp,
+ TypeResizeUpDown,
+ TypeTransparent // Special type
+ };
+
+ NSCursorInfo();
+ explicit NSCursorInfo(NSCursor* aCursor);
+ explicit NSCursorInfo(const Cursor* aCursor);
+ ~NSCursorInfo();
+
+ NSCursor* GetNSCursor() const;
+ Type GetType() const;
+ const char* GetTypeName() const;
+ nsPoint GetHotSpot() const;
+ uint8_t* GetCustomImageData() const;
+ uint32_t GetCustomImageDataLength() const;
+
+ void SetType(Type aType);
+ void SetHotSpot(nsPoint aHotSpot);
+ void SetCustomImageData(uint8_t* aData, uint32_t aDataLength);
+
+ static bool GetNativeCursorsSupported();
+
+private:
+ NSCursor* GetTransparentCursor() const;
+
+ Type mType;
+ // The hot spot's coordinate system is the cursor's coordinate system, and
+ // has an upper-left origin (in both Cocoa and pre-Cocoa systems).
+ nsPoint mHotSpot;
+ uint8_t* mCustomImageData;
+ uint32_t mCustomImageDataLength;
+ static int32_t mNativeCursorsSupported;
+};
+
+namespace parent {
+
+void OnPluginShowWindow(uint32_t window_id, CGRect window_bounds, bool modal);
+void OnPluginHideWindow(uint32_t window_id, pid_t aPluginPid);
+void OnSetCursor(const NSCursorInfo& cursorInfo);
+void OnShowCursor(bool show);
+void OnPushCursor(const NSCursorInfo& cursorInfo);
+void OnPopCursor();
+
+} // namespace parent
+
+namespace child {
+
+void SetUpCocoaInterposing();
+
+} // namespace child
+
+} // namespace mac_plugin_interposing
+
+#endif /* DOM_PLUGINS_IPC_PLUGININTERPOSEOSX_H */
diff --git a/dom/plugins/ipc/PluginInterposeOSX.mm b/dom/plugins/ipc/PluginInterposeOSX.mm
new file mode 100644
index 0000000000..bf24d2b0d8
--- /dev/null
+++ b/dom/plugins/ipc/PluginInterposeOSX.mm
@@ -0,0 +1,1158 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:set ts=2 sts=2 sw=2 et cin:
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 THE COPYRIGHT
+// OWNER 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.
+
+#include "base/basictypes.h"
+#include "nsCocoaUtils.h"
+#include "PluginModuleChild.h"
+#include "nsDebug.h"
+#include "PluginInterposeOSX.h"
+#include <set>
+#import <AppKit/AppKit.h>
+#import <objc/runtime.h>
+#import <Carbon/Carbon.h>
+
+using namespace mozilla::plugins;
+
+namespace mac_plugin_interposing {
+
+int32_t NSCursorInfo::mNativeCursorsSupported = -1;
+
+// This constructor may be called from the browser process or the plugin
+// process.
+NSCursorInfo::NSCursorInfo()
+ : mType(TypeArrow)
+ , mHotSpot(nsPoint(0, 0))
+ , mCustomImageData(NULL)
+ , mCustomImageDataLength(0)
+{
+}
+
+NSCursorInfo::NSCursorInfo(NSCursor* aCursor)
+ : mType(TypeArrow)
+ , mHotSpot(nsPoint(0, 0))
+ , mCustomImageData(NULL)
+ , mCustomImageDataLength(0)
+{
+ // This constructor is only ever called from the plugin process, so the
+ // following is safe.
+ if (!GetNativeCursorsSupported()) {
+ return;
+ }
+
+ NSPoint hotSpotCocoa = [aCursor hotSpot];
+ mHotSpot = nsPoint(hotSpotCocoa.x, hotSpotCocoa.y);
+
+ Class nsCursorClass = [NSCursor class];
+ if ([aCursor isEqual:[NSCursor arrowCursor]]) {
+ mType = TypeArrow;
+ } else if ([aCursor isEqual:[NSCursor closedHandCursor]]) {
+ mType = TypeClosedHand;
+ } else if ([aCursor isEqual:[NSCursor crosshairCursor]]) {
+ mType = TypeCrosshair;
+ } else if ([aCursor isEqual:[NSCursor disappearingItemCursor]]) {
+ mType = TypeDisappearingItem;
+ } else if ([aCursor isEqual:[NSCursor IBeamCursor]]) {
+ mType = TypeIBeam;
+ } else if ([aCursor isEqual:[NSCursor openHandCursor]]) {
+ mType = TypeOpenHand;
+ } else if ([aCursor isEqual:[NSCursor pointingHandCursor]]) {
+ mType = TypePointingHand;
+ } else if ([aCursor isEqual:[NSCursor resizeDownCursor]]) {
+ mType = TypeResizeDown;
+ } else if ([aCursor isEqual:[NSCursor resizeLeftCursor]]) {
+ mType = TypeResizeLeft;
+ } else if ([aCursor isEqual:[NSCursor resizeLeftRightCursor]]) {
+ mType = TypeResizeLeftRight;
+ } else if ([aCursor isEqual:[NSCursor resizeRightCursor]]) {
+ mType = TypeResizeRight;
+ } else if ([aCursor isEqual:[NSCursor resizeUpCursor]]) {
+ mType = TypeResizeUp;
+ } else if ([aCursor isEqual:[NSCursor resizeUpDownCursor]]) {
+ mType = TypeResizeUpDown;
+ // The following cursor types are only supported on OS X 10.6 and up.
+ } else if ([nsCursorClass respondsToSelector:@selector(contextualMenuCursor)] &&
+ [aCursor isEqual:[nsCursorClass performSelector:@selector(contextualMenuCursor)]]) {
+ mType = TypeContextualMenu;
+ } else if ([nsCursorClass respondsToSelector:@selector(dragCopyCursor)] &&
+ [aCursor isEqual:[nsCursorClass performSelector:@selector(dragCopyCursor)]]) {
+ mType = TypeDragCopy;
+ } else if ([nsCursorClass respondsToSelector:@selector(dragLinkCursor)] &&
+ [aCursor isEqual:[nsCursorClass performSelector:@selector(dragLinkCursor)]]) {
+ mType = TypeDragLink;
+ } else if ([nsCursorClass respondsToSelector:@selector(operationNotAllowedCursor)] &&
+ [aCursor isEqual:[nsCursorClass performSelector:@selector(operationNotAllowedCursor)]]) {
+ mType = TypeNotAllowed;
+ } else {
+ NSImage* image = [aCursor image];
+ NSArray* reps = image ? [image representations] : nil;
+ NSUInteger repsCount = reps ? [reps count] : 0;
+ if (!repsCount) {
+ // If we have a custom cursor with no image representations, assume we
+ // need a transparent cursor.
+ mType = TypeTransparent;
+ } else {
+ CGImageRef cgImage = nil;
+ // XXX We don't know how to deal with a cursor that doesn't have a
+ // bitmap image representation. For now we fall back to an arrow
+ // cursor.
+ for (NSUInteger i = 0; i < repsCount; ++i) {
+ id rep = [reps objectAtIndex:i];
+ if ([rep isKindOfClass:[NSBitmapImageRep class]]) {
+ cgImage = [(NSBitmapImageRep*)rep CGImage];
+ break;
+ }
+ }
+ if (cgImage) {
+ CFMutableDataRef data = ::CFDataCreateMutable(kCFAllocatorDefault, 0);
+ if (data) {
+ CGImageDestinationRef dest = ::CGImageDestinationCreateWithData(data,
+ kUTTypePNG,
+ 1,
+ NULL);
+ if (dest) {
+ ::CGImageDestinationAddImage(dest, cgImage, NULL);
+ if (::CGImageDestinationFinalize(dest)) {
+ uint32_t dataLength = (uint32_t) ::CFDataGetLength(data);
+ mCustomImageData = (uint8_t*) moz_xmalloc(dataLength);
+ ::CFDataGetBytes(data, ::CFRangeMake(0, dataLength), mCustomImageData);
+ mCustomImageDataLength = dataLength;
+ mType = TypeCustom;
+ }
+ ::CFRelease(dest);
+ }
+ ::CFRelease(data);
+ }
+ }
+ if (!mCustomImageData) {
+ mType = TypeArrow;
+ }
+ }
+ }
+}
+
+NSCursorInfo::NSCursorInfo(const Cursor* aCursor)
+ : mType(TypeArrow)
+ , mHotSpot(nsPoint(0, 0))
+ , mCustomImageData(NULL)
+ , mCustomImageDataLength(0)
+{
+ // This constructor is only ever called from the plugin process, so the
+ // following is safe.
+ if (!GetNativeCursorsSupported()) {
+ return;
+ }
+
+ mHotSpot = nsPoint(aCursor->hotSpot.h, aCursor->hotSpot.v);
+
+ int width = 16, height = 16;
+ int bytesPerPixel = 4;
+ int rowBytes = width * bytesPerPixel;
+ int bitmapSize = height * rowBytes;
+
+ bool isTransparent = true;
+
+ uint8_t* bitmap = (uint8_t*) moz_xmalloc(bitmapSize);
+ // The way we create 'bitmap' is largely "borrowed" from Chrome's
+ // WebCursor::InitFromCursor().
+ for (int y = 0; y < height; ++y) {
+ unsigned short data = aCursor->data[y];
+ unsigned short mask = aCursor->mask[y];
+ // Change 'data' and 'mask' from big-endian to little-endian, but output
+ // big-endian data below.
+ data = ((data << 8) & 0xFF00) | ((data >> 8) & 0x00FF);
+ mask = ((mask << 8) & 0xFF00) | ((mask >> 8) & 0x00FF);
+ // It'd be nice to use a gray-scale bitmap. But
+ // CGBitmapContextCreateImage() (used below) won't work with one that also
+ // has alpha values.
+ for (int x = 0; x < width; ++x) {
+ int offset = (y * rowBytes) + (x * bytesPerPixel);
+ // Color value
+ if (data & 0x8000) {
+ bitmap[offset] = 0x0;
+ bitmap[offset + 1] = 0x0;
+ bitmap[offset + 2] = 0x0;
+ } else {
+ bitmap[offset] = 0xFF;
+ bitmap[offset + 1] = 0xFF;
+ bitmap[offset + 2] = 0xFF;
+ }
+ // Mask value
+ if (mask & 0x8000) {
+ bitmap[offset + 3] = 0xFF;
+ isTransparent = false;
+ } else {
+ bitmap[offset + 3] = 0x0;
+ }
+ data <<= 1;
+ mask <<= 1;
+ }
+ }
+
+ if (isTransparent) {
+ // If aCursor is transparent, we don't need to serialize custom cursor
+ // data over IPC.
+ mType = TypeTransparent;
+ } else {
+ CGColorSpaceRef color = ::CGColorSpaceCreateDeviceRGB();
+ if (color) {
+ CGContextRef context =
+ ::CGBitmapContextCreate(bitmap,
+ width,
+ height,
+ 8,
+ rowBytes,
+ color,
+ kCGImageAlphaPremultipliedLast |
+ kCGBitmapByteOrder32Big);
+ if (context) {
+ CGImageRef image = ::CGBitmapContextCreateImage(context);
+ if (image) {
+ ::CFMutableDataRef data = ::CFDataCreateMutable(kCFAllocatorDefault, 0);
+ if (data) {
+ CGImageDestinationRef dest =
+ ::CGImageDestinationCreateWithData(data,
+ kUTTypePNG,
+ 1,
+ NULL);
+ if (dest) {
+ ::CGImageDestinationAddImage(dest, image, NULL);
+ if (::CGImageDestinationFinalize(dest)) {
+ uint32_t dataLength = (uint32_t) ::CFDataGetLength(data);
+ mCustomImageData = (uint8_t*) moz_xmalloc(dataLength);
+ ::CFDataGetBytes(data,
+ ::CFRangeMake(0, dataLength),
+ mCustomImageData);
+ mCustomImageDataLength = dataLength;
+ mType = TypeCustom;
+ }
+ ::CFRelease(dest);
+ }
+ ::CFRelease(data);
+ }
+ ::CGImageRelease(image);
+ }
+ ::CGContextRelease(context);
+ }
+ ::CGColorSpaceRelease(color);
+ }
+ }
+
+ free(bitmap);
+}
+
+NSCursorInfo::~NSCursorInfo()
+{
+ if (mCustomImageData) {
+ free(mCustomImageData);
+ }
+}
+
+NSCursor* NSCursorInfo::GetNSCursor() const
+{
+ NSCursor* retval = nil;
+
+ Class nsCursorClass = [NSCursor class];
+ switch(mType) {
+ case TypeArrow:
+ retval = [NSCursor arrowCursor];
+ break;
+ case TypeClosedHand:
+ retval = [NSCursor closedHandCursor];
+ break;
+ case TypeCrosshair:
+ retval = [NSCursor crosshairCursor];
+ break;
+ case TypeDisappearingItem:
+ retval = [NSCursor disappearingItemCursor];
+ break;
+ case TypeIBeam:
+ retval = [NSCursor IBeamCursor];
+ break;
+ case TypeOpenHand:
+ retval = [NSCursor openHandCursor];
+ break;
+ case TypePointingHand:
+ retval = [NSCursor pointingHandCursor];
+ break;
+ case TypeResizeDown:
+ retval = [NSCursor resizeDownCursor];
+ break;
+ case TypeResizeLeft:
+ retval = [NSCursor resizeLeftCursor];
+ break;
+ case TypeResizeLeftRight:
+ retval = [NSCursor resizeLeftRightCursor];
+ break;
+ case TypeResizeRight:
+ retval = [NSCursor resizeRightCursor];
+ break;
+ case TypeResizeUp:
+ retval = [NSCursor resizeUpCursor];
+ break;
+ case TypeResizeUpDown:
+ retval = [NSCursor resizeUpDownCursor];
+ break;
+ // The following four cursor types are only supported on OS X 10.6 and up.
+ case TypeContextualMenu: {
+ if ([nsCursorClass respondsToSelector:@selector(contextualMenuCursor)]) {
+ retval = [nsCursorClass performSelector:@selector(contextualMenuCursor)];
+ }
+ break;
+ }
+ case TypeDragCopy: {
+ if ([nsCursorClass respondsToSelector:@selector(dragCopyCursor)]) {
+ retval = [nsCursorClass performSelector:@selector(dragCopyCursor)];
+ }
+ break;
+ }
+ case TypeDragLink: {
+ if ([nsCursorClass respondsToSelector:@selector(dragLinkCursor)]) {
+ retval = [nsCursorClass performSelector:@selector(dragLinkCursor)];
+ }
+ break;
+ }
+ case TypeNotAllowed: {
+ if ([nsCursorClass respondsToSelector:@selector(operationNotAllowedCursor)]) {
+ retval = [nsCursorClass performSelector:@selector(operationNotAllowedCursor)];
+ }
+ break;
+ }
+ case TypeTransparent:
+ retval = GetTransparentCursor();
+ break;
+ default:
+ break;
+ }
+
+ if (!retval && mCustomImageData && mCustomImageDataLength) {
+ CGDataProviderRef provider = ::CGDataProviderCreateWithData(NULL,
+ (const void*)mCustomImageData,
+ mCustomImageDataLength,
+ NULL);
+ if (provider) {
+ CGImageRef cgImage = ::CGImageCreateWithPNGDataProvider(provider,
+ NULL,
+ false,
+ kCGRenderingIntentDefault);
+ if (cgImage) {
+ NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage];
+ if (rep) {
+ NSImage* image = [[NSImage alloc] init];
+ if (image) {
+ [image addRepresentation:rep];
+ retval = [[[NSCursor alloc] initWithImage:image
+ hotSpot:NSMakePoint(mHotSpot.x, mHotSpot.y)]
+ autorelease];
+ [image release];
+ }
+ [rep release];
+ }
+ ::CGImageRelease(cgImage);
+ }
+ ::CFRelease(provider);
+ }
+ }
+
+ // Fall back to an arrow cursor if need be.
+ if (!retval) {
+ retval = [NSCursor arrowCursor];
+ }
+
+ return retval;
+}
+
+// Get a transparent cursor with the appropriate hot spot. We need one if
+// (for example) we have a custom cursor with no image data.
+NSCursor* NSCursorInfo::GetTransparentCursor() const
+{
+ NSCursor* retval = nil;
+
+ int width = 16, height = 16;
+ int bytesPerPixel = 2;
+ int rowBytes = width * bytesPerPixel;
+ int dataSize = height * rowBytes;
+
+ uint8_t* data = (uint8_t*) moz_xmalloc(dataSize);
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ int offset = (y * rowBytes) + (x * bytesPerPixel);
+ data[offset] = 0x7E; // Arbitrary gray-scale value
+ data[offset + 1] = 0; // Alpha value to make us transparent
+ }
+ }
+
+ NSBitmapImageRep* imageRep =
+ [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
+ pixelsWide:width
+ pixelsHigh:height
+ bitsPerSample:8
+ samplesPerPixel:2
+ hasAlpha:YES
+ isPlanar:NO
+ colorSpaceName:NSCalibratedWhiteColorSpace
+ bytesPerRow:rowBytes
+ bitsPerPixel:16]
+ autorelease];
+ if (imageRep) {
+ uint8_t* repDataPtr = [imageRep bitmapData];
+ if (repDataPtr) {
+ memcpy(repDataPtr, data, dataSize);
+ NSImage *image =
+ [[[NSImage alloc] initWithSize:NSMakeSize(width, height)]
+ autorelease];
+ if (image) {
+ [image addRepresentation:imageRep];
+ retval =
+ [[[NSCursor alloc] initWithImage:image
+ hotSpot:NSMakePoint(mHotSpot.x, mHotSpot.y)]
+ autorelease];
+ }
+ }
+ }
+
+ free(data);
+
+ // Fall back to an arrow cursor if (for some reason) the above code failed.
+ if (!retval) {
+ retval = [NSCursor arrowCursor];
+ }
+
+ return retval;
+}
+
+NSCursorInfo::Type NSCursorInfo::GetType() const
+{
+ return mType;
+}
+
+const char* NSCursorInfo::GetTypeName() const
+{
+ switch(mType) {
+ case TypeCustom:
+ return "TypeCustom";
+ case TypeArrow:
+ return "TypeArrow";
+ case TypeClosedHand:
+ return "TypeClosedHand";
+ case TypeContextualMenu:
+ return "TypeContextualMenu";
+ case TypeCrosshair:
+ return "TypeCrosshair";
+ case TypeDisappearingItem:
+ return "TypeDisappearingItem";
+ case TypeDragCopy:
+ return "TypeDragCopy";
+ case TypeDragLink:
+ return "TypeDragLink";
+ case TypeIBeam:
+ return "TypeIBeam";
+ case TypeNotAllowed:
+ return "TypeNotAllowed";
+ case TypeOpenHand:
+ return "TypeOpenHand";
+ case TypePointingHand:
+ return "TypePointingHand";
+ case TypeResizeDown:
+ return "TypeResizeDown";
+ case TypeResizeLeft:
+ return "TypeResizeLeft";
+ case TypeResizeLeftRight:
+ return "TypeResizeLeftRight";
+ case TypeResizeRight:
+ return "TypeResizeRight";
+ case TypeResizeUp:
+ return "TypeResizeUp";
+ case TypeResizeUpDown:
+ return "TypeResizeUpDown";
+ case TypeTransparent:
+ return "TypeTransparent";
+ default:
+ break;
+ }
+ return "TypeUnknown";
+}
+
+nsPoint NSCursorInfo::GetHotSpot() const
+{
+ return mHotSpot;
+}
+
+uint8_t* NSCursorInfo::GetCustomImageData() const
+{
+ return mCustomImageData;
+}
+
+uint32_t NSCursorInfo::GetCustomImageDataLength() const
+{
+ return mCustomImageDataLength;
+}
+
+void NSCursorInfo::SetType(Type aType)
+{
+ mType = aType;
+}
+
+void NSCursorInfo::SetHotSpot(nsPoint aHotSpot)
+{
+ mHotSpot = aHotSpot;
+}
+
+void NSCursorInfo::SetCustomImageData(uint8_t* aData, uint32_t aDataLength)
+{
+ if (mCustomImageData) {
+ free(mCustomImageData);
+ }
+ if (aDataLength) {
+ mCustomImageData = (uint8_t*) moz_xmalloc(aDataLength);
+ memcpy(mCustomImageData, aData, aDataLength);
+ } else {
+ mCustomImageData = NULL;
+ }
+ mCustomImageDataLength = aDataLength;
+}
+
+// This should never be called from the browser process -- only from the
+// plugin process.
+bool NSCursorInfo::GetNativeCursorsSupported()
+{
+ if (mNativeCursorsSupported == -1) {
+ ENSURE_PLUGIN_THREAD(false);
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
+ if (pmc) {
+ bool result = pmc->GetNativeCursorsSupported();
+ if (result) {
+ mNativeCursorsSupported = 1;
+ } else {
+ mNativeCursorsSupported = 0;
+ }
+ }
+ }
+ return (mNativeCursorsSupported == 1);
+}
+
+} // namespace mac_plugin_interposing
+
+namespace mac_plugin_interposing {
+namespace parent {
+
+// Tracks plugin windows currently visible.
+std::set<uint32_t> plugin_visible_windows_set_;
+// Tracks full screen windows currently visible.
+std::set<uint32_t> plugin_fullscreen_windows_set_;
+// Tracks modal windows currently visible.
+std::set<uint32_t> plugin_modal_windows_set_;
+
+void OnPluginShowWindow(uint32_t window_id,
+ CGRect window_bounds,
+ bool modal) {
+ plugin_visible_windows_set_.insert(window_id);
+
+ if (modal)
+ plugin_modal_windows_set_.insert(window_id);
+
+ CGRect main_display_bounds = ::CGDisplayBounds(CGMainDisplayID());
+
+ if (CGRectEqualToRect(window_bounds, main_display_bounds) &&
+ (plugin_fullscreen_windows_set_.find(window_id) ==
+ plugin_fullscreen_windows_set_.end())) {
+ plugin_fullscreen_windows_set_.insert(window_id);
+
+ nsCocoaUtils::HideOSChromeOnScreen(true);
+ }
+}
+
+static void ActivateProcess(pid_t pid) {
+ ProcessSerialNumber process;
+ OSStatus status = ::GetProcessForPID(pid, &process);
+
+ if (status == noErr) {
+ SetFrontProcess(&process);
+ } else {
+ NS_WARNING("Unable to get process for pid.");
+ }
+}
+
+// Must be called on the UI thread.
+// If plugin_pid is -1, the browser will be the active process on return,
+// otherwise that process will be given focus back before this function returns.
+static void ReleasePluginFullScreen(pid_t plugin_pid) {
+ // Releasing full screen only works if we are the frontmost process; grab
+ // focus, but give it back to the plugin process if requested.
+ ActivateProcess(base::GetCurrentProcId());
+
+ nsCocoaUtils::HideOSChromeOnScreen(false);
+
+ if (plugin_pid != -1) {
+ ActivateProcess(plugin_pid);
+ }
+}
+
+void OnPluginHideWindow(uint32_t window_id, pid_t aPluginPid) {
+ bool had_windows = !plugin_visible_windows_set_.empty();
+ plugin_visible_windows_set_.erase(window_id);
+ bool browser_needs_activation = had_windows &&
+ plugin_visible_windows_set_.empty();
+
+ plugin_modal_windows_set_.erase(window_id);
+ if (plugin_fullscreen_windows_set_.find(window_id) !=
+ plugin_fullscreen_windows_set_.end()) {
+ plugin_fullscreen_windows_set_.erase(window_id);
+ pid_t plugin_pid = browser_needs_activation ? -1 : aPluginPid;
+ browser_needs_activation = false;
+ ReleasePluginFullScreen(plugin_pid);
+ }
+
+ if (browser_needs_activation) {
+ ActivateProcess(getpid());
+ }
+}
+
+void OnSetCursor(const NSCursorInfo& cursorInfo)
+{
+ NSCursor* aCursor = cursorInfo.GetNSCursor();
+ if (aCursor) {
+ [aCursor set];
+ }
+}
+
+void OnShowCursor(bool show)
+{
+ if (show) {
+ [NSCursor unhide];
+ } else {
+ [NSCursor hide];
+ }
+}
+
+void OnPushCursor(const NSCursorInfo& cursorInfo)
+{
+ NSCursor* aCursor = cursorInfo.GetNSCursor();
+ if (aCursor) {
+ [aCursor push];
+ }
+}
+
+void OnPopCursor()
+{
+ [NSCursor pop];
+}
+
+} // namespace parent
+} // namespace mac_plugin_interposing
+
+namespace mac_plugin_interposing {
+namespace child {
+
+// TODO(stuartmorgan): Make this an IPC to order the plugin process above the
+// browser process only if the browser is current frontmost.
+void FocusPluginProcess() {
+ ProcessSerialNumber this_process, front_process;
+ if ((GetCurrentProcess(&this_process) != noErr) ||
+ (GetFrontProcess(&front_process) != noErr)) {
+ return;
+ }
+
+ Boolean matched = false;
+ if ((SameProcess(&this_process, &front_process, &matched) == noErr) &&
+ !matched) {
+ SetFrontProcess(&this_process);
+ }
+}
+
+void NotifyBrowserOfPluginShowWindow(uint32_t window_id, CGRect bounds,
+ bool modal) {
+ ENSURE_PLUGIN_THREAD_VOID();
+
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
+ if (pmc)
+ pmc->PluginShowWindow(window_id, modal, bounds);
+}
+
+void NotifyBrowserOfPluginHideWindow(uint32_t window_id, CGRect bounds) {
+ ENSURE_PLUGIN_THREAD_VOID();
+
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
+ if (pmc)
+ pmc->PluginHideWindow(window_id);
+}
+
+void NotifyBrowserOfSetCursor(NSCursorInfo& aCursorInfo)
+{
+ ENSURE_PLUGIN_THREAD_VOID();
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
+ if (pmc) {
+ pmc->SetCursor(aCursorInfo);
+ }
+}
+
+void NotifyBrowserOfShowCursor(bool show)
+{
+ ENSURE_PLUGIN_THREAD_VOID();
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
+ if (pmc) {
+ pmc->ShowCursor(show);
+ }
+}
+
+void NotifyBrowserOfPushCursor(NSCursorInfo& aCursorInfo)
+{
+ ENSURE_PLUGIN_THREAD_VOID();
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
+ if (pmc) {
+ pmc->PushCursor(aCursorInfo);
+ }
+}
+
+void NotifyBrowserOfPopCursor()
+{
+ ENSURE_PLUGIN_THREAD_VOID();
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
+ if (pmc) {
+ pmc->PopCursor();
+ }
+}
+
+struct WindowInfo {
+ uint32_t window_id;
+ CGRect bounds;
+ explicit WindowInfo(NSWindow* aWindow) {
+ NSInteger window_num = [aWindow windowNumber];
+ window_id = window_num > 0 ? window_num : 0;
+ bounds = NSRectToCGRect([aWindow frame]);
+ }
+};
+
+static void OnPluginWindowClosed(const WindowInfo& window_info) {
+ if (window_info.window_id == 0)
+ return;
+ mac_plugin_interposing::child::NotifyBrowserOfPluginHideWindow(window_info.window_id,
+ window_info.bounds);
+}
+
+static void OnPluginWindowShown(const WindowInfo& window_info, BOOL is_modal) {
+ // The window id is 0 if it has never been shown (including while it is the
+ // process of being shown for the first time); when that happens, we'll catch
+ // it in _setWindowNumber instead.
+ static BOOL s_pending_display_is_modal = NO;
+ if (window_info.window_id == 0) {
+ if (is_modal)
+ s_pending_display_is_modal = YES;
+ return;
+ }
+ if (s_pending_display_is_modal) {
+ is_modal = YES;
+ s_pending_display_is_modal = NO;
+ }
+ mac_plugin_interposing::child::NotifyBrowserOfPluginShowWindow(
+ window_info.window_id, window_info.bounds, is_modal);
+}
+
+static BOOL OnSetCursor(NSCursorInfo &aInfo)
+{
+ if (NSCursorInfo::GetNativeCursorsSupported()) {
+ NotifyBrowserOfSetCursor(aInfo);
+ return YES;
+ }
+ return NO;
+}
+
+static BOOL OnHideCursor()
+{
+ if (NSCursorInfo::GetNativeCursorsSupported()) {
+ NotifyBrowserOfShowCursor(NO);
+ return YES;
+ }
+ return NO;
+}
+
+static BOOL OnUnhideCursor()
+{
+ if (NSCursorInfo::GetNativeCursorsSupported()) {
+ NotifyBrowserOfShowCursor(YES);
+ return YES;
+ }
+ return NO;
+}
+
+static BOOL OnPushCursor(NSCursorInfo &aInfo)
+{
+ if (NSCursorInfo::GetNativeCursorsSupported()) {
+ NotifyBrowserOfPushCursor(aInfo);
+ return YES;
+ }
+ return NO;
+}
+
+static BOOL OnPopCursor()
+{
+ if (NSCursorInfo::GetNativeCursorsSupported()) {
+ NotifyBrowserOfPopCursor();
+ return YES;
+ }
+ return NO;
+}
+
+} // namespace child
+} // namespace mac_plugin_interposing
+
+using namespace mac_plugin_interposing::child;
+
+@interface NSWindow (PluginInterposing)
+- (void)pluginInterpose_orderOut:(id)sender;
+- (void)pluginInterpose_orderFront:(id)sender;
+- (void)pluginInterpose_makeKeyAndOrderFront:(id)sender;
+- (void)pluginInterpose_setWindowNumber:(NSInteger)num;
+@end
+
+@implementation NSWindow (PluginInterposing)
+
+- (void)pluginInterpose_orderOut:(id)sender {
+ WindowInfo window_info(self);
+ [self pluginInterpose_orderOut:sender];
+ OnPluginWindowClosed(window_info);
+}
+
+- (void)pluginInterpose_orderFront:(id)sender {
+ mac_plugin_interposing::child::FocusPluginProcess();
+ [self pluginInterpose_orderFront:sender];
+ OnPluginWindowShown(WindowInfo(self), NO);
+}
+
+- (void)pluginInterpose_makeKeyAndOrderFront:(id)sender {
+ mac_plugin_interposing::child::FocusPluginProcess();
+ [self pluginInterpose_makeKeyAndOrderFront:sender];
+ OnPluginWindowShown(WindowInfo(self), NO);
+}
+
+- (void)pluginInterpose_setWindowNumber:(NSInteger)num {
+ if (num > 0)
+ mac_plugin_interposing::child::FocusPluginProcess();
+ [self pluginInterpose_setWindowNumber:num];
+ if (num > 0)
+ OnPluginWindowShown(WindowInfo(self), NO);
+}
+
+@end
+
+@interface NSApplication (PluginInterposing)
+- (NSInteger)pluginInterpose_runModalForWindow:(NSWindow*)window;
+@end
+
+@implementation NSApplication (PluginInterposing)
+
+- (NSInteger)pluginInterpose_runModalForWindow:(NSWindow*)window {
+ mac_plugin_interposing::child::FocusPluginProcess();
+ // This is out-of-order relative to the other calls, but runModalForWindow:
+ // won't return until the window closes, and the order only matters for
+ // full-screen windows.
+ OnPluginWindowShown(WindowInfo(window), YES);
+ return [self pluginInterpose_runModalForWindow:window];
+}
+
+@end
+
+// Hook commands to manipulate the current cursor, so that they can be passed
+// from the child process to the parent process. These commands have no
+// effect unless they're performed in the parent process.
+@interface NSCursor (PluginInterposing)
+- (void)pluginInterpose_set;
+- (void)pluginInterpose_push;
+- (void)pluginInterpose_pop;
++ (NSCursor*)pluginInterpose_currentCursor;
++ (void)pluginInterpose_hide;
++ (void)pluginInterpose_unhide;
++ (void)pluginInterpose_pop;
+@end
+
+// Cache the results of [NSCursor set], [NSCursor push] and [NSCursor pop].
+// The last element is always the current cursor.
+static NSMutableArray* gCursorStack = nil;
+
+static BOOL initCursorStack()
+{
+ if (!gCursorStack) {
+ gCursorStack = [[NSMutableArray arrayWithCapacity:5] retain];
+ }
+ return (gCursorStack != NULL);
+}
+
+static NSCursor* currentCursorFromCache()
+{
+ if (!initCursorStack())
+ return nil;
+ return (NSCursor*) [gCursorStack lastObject];
+}
+
+static void setCursorInCache(NSCursor* aCursor)
+{
+ if (!initCursorStack() || !aCursor)
+ return;
+ NSUInteger count = [gCursorStack count];
+ if (count) {
+ [gCursorStack replaceObjectAtIndex:count - 1 withObject:aCursor];
+ } else {
+ [gCursorStack addObject:aCursor];
+ }
+}
+
+static void pushCursorInCache(NSCursor* aCursor)
+{
+ if (!initCursorStack() || !aCursor)
+ return;
+ [gCursorStack addObject:aCursor];
+}
+
+static void popCursorInCache()
+{
+ if (!initCursorStack())
+ return;
+ // Apple's doc on the +[NSCursor pop] method says: "If the current cursor
+ // is the only cursor on the stack, this method does nothing."
+ if ([gCursorStack count] > 1) {
+ [gCursorStack removeLastObject];
+ }
+}
+
+@implementation NSCursor (PluginInterposing)
+
+- (void)pluginInterpose_set
+{
+ NSCursorInfo info(self);
+ OnSetCursor(info);
+ setCursorInCache(self);
+ [self pluginInterpose_set];
+}
+
+- (void)pluginInterpose_push
+{
+ NSCursorInfo info(self);
+ OnPushCursor(info);
+ pushCursorInCache(self);
+ [self pluginInterpose_push];
+}
+
+- (void)pluginInterpose_pop
+{
+ OnPopCursor();
+ popCursorInCache();
+ [self pluginInterpose_pop];
+}
+
+// The currentCursor method always returns nil when running in a background
+// process. But this may confuse plugins (notably Flash, see bug 621117). So
+// if we get a nil return from the "call to super", we return a cursor that's
+// been cached by previous calls to set or push. According to Apple's docs,
+// currentCursor "only returns the cursor set by your application using
+// NSCursor methods". So we don't need to worry about changes to the cursor
+// made by other methods like SetThemeCursor().
++ (NSCursor*)pluginInterpose_currentCursor
+{
+ NSCursor* retval = [self pluginInterpose_currentCursor];
+ if (!retval) {
+ retval = currentCursorFromCache();
+ }
+ return retval;
+}
+
++ (void)pluginInterpose_hide
+{
+ OnHideCursor();
+ [self pluginInterpose_hide];
+}
+
++ (void)pluginInterpose_unhide
+{
+ OnUnhideCursor();
+ [self pluginInterpose_unhide];
+}
+
++ (void)pluginInterpose_pop
+{
+ OnPopCursor();
+ popCursorInCache();
+ [self pluginInterpose_pop];
+}
+
+@end
+
+static void ExchangeMethods(Class target_class,
+ BOOL class_method,
+ SEL original,
+ SEL replacement) {
+ Method m1;
+ Method m2;
+ if (class_method) {
+ m1 = class_getClassMethod(target_class, original);
+ m2 = class_getClassMethod(target_class, replacement);
+ } else {
+ m1 = class_getInstanceMethod(target_class, original);
+ m2 = class_getInstanceMethod(target_class, replacement);
+ }
+
+ if (m1 == m2)
+ return;
+
+ if (m1 && m2)
+ method_exchangeImplementations(m1, m2);
+ else
+ NS_NOTREACHED("Cocoa swizzling failed");
+}
+
+namespace mac_plugin_interposing {
+namespace child {
+
+void SetUpCocoaInterposing() {
+ Class nswindow_class = [NSWindow class];
+ ExchangeMethods(nswindow_class, NO, @selector(orderOut:),
+ @selector(pluginInterpose_orderOut:));
+ ExchangeMethods(nswindow_class, NO, @selector(orderFront:),
+ @selector(pluginInterpose_orderFront:));
+ ExchangeMethods(nswindow_class, NO, @selector(makeKeyAndOrderFront:),
+ @selector(pluginInterpose_makeKeyAndOrderFront:));
+ ExchangeMethods(nswindow_class, NO, @selector(_setWindowNumber:),
+ @selector(pluginInterpose_setWindowNumber:));
+
+ ExchangeMethods([NSApplication class], NO, @selector(runModalForWindow:),
+ @selector(pluginInterpose_runModalForWindow:));
+
+ Class nscursor_class = [NSCursor class];
+ ExchangeMethods(nscursor_class, NO, @selector(set),
+ @selector(pluginInterpose_set));
+ ExchangeMethods(nscursor_class, NO, @selector(push),
+ @selector(pluginInterpose_push));
+ ExchangeMethods(nscursor_class, NO, @selector(pop),
+ @selector(pluginInterpose_pop));
+ ExchangeMethods(nscursor_class, YES, @selector(currentCursor),
+ @selector(pluginInterpose_currentCursor));
+ ExchangeMethods(nscursor_class, YES, @selector(hide),
+ @selector(pluginInterpose_hide));
+ ExchangeMethods(nscursor_class, YES, @selector(unhide),
+ @selector(pluginInterpose_unhide));
+ ExchangeMethods(nscursor_class, YES, @selector(pop),
+ @selector(pluginInterpose_pop));
+}
+
+} // namespace child
+} // namespace mac_plugin_interposing
+
+// Called from plugin_child_interpose.mm, which hooks calls to
+// SetCursor() (the QuickDraw call) from the plugin child process.
+extern "C" NS_VISIBILITY_DEFAULT BOOL
+mac_plugin_interposing_child_OnSetCursor(const Cursor* cursor)
+{
+ NSCursorInfo info(cursor);
+ return OnSetCursor(info);
+}
+
+// Called from plugin_child_interpose.mm, which hooks calls to
+// SetThemeCursor() (the Appearance Manager call) from the plugin child
+// process.
+extern "C" NS_VISIBILITY_DEFAULT BOOL
+mac_plugin_interposing_child_OnSetThemeCursor(ThemeCursor cursor)
+{
+ NSCursorInfo info;
+ switch (cursor) {
+ case kThemeArrowCursor:
+ info.SetType(NSCursorInfo::TypeArrow);
+ break;
+ case kThemeCopyArrowCursor:
+ info.SetType(NSCursorInfo::TypeDragCopy);
+ break;
+ case kThemeAliasArrowCursor:
+ info.SetType(NSCursorInfo::TypeDragLink);
+ break;
+ case kThemeContextualMenuArrowCursor:
+ info.SetType(NSCursorInfo::TypeContextualMenu);
+ break;
+ case kThemeIBeamCursor:
+ info.SetType(NSCursorInfo::TypeIBeam);
+ break;
+ case kThemeCrossCursor:
+ case kThemePlusCursor:
+ info.SetType(NSCursorInfo::TypeCrosshair);
+ break;
+ case kThemeWatchCursor:
+ case kThemeSpinningCursor:
+ info.SetType(NSCursorInfo::TypeArrow);
+ break;
+ case kThemeClosedHandCursor:
+ info.SetType(NSCursorInfo::TypeClosedHand);
+ break;
+ case kThemeOpenHandCursor:
+ info.SetType(NSCursorInfo::TypeOpenHand);
+ break;
+ case kThemePointingHandCursor:
+ case kThemeCountingUpHandCursor:
+ case kThemeCountingDownHandCursor:
+ case kThemeCountingUpAndDownHandCursor:
+ info.SetType(NSCursorInfo::TypePointingHand);
+ break;
+ case kThemeResizeLeftCursor:
+ info.SetType(NSCursorInfo::TypeResizeLeft);
+ break;
+ case kThemeResizeRightCursor:
+ info.SetType(NSCursorInfo::TypeResizeRight);
+ break;
+ case kThemeResizeLeftRightCursor:
+ info.SetType(NSCursorInfo::TypeResizeLeftRight);
+ break;
+ case kThemeNotAllowedCursor:
+ info.SetType(NSCursorInfo::TypeNotAllowed);
+ break;
+ case kThemeResizeUpCursor:
+ info.SetType(NSCursorInfo::TypeResizeUp);
+ break;
+ case kThemeResizeDownCursor:
+ info.SetType(NSCursorInfo::TypeResizeDown);
+ break;
+ case kThemeResizeUpDownCursor:
+ info.SetType(NSCursorInfo::TypeResizeUpDown);
+ break;
+ case kThemePoofCursor:
+ info.SetType(NSCursorInfo::TypeDisappearingItem);
+ break;
+ default:
+ info.SetType(NSCursorInfo::TypeArrow);
+ break;
+ }
+ return OnSetCursor(info);
+}
+
+extern "C" NS_VISIBILITY_DEFAULT BOOL
+mac_plugin_interposing_child_OnHideCursor()
+{
+ return OnHideCursor();
+}
+
+extern "C" NS_VISIBILITY_DEFAULT BOOL
+mac_plugin_interposing_child_OnShowCursor()
+{
+ return OnUnhideCursor();
+}
diff --git a/dom/plugins/ipc/PluginLibrary.h b/dom/plugins/ipc/PluginLibrary.h
index 50182d0b0d..2f9a3f81b2 100644
--- a/dom/plugins/ipc/PluginLibrary.h
+++ b/dom/plugins/ipc/PluginLibrary.h
@@ -57,7 +57,7 @@ public:
virtual bool HasRequiredFunctions() = 0;
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) = 0;
#else
virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) = 0;
@@ -66,7 +66,7 @@ public:
virtual nsresult NP_GetMIMEDescription(const char** mimeDesc) = 0;
virtual nsresult NP_GetValue(void *future, NPPVariable aVariable,
void *aValue, NPError* error) = 0;
-#if defined(XP_WIN)
+#if defined(XP_WIN) || defined(XP_MACOSX)
virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) = 0;
#endif
virtual nsresult NPP_New(NPMIMEType pluginType, NPP instance,
@@ -83,7 +83,10 @@ public:
virtual nsresult GetImageSize(NPP instance, nsIntSize* aSize) = 0;
virtual void DidComposite(NPP instance) = 0;
virtual bool IsOOP() = 0;
-#if defined(XP_WIN)
+#if defined(XP_MACOSX)
+ virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing) = 0;
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor) = 0;
#endif
#if defined(XP_WIN)
diff --git a/dom/plugins/ipc/PluginMessageUtils.cpp b/dom/plugins/ipc/PluginMessageUtils.cpp
index 9b27c76d5a..be1b2d15a5 100644
--- a/dom/plugins/ipc/PluginMessageUtils.cpp
+++ b/dom/plugins/ipc/PluginMessageUtils.cpp
@@ -49,10 +49,13 @@ namespace plugins {
NPRemoteWindow::NPRemoteWindow() :
window(0), x(0), y(0), width(0), height(0), type(NPWindowTypeDrawable)
-#if defined(MOZ_X11) && defined(XP_UNIX)
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
, visualID(0)
, colormap(0)
#endif /* XP_UNIX */
+#if defined(XP_MACOSX)
+ ,contentsScaleFactor(1.0)
+#endif
{
clipRect.top = 0;
clipRect.left = 0;
diff --git a/dom/plugins/ipc/PluginMessageUtils.h b/dom/plugins/ipc/PluginMessageUtils.h
index e06c2cee24..a9cd52ae29 100644
--- a/dom/plugins/ipc/PluginMessageUtils.h
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=4 ts=4 et :
* 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/. */
@@ -22,7 +23,11 @@
#include "nsTArray.h"
#include "mozilla/Logging.h"
#include "nsHashKeys.h"
+#ifdef XP_MACOSX
+#include "PluginInterposeOSX.h"
+#else
namespace mac_plugin_interposing { class NSCursorInfo { }; }
+#endif
using mac_plugin_interposing::NSCursorInfo;
namespace mozilla {
@@ -82,30 +87,32 @@ struct NPRemoteWindow
uint32_t height;
NPRect clipRect;
NPWindowType type;
-#if defined(MOZ_X11) && defined(XP_UNIX)
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
VisualID visualID;
Colormap colormap;
#endif /* XP_UNIX */
-#if defined(XP_WIN)
+#if defined(XP_MACOSX) || defined(XP_WIN)
double contentsScaleFactor;
#endif
};
-// This struct is like NPAudioDeviceChangeDetails, only it uses a
-// std::wstring instead of a const wchar_t* for the defaultDevice.
-// This gives us the necessary memory-ownership semantics without
-// requiring C++ objects in npapi.h.
-struct NPAudioDeviceChangeDetailsIPC
-{
- int32_t flow;
- int32_t role;
- std::wstring defaultDevice;
+// This struct is like NPAudioDeviceChangeDetails, only it uses a
+// std::wstring instead of a const wchar_t* for the defaultDevice.
+// This gives us the necessary memory-ownership semantics without
+// requiring C++ objects in npapi.h.
+struct NPAudioDeviceChangeDetailsIPC
+{
+ int32_t flow;
+ int32_t role;
+ std::wstring defaultDevice;
};
#ifdef XP_WIN
typedef HWND NativeWindowHandle;
#elif defined(MOZ_X11)
typedef XID NativeWindowHandle;
+#elif defined(XP_DARWIN)
+typedef intptr_t NativeWindowHandle; // never actually used, will always be 0
#else
#error Need NativeWindowHandle for this platform
#endif
@@ -149,6 +156,11 @@ NPPVariableToString(NPPVariable aVar)
VARSTR(NPPVpluginWantsAllNetworkStreams);
+#ifdef XP_MACOSX
+ VARSTR(NPPVpluginDrawingModel);
+ VARSTR(NPPVpluginEventModel);
+#endif
+
#ifdef XP_WIN
VARSTR(NPPVpluginRequiresAudioDeviceChanges);
#endif
@@ -345,11 +357,11 @@ struct ParamTraits<mozilla::plugins::NPRemoteWindow>
WriteParam(aMsg, aParam.height);
WriteParam(aMsg, aParam.clipRect);
WriteParam(aMsg, aParam.type);
-#if defined(MOZ_X11) && defined(XP_UNIX)
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
aMsg->WriteULong(aParam.visualID);
aMsg->WriteULong(aParam.colormap);
#endif
-#if defined(XP_WIN)
+#if defined(XP_MACOSX) || defined(XP_WIN)
aMsg->WriteDouble(aParam.contentsScaleFactor);
#endif
}
@@ -370,7 +382,7 @@ struct ParamTraits<mozilla::plugins::NPRemoteWindow>
ReadParam(aMsg, aIter, &type)))
return false;
-#if defined(MOZ_X11) && defined(XP_UNIX)
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
unsigned long visualID;
unsigned long colormap;
if (!(aMsg->ReadULong(aIter, &visualID) &&
@@ -378,7 +390,7 @@ struct ParamTraits<mozilla::plugins::NPRemoteWindow>
return false;
#endif
-#if defined(XP_WIN)
+#if defined(XP_MACOSX) || defined(XP_WIN)
double contentsScaleFactor;
if (!aMsg->ReadDouble(aIter, &contentsScaleFactor))
return false;
@@ -391,11 +403,11 @@ struct ParamTraits<mozilla::plugins::NPRemoteWindow>
aResult->height = height;
aResult->clipRect = clipRect;
aResult->type = type;
-#if defined(MOZ_X11) && defined(XP_UNIX)
+#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
aResult->visualID = visualID;
aResult->colormap = colormap;
#endif
-#if defined(XP_WIN)
+#if defined(XP_MACOSX) || defined(XP_WIN)
aResult->contentsScaleFactor = contentsScaleFactor;
#endif
return true;
@@ -410,6 +422,161 @@ struct ParamTraits<mozilla::plugins::NPRemoteWindow>
}
};
+#ifdef XP_MACOSX
+template <>
+struct ParamTraits<NPNSString*>
+{
+ typedef NPNSString* paramType;
+
+ // Empty string writes a length of 0 and no buffer.
+ // We don't write a nullptr terminating character in buffers.
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ CFStringRef cfString = (CFStringRef)aParam;
+
+ // Write true if we have a string, false represents nullptr.
+ aMsg->WriteBool(!!cfString);
+ if (!cfString) {
+ return;
+ }
+
+ long length = ::CFStringGetLength(cfString);
+ WriteParam(aMsg, length);
+ if (length == 0) {
+ return;
+ }
+
+ // Attempt to get characters without any allocation/conversion.
+ if (::CFStringGetCharactersPtr(cfString)) {
+ aMsg->WriteBytes(::CFStringGetCharactersPtr(cfString), length * sizeof(UniChar));
+ } else {
+ UniChar *buffer = (UniChar*)moz_xmalloc(length * sizeof(UniChar));
+ ::CFStringGetCharacters(cfString, ::CFRangeMake(0, length), buffer);
+ aMsg->WriteBytes(buffer, length * sizeof(UniChar));
+ free(buffer);
+ }
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ bool haveString = false;
+ if (!aMsg->ReadBool(aIter, &haveString)) {
+ return false;
+ }
+ if (!haveString) {
+ *aResult = nullptr;
+ return true;
+ }
+
+ long length;
+ if (!ReadParam(aMsg, aIter, &length)) {
+ return false;
+ }
+
+ // Avoid integer multiplication overflow.
+ if (length > INT_MAX / static_cast<long>(sizeof(UniChar))) {
+ return false;
+ }
+
+ auto chars = mozilla::MakeUnique<UniChar[]>(length);
+ if (length != 0) {
+ if (!aMsg->ReadBytesInto(aIter, chars.get(), length * sizeof(UniChar))) {
+ return false;
+ }
+ }
+
+ *aResult = (NPNSString*)::CFStringCreateWithBytes(kCFAllocatorDefault, (UInt8*)chars.get(),
+ length * sizeof(UniChar),
+ kCFStringEncodingUTF16, false);
+ if (!*aResult) {
+ return false;
+ }
+
+ return true;
+ }
+};
+#endif
+
+#ifdef XP_MACOSX
+template <>
+struct ParamTraits<NSCursorInfo>
+{
+ typedef NSCursorInfo paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ NSCursorInfo::Type type = aParam.GetType();
+
+ aMsg->WriteInt(type);
+
+ nsPoint hotSpot = aParam.GetHotSpot();
+ WriteParam(aMsg, hotSpot.x);
+ WriteParam(aMsg, hotSpot.y);
+
+ uint32_t dataLength = aParam.GetCustomImageDataLength();
+ WriteParam(aMsg, dataLength);
+ if (dataLength == 0) {
+ return;
+ }
+
+ uint8_t* buffer = (uint8_t*)moz_xmalloc(dataLength);
+ memcpy(buffer, aParam.GetCustomImageData(), dataLength);
+ aMsg->WriteBytes(buffer, dataLength);
+ free(buffer);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ NSCursorInfo::Type type;
+ if (!aMsg->ReadInt(aIter, (int*)&type)) {
+ return false;
+ }
+
+ nscoord hotSpotX, hotSpotY;
+ if (!ReadParam(aMsg, aIter, &hotSpotX) ||
+ !ReadParam(aMsg, aIter, &hotSpotY)) {
+ return false;
+ }
+
+ uint32_t dataLength;
+ if (!ReadParam(aMsg, aIter, &dataLength)) {
+ return false;
+ }
+
+ auto data = mozilla::MakeUnique<uint8_t[]>(dataLength);
+ if (dataLength != 0) {
+ if (!aMsg->ReadBytesInto(aIter, data.get(), dataLength)) {
+ return false;
+ }
+ }
+
+ aResult->SetType(type);
+ aResult->SetHotSpot(nsPoint(hotSpotX, hotSpotY));
+ aResult->SetCustomImageData(data.get(), dataLength);
+
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ const char* typeName = aParam.GetTypeName();
+ nsPoint hotSpot = aParam.GetHotSpot();
+ int hotSpotX, hotSpotY;
+#ifdef NS_COORD_IS_FLOAT
+ hotSpotX = rint(hotSpot.x);
+ hotSpotY = rint(hotSpot.y);
+#else
+ hotSpotX = hotSpot.x;
+ hotSpotY = hotSpot.y;
+#endif
+ uint32_t dataLength = aParam.GetCustomImageDataLength();
+ uint8_t* data = aParam.GetCustomImageData();
+
+ aLog->append(StringPrintf(L"[%s, (%i %i), %u, %p]",
+ typeName, hotSpotX, hotSpotY, dataLength, data));
+ }
+};
+#else
template<>
struct ParamTraits<NSCursorInfo>
{
@@ -422,6 +589,7 @@ struct ParamTraits<NSCursorInfo>
return false;
}
};
+#endif // #ifdef XP_MACOSX
template <>
struct ParamTraits<mozilla::plugins::IPCByteRange>
@@ -564,7 +732,9 @@ struct ParamTraits<mozilla::plugins::NPAudioDeviceChangeDetailsIPC>
//
// NB: these guards are based on those where struct NPEvent is defined
// in npapi.h. They should be kept in sync.
-#if defined(XP_WIN)
+#if defined(XP_MACOSX)
+# include "mozilla/plugins/NPEventOSX.h"
+#elif defined(XP_WIN)
# include "mozilla/plugins/NPEventWindows.h"
#elif defined(XP_UNIX)
# include "mozilla/plugins/NPEventUnix.h"
diff --git a/dom/plugins/ipc/PluginModuleChild.cpp b/dom/plugins/ipc/PluginModuleChild.cpp
index bd13b8d9a4..1e65345fda 100644
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -132,6 +132,12 @@ PluginModuleChild::PluginModuleChild(bool aIsChrome)
MOZ_ASSERT(!gChromeInstance);
gChromeInstance = this;
}
+
+#ifdef XP_MACOSX
+ if (aIsChrome) {
+ mac_plugin_interposing::child::SetUpCocoaInterposing();
+ }
+#endif
}
PluginModuleChild::~PluginModuleChild()
@@ -254,8 +260,14 @@ PluginModuleChild::InitForChrome(const std::string& aPluginFilename,
AddQuirk(QUIRK_FLASH_EXPOSE_COORD_TRANSLATION);
}
#endif
+#if defined(XP_MACOSX)
+ const char* namePrefix = "Plugin Content";
+ char nameBuffer[80];
+ SprintfLiteral(nameBuffer, "%s (%s)", namePrefix, info.fName);
+ mozilla::plugins::PluginUtilsOSX::SetProcessName(nameBuffer);
+#endif
pluginFile.FreePluginInfo(info);
-#if defined(MOZ_X11)
+#if defined(MOZ_X11) || defined(XP_MACOSX)
if (!mLibrary)
#endif
{
diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp
index f7609bcd5e..6ea205ef0f 100755
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
* 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/. */
@@ -47,6 +48,9 @@
#ifdef MOZ_WIDGET_GTK
#include <glib.h>
+#elif XP_MACOSX
+#include "PluginInterposeOSX.h"
+#include "PluginUtilsOSX.h"
#endif
using base::KillProcess;
@@ -504,7 +508,7 @@ PluginModuleChromeParent::OnProcessLaunched(const bool aSucceeded)
if (NS_SUCCEEDED(mAsyncInitRv))
#endif
{
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
mAsyncInitRv = NP_Initialize(mNPNIface,
mNPPIface,
&mAsyncInitError);
@@ -513,6 +517,13 @@ PluginModuleChromeParent::OnProcessLaunched(const bool aSucceeded)
&mAsyncInitError);
#endif
}
+
+#if defined(XP_MACOSX)
+ if (NS_SUCCEEDED(mAsyncInitRv)) {
+ mAsyncInitRv = NP_GetEntryPoints(mNPPIface,
+ &mAsyncInitError);
+ }
+#endif
}
}
@@ -1657,8 +1668,13 @@ PluginModuleParent::GetSettings(PluginSettings* aSettings)
aSettings->supportsWindowless() = GetSetting(NPNVSupportsWindowless);
aSettings->userAgent() = NullableString(mNPNIface->uagent(nullptr));
+#if defined(XP_MACOSX)
+ aSettings->nativeCursorsSupported() =
+ Preferences::GetBool("dom.ipc.plugins.nativeCursorSupport", false);
+#else
// Need to initialize this to satisfy IPDL.
aSettings->nativeCursorsSupported() = false;
+#endif
}
void
@@ -1676,7 +1692,7 @@ PluginModuleChromeParent::CachedSettingChanged(const char* aPref, void* aModule)
module->CachedSettingChanged();
}
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
nsresult
PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error)
{
@@ -1814,7 +1830,7 @@ PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
return NS_OK;
}
-#if defined(XP_WIN)
+#if defined(XP_WIN) || defined(XP_MACOSX)
nsresult
PluginModuleContentParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
@@ -1837,10 +1853,20 @@ PluginModuleChromeParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
if (NS_FAILED(rv))
return rv;
+#if defined(XP_MACOSX)
+ if (!mSubprocess->IsConnected()) {
+ // The subprocess isn't connected yet. Defer NP_Initialize until
+ // OnProcessLaunched is invoked.
+ mInitOnAsyncConnect = true;
+ *error = NPERR_NO_ERROR;
+ return NS_OK;
+ }
+#else
if (mInitOnAsyncConnect) {
*error = NPERR_NO_ERROR;
return NS_OK;
}
+#endif
PluginSettings settings;
GetSettings(&settings);
@@ -2032,7 +2058,7 @@ PluginModuleParent::NP_GetValue(void *future, NPPVariable aVariable,
return NS_OK;
}
-#if defined(XP_WIN)
+#if defined(XP_WIN) || defined(XP_MACOSX)
nsresult
PluginModuleParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
{
@@ -2041,7 +2067,16 @@ PluginModuleParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
*error = NPERR_NO_ERROR;
if (mIsStartingAsync && !IsChrome()) {
mNPPIface = pFuncs;
+#if defined(XP_MACOSX)
+ if (mNPInitialized) {
+ SetPluginFuncs(pFuncs);
+ InitAsyncSurrogates();
+ } else {
+ PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
+ }
+#else
PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
+#endif
} else {
SetPluginFuncs(pFuncs);
}
@@ -2052,6 +2087,14 @@ PluginModuleParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
nsresult
PluginModuleChromeParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
{
+#if defined(XP_MACOSX)
+ if (mInitOnAsyncConnect) {
+ PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
+ mNPPIface = pFuncs;
+ *error = NPERR_NO_ERROR;
+ return NS_OK;
+ }
+#else
if (mIsStartingAsync) {
PluginAsyncSurrogate::NP_GetEntryPoints(pFuncs);
}
@@ -2061,6 +2104,7 @@ PluginModuleChromeParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* erro
*error = NPERR_NO_ERROR;
return NS_OK;
}
+#endif
// We need to have the plugin process update its function table here by
// actually calling NP_GetEntryPoints. The parent's function table will
@@ -2281,7 +2325,18 @@ PluginModuleParent::NPP_GetSitesWithData(nsCOMPtr<nsIGetSitesWithDataCallback> c
return NS_OK;
}
-#if defined(XP_WIN)
+#if defined(XP_MACOSX)
+nsresult
+PluginModuleParent::IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing)
+{
+ PluginInstanceParent* i = PluginInstanceParent::Cast(instance);
+ if (!i)
+ return NS_ERROR_FAILURE;
+
+ return i->IsRemoteDrawingCoreAnimation(aDrawing);
+}
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
nsresult
PluginModuleParent::ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor)
{
@@ -2291,9 +2346,17 @@ PluginModuleParent::ContentsScaleFactorChanged(NPP instance, double aContentsSca
return i->ContentsScaleFactorChanged(aContentsScaleFactor);
}
-#endif // #if defined(XP_WIN)
+#endif // #if defined(XP_MACOSX)
+
+#if defined(XP_MACOSX)
+bool
+PluginModuleParent::AnswerProcessSomeEvents()
+{
+ mozilla::plugins::PluginUtilsOSX::InvokeNativeEventLoop();
+ return true;
+}
-#if !defined(MOZ_WIDGET_GTK)
+#elif !defined(MOZ_WIDGET_GTK)
bool
PluginModuleParent::AnswerProcessSomeEvents()
{
@@ -2351,54 +2414,85 @@ PluginModuleParent::RecvPluginShowWindow(const uint32_t& aWindowId, const bool&
const size_t& aWidth, const size_t& aHeight)
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ CGRect windowBound = ::CGRectMake(aX, aY, aWidth, aHeight);
+ mac_plugin_interposing::parent::OnPluginShowWindow(aWindowId, windowBound, aModal);
+ return true;
+#else
NS_NOTREACHED(
"PluginInstanceParent::RecvPluginShowWindow not implemented!");
return false;
+#endif
}
bool
PluginModuleParent::RecvPluginHideWindow(const uint32_t& aWindowId)
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ mac_plugin_interposing::parent::OnPluginHideWindow(aWindowId, OtherPid());
+ return true;
+#else
NS_NOTREACHED(
"PluginInstanceParent::RecvPluginHideWindow not implemented!");
return false;
+#endif
}
bool
PluginModuleParent::RecvSetCursor(const NSCursorInfo& aCursorInfo)
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ mac_plugin_interposing::parent::OnSetCursor(aCursorInfo);
+ return true;
+#else
NS_NOTREACHED(
"PluginInstanceParent::RecvSetCursor not implemented!");
return false;
+#endif
}
bool
PluginModuleParent::RecvShowCursor(const bool& aShow)
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ mac_plugin_interposing::parent::OnShowCursor(aShow);
+ return true;
+#else
NS_NOTREACHED(
"PluginInstanceParent::RecvShowCursor not implemented!");
return false;
+#endif
}
bool
PluginModuleParent::RecvPushCursor(const NSCursorInfo& aCursorInfo)
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ mac_plugin_interposing::parent::OnPushCursor(aCursorInfo);
+ return true;
+#else
NS_NOTREACHED(
"PluginInstanceParent::RecvPushCursor not implemented!");
return false;
+#endif
}
bool
PluginModuleParent::RecvPopCursor()
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
+#if defined(XP_MACOSX)
+ mac_plugin_interposing::parent::OnPopCursor();
+ return true;
+#else
NS_NOTREACHED(
"PluginInstanceParent::RecvPopCursor not implemented!");
return false;
+#endif
}
bool
diff --git a/dom/plugins/ipc/PluginModuleParent.h b/dom/plugins/ipc/PluginModuleParent.h
index 11c2a47c65..909e8fe351 100644
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -247,7 +247,7 @@ protected:
const mozilla::NativeEventData& aNativeKeyData,
bool aIsConsumed) override;
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) override;
#else
virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) override;
@@ -257,7 +257,7 @@ protected:
virtual nsresult NP_GetMIMEDescription(const char** mimeDesc) override;
virtual nsresult NP_GetValue(void *future, NPPVariable aVariable,
void *aValue, NPError* error) override;
-#if defined(XP_WIN)
+#if defined(XP_WIN) || defined(XP_MACOSX)
virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) override;
#endif
virtual nsresult NPP_New(NPMIMEType pluginType, NPP instance,
@@ -278,7 +278,10 @@ private:
public:
-#if defined(XP_WIN)
+#if defined(XP_MACOSX)
+ virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing) override;
+#endif
+#if defined(XP_MACOSX) || defined(XP_WIN)
virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor) override;
#endif
@@ -352,7 +355,7 @@ class PluginModuleContentParent : public PluginModuleParent
virtual ~PluginModuleContentParent();
-#if defined(XP_WIN)
+#if defined(XP_WIN) || defined(XP_MACOSX)
nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) override;
#endif
@@ -476,13 +479,13 @@ private:
PluginProcessParent* Process() const { return mSubprocess; }
base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); }
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) override;
#else
virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) override;
#endif
-#if defined(XP_WIN)
+#if defined(XP_WIN) || defined(XP_MACOSX)
virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) override;
#endif
diff --git a/dom/plugins/ipc/PluginProcessChild.cpp b/dom/plugins/ipc/PluginProcessChild.cpp
index 9ba0926636..32bf062150 100644
--- a/dom/plugins/ipc/PluginProcessChild.cpp
+++ b/dom/plugins/ipc/PluginProcessChild.cpp
@@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
* 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/. */
@@ -12,6 +13,13 @@
#include "base/string_util.h"
#include "nsDebugImpl.h"
+#if defined(XP_MACOSX)
+#include "nsCocoaFeatures.h"
+// An undocumented CoreGraphics framework method, present in the same form
+// since at least OS X 10.5.
+extern "C" CGError CGSSetDebugOptions(int options);
+#endif
+
#ifdef XP_WIN
bool ShouldProtectPluginCurrentDirectory(char16ptr_t pluginFilePath);
#endif
@@ -32,6 +40,42 @@ PluginProcessChild::Init()
{
nsDebugImpl::SetMultiprocessMode("NPAPI");
+#if defined(XP_MACOSX)
+ // Remove the trigger for "dyld interposing" that we added in
+ // GeckoChildProcessHost::PerformAsyncLaunchInternal(), in the host
+ // process just before we were launched. Dyld interposing will still
+ // happen in our process (the plugin child process). But we don't want
+ // it to happen in any processes that the plugin might launch from our
+ // process.
+ nsCString interpose(PR_GetEnv("DYLD_INSERT_LIBRARIES"));
+ if (!interpose.IsEmpty()) {
+ // If we added the path to libplugin_child_interpose.dylib to an
+ // existing DYLD_INSERT_LIBRARIES, we appended it to the end, after a
+ // ":" path seperator.
+ int32_t lastSeparatorPos = interpose.RFind(":");
+ int32_t lastTriggerPos = interpose.RFind("libplugin_child_interpose.dylib");
+ bool needsReset = false;
+ if (lastTriggerPos != -1) {
+ if (lastSeparatorPos == -1) {
+ interpose.Truncate();
+ needsReset = true;
+ } else if (lastTriggerPos > lastSeparatorPos) {
+ interpose.SetLength(lastSeparatorPos);
+ needsReset = true;
+ }
+ }
+ if (needsReset) {
+ nsCString setInterpose("DYLD_INSERT_LIBRARIES=");
+ if (!interpose.IsEmpty()) {
+ setInterpose.Append(interpose);
+ }
+ // Values passed to PR_SetEnv() must be seperately allocated.
+ char* setInterposePtr = strdup(setInterpose.get());
+ PR_SetEnv(setInterposePtr);
+ }
+ }
+#endif
+
// Certain plugins, such as flash, steal the unhandled exception filter
// thus we never get crash reports when they fault. This call fixes it.
message_loop()->set_exception_restoration(true);
@@ -71,6 +115,17 @@ PluginProcessChild::Init()
bool retval = mPlugin.InitForChrome(pluginFilename, ParentPid(),
IOThreadChild::message_loop(),
IOThreadChild::channel());
+#if defined(XP_MACOSX)
+ 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);
+ }
+#endif
return retval;
}
diff --git a/dom/plugins/ipc/PluginProcessParent.cpp b/dom/plugins/ipc/PluginProcessParent.cpp
index 4445681359..53cd78ff3b 100644
--- a/dom/plugins/ipc/PluginProcessParent.cpp
+++ b/dom/plugins/ipc/PluginProcessParent.cpp
@@ -47,6 +47,13 @@ PluginProcessParent::Launch(mozilla::UniquePtr<LaunchCompleteTask> aLaunchComple
uint32_t containerArchitectures = GetSupportedArchitecturesForProcessType(GeckoProcessType_Plugin);
uint32_t pluginLibArchitectures = currentArchitecture;
+#ifdef XP_MACOSX
+ nsresult rv = GetArchitecturesForBinary(mPluginFilePath.c_str(), &pluginLibArchitectures);
+ if (NS_FAILED(rv)) {
+ // If the call failed just assume that we want the current architecture.
+ pluginLibArchitectures = currentArchitecture;
+ }
+#endif
ProcessArchitecture selectedArchitecture = currentArchitecture;
if (!(pluginLibArchitectures & containerArchitectures & currentArchitecture)) {
diff --git a/dom/plugins/ipc/PluginQuirks.cpp b/dom/plugins/ipc/PluginQuirks.cpp
index a8b0f62056..c60cf88362 100644
--- a/dom/plugins/ipc/PluginQuirks.cpp
+++ b/dom/plugins/ipc/PluginQuirks.cpp
@@ -49,6 +49,16 @@ int GetQuirksFromMimeTypeAndFilename(const nsCString& aMimeType,
}
#endif
+#ifdef XP_MACOSX
+ // Whitelist Flash and Quicktime to support offline renderer
+ NS_NAMED_LITERAL_CSTRING(quicktime, "QuickTime Plugin.plugin");
+ if (specialType == nsPluginHost::eSpecialType_Flash) {
+ quirks |= QUIRK_ALLOW_OFFLINE_RENDERER;
+ } else if (FindInReadable(quicktime, aPluginFilename)) {
+ quirks |= QUIRK_ALLOW_OFFLINE_RENDERER;
+ }
+#endif
+
#ifdef OS_WIN
if (specialType == nsPluginHost::eSpecialType_Unity) {
quirks |= QUIRK_UNITY_FIXUP_MOUSE_CAPTURE;
diff --git a/dom/plugins/ipc/PluginUtilsOSX.h b/dom/plugins/ipc/PluginUtilsOSX.h
new file mode 100644
index 0000000000..c201782c0f
--- /dev/null
+++ b/dom/plugins/ipc/PluginUtilsOSX.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:set ts=2 sts=2 sw=2 et cin:
+/* 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 dom_plugins_PluginUtilsOSX_h
+#define dom_plugins_PluginUtilsOSX_h 1
+
+#include "npapi.h"
+#include "mozilla/gfx/QuartzSupport.h"
+#include "nsRect.h"
+
+namespace mozilla {
+namespace plugins {
+namespace PluginUtilsOSX {
+
+// Need to call back into the browser's message loop to process event.
+typedef void (*RemoteProcessEvents) (void*);
+
+NPError ShowCocoaContextMenu(void* aMenu, int aX, int aY, void* pluginModule, RemoteProcessEvents remoteEvent);
+
+void InvokeNativeEventLoop();
+
+// Need to call back and send a cocoa draw event to the plugin.
+typedef void (*DrawPluginFunc) (CGContextRef, void*, nsIntRect aUpdateRect);
+
+void* GetCGLayer(DrawPluginFunc aFunc, void* aPluginInstance, double aContentsScaleFactor);
+void ReleaseCGLayer(void* cgLayer);
+void Repaint(void* cgLayer, nsIntRect aRect);
+
+bool SetProcessName(const char* aProcessName);
+
+/*
+ * Provides a wrapper around nsCARenderer to manage double buffering
+ * without having to unbind nsCARenderer on every surface swaps.
+ *
+ * The double buffer renderer begins with no initialize surfaces.
+ * The buffers can be initialized and cleared individually.
+ * Swapping still occurs regardless if the buffers are initialized.
+ */
+class nsDoubleBufferCARenderer {
+public:
+ nsDoubleBufferCARenderer() : mCALayer(nullptr), mContentsScaleFactor(1.0) {}
+ // Returns width in "display pixels". A "display pixel" is the smallest
+ // fully addressable part of a display. But in HiDPI modes each "display
+ // pixel" corresponds to more than one device pixel. Multiply display pixels
+ // by mContentsScaleFactor to get device pixels.
+ size_t GetFrontSurfaceWidth();
+ // Returns height in "display pixels". Multiply by
+ // mContentsScaleFactor to get device pixels.
+ size_t GetFrontSurfaceHeight();
+ double GetFrontSurfaceContentsScaleFactor();
+ // Returns width in "display pixels". Multiply by
+ // mContentsScaleFactor to get device pixels.
+ size_t GetBackSurfaceWidth();
+ // Returns height in "display pixels". Multiply by
+ // mContentsScaleFactor to get device pixels.
+ size_t GetBackSurfaceHeight();
+ double GetBackSurfaceContentsScaleFactor();
+ IOSurfaceID GetFrontSurfaceID();
+
+ bool HasBackSurface();
+ bool HasFrontSurface();
+ bool HasCALayer();
+
+ void SetCALayer(void *aCALayer);
+ // aWidth and aHeight are in "display pixels". Multiply by
+ // aContentsScaleFactor to get device pixels.
+ bool InitFrontSurface(size_t aWidth, size_t aHeight,
+ double aContentsScaleFactor,
+ AllowOfflineRendererEnum aAllowOfflineRenderer);
+ void Render();
+ void SwapSurfaces();
+ void ClearFrontSurface();
+ void ClearBackSurface();
+
+ double GetContentsScaleFactor() { return mContentsScaleFactor; }
+
+private:
+ void *mCALayer;
+ RefPtr<nsCARenderer> mCARenderer;
+ RefPtr<MacIOSurface> mFrontSurface;
+ RefPtr<MacIOSurface> mBackSurface;
+ double mContentsScaleFactor;
+};
+
+} // namespace PluginUtilsOSX
+} // namespace plugins
+} // namespace mozilla
+
+#endif //dom_plugins_PluginUtilsOSX_h
diff --git a/dom/plugins/ipc/PluginUtilsOSX.mm b/dom/plugins/ipc/PluginUtilsOSX.mm
new file mode 100644
index 0000000000..2a920f7f61
--- /dev/null
+++ b/dom/plugins/ipc/PluginUtilsOSX.mm
@@ -0,0 +1,462 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:set ts=2 sts=2 sw=2 et cin:
+/* 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>
+#import <AppKit/AppKit.h>
+#import <QuartzCore/QuartzCore.h>
+#include "PluginUtilsOSX.h"
+
+// Remove definitions for try/catch interfering with ObjCException macros.
+#include "nsObjCExceptions.h"
+#include "nsCocoaUtils.h"
+
+#include "nsDebug.h"
+
+#include "mozilla/Sprintf.h"
+
+@interface CALayer (ContentsScale)
+- (double)contentsScale;
+- (void)setContentsScale:(double)scale;
+@end
+
+using namespace mozilla::plugins::PluginUtilsOSX;
+
+@interface CGBridgeLayer : CALayer {
+ DrawPluginFunc mDrawFunc;
+ void* mPluginInstance;
+ nsIntRect mUpdateRect;
+}
+- (void)setDrawFunc:(DrawPluginFunc)aFunc
+ pluginInstance:(void*)aPluginInstance;
+- (void)updateRect:(nsIntRect)aRect;
+
+@end
+
+// CGBitmapContextSetData() is an undocumented function present (with
+// the same signature) since at least OS X 10.5. As the name suggests,
+// it's used to replace the "data" in a bitmap context that was
+// originally specified in a call to CGBitmapContextCreate() or
+// CGBitmapContextCreateWithData().
+typedef void (*CGBitmapContextSetDataFunc) (CGContextRef c,
+ size_t x,
+ size_t y,
+ size_t width,
+ size_t height,
+ void* data,
+ size_t bitsPerComponent,
+ size_t bitsPerPixel,
+ size_t bytesPerRow);
+CGBitmapContextSetDataFunc CGBitmapContextSetDataPtr = NULL;
+
+@implementation CGBridgeLayer
+- (void) updateRect:(nsIntRect)aRect
+{
+ mUpdateRect.UnionRect(mUpdateRect, aRect);
+}
+
+- (void) setDrawFunc:(DrawPluginFunc)aFunc
+ pluginInstance:(void*)aPluginInstance
+{
+ mDrawFunc = aFunc;
+ mPluginInstance = aPluginInstance;
+}
+
+- (void)drawInContext:(CGContextRef)aCGContext
+{
+ ::CGContextSaveGState(aCGContext);
+ ::CGContextTranslateCTM(aCGContext, 0, self.bounds.size.height);
+ ::CGContextScaleCTM(aCGContext, (CGFloat) 1, (CGFloat) -1);
+
+ mUpdateRect = nsIntRect::Truncate(0, 0, self.bounds.size.width, self.bounds.size.height);
+
+ mDrawFunc(aCGContext, mPluginInstance, mUpdateRect);
+
+ ::CGContextRestoreGState(aCGContext);
+
+ mUpdateRect.SetEmpty();
+}
+
+@end
+
+void* mozilla::plugins::PluginUtilsOSX::GetCGLayer(DrawPluginFunc aFunc,
+ void* aPluginInstance,
+ double aContentsScaleFactor) {
+ CGBridgeLayer *bridgeLayer = [[CGBridgeLayer alloc] init];
+
+ // We need to make bridgeLayer behave properly when its superlayer changes
+ // size (in nsCARenderer::SetBounds()).
+ bridgeLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
+ bridgeLayer.needsDisplayOnBoundsChange = YES;
+ NSNull *nullValue = [NSNull null];
+ NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys:
+ nullValue, @"bounds",
+ nullValue, @"contents",
+ nullValue, @"contentsRect",
+ nullValue, @"position",
+ nil];
+ [bridgeLayer setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]];
+
+ // For reasons that aren't clear (perhaps one or more OS bugs), we can only
+ // use full HiDPI resolution here if the tree is built with the 10.7 SDK or
+ // up. If we build with the 10.6 SDK, changing the contentsScale property
+ // of bridgeLayer (even to the same value) causes it to stop working (go
+ // blank). This doesn't happen with objects that are members of the CALayer
+ // class (as opposed to one of its subclasses).
+#if defined(MAC_OS_X_VERSION_10_7) && \
+ MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+ if ([bridgeLayer respondsToSelector:@selector(setContentsScale:)]) {
+ bridgeLayer.contentsScale = aContentsScaleFactor;
+ }
+#endif
+
+ [bridgeLayer setDrawFunc:aFunc
+ pluginInstance:aPluginInstance];
+ return bridgeLayer;
+}
+
+void mozilla::plugins::PluginUtilsOSX::ReleaseCGLayer(void *cgLayer) {
+ CGBridgeLayer *bridgeLayer = (CGBridgeLayer*)cgLayer;
+ [bridgeLayer release];
+}
+
+void mozilla::plugins::PluginUtilsOSX::Repaint(void *caLayer, nsIntRect aRect) {
+ CGBridgeLayer *bridgeLayer = (CGBridgeLayer*)caLayer;
+ [CATransaction begin];
+ [bridgeLayer updateRect:aRect];
+ [bridgeLayer setNeedsDisplay];
+ [bridgeLayer displayIfNeeded];
+ [CATransaction commit];
+}
+
+@interface EventProcessor : NSObject {
+ RemoteProcessEvents aRemoteEvents;
+ void *aPluginModule;
+}
+- (void)setRemoteEvents:(RemoteProcessEvents) remoteEvents pluginModule:(void*) pluginModule;
+- (void)onTick;
+@end
+
+@implementation EventProcessor
+- (void) onTick
+{
+ aRemoteEvents(aPluginModule);
+}
+
+- (void)setRemoteEvents:(RemoteProcessEvents) remoteEvents pluginModule:(void*) pluginModule
+{
+ aRemoteEvents = remoteEvents;
+ aPluginModule = pluginModule;
+}
+@end
+
+#define EVENT_PROCESS_DELAY 0.05 // 50 ms
+
+NPError mozilla::plugins::PluginUtilsOSX::ShowCocoaContextMenu(void* aMenu, int aX, int aY, void* pluginModule, RemoteProcessEvents remoteEvent)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ // Set the native cursor to the OS default (an arrow) before displaying the
+ // context menu. Otherwise (if the plugin has changed the cursor) it may
+ // stay as the plugin has set it -- which means it may be invisible. We
+ // need to do this because we display the context menu without making the
+ // plugin process the foreground process. If we did, the cursor would
+ // change to an arrow cursor automatically -- as it does in Chrome.
+ [[NSCursor arrowCursor] set];
+
+ EventProcessor* eventProcessor = nullptr;
+ NSTimer *eventTimer = nullptr;
+ if (pluginModule) {
+ // Create a timer to process browser events while waiting
+ // on the menu. This prevents the browser from hanging
+ // during the lifetime of the menu.
+ eventProcessor = [[EventProcessor alloc] init];
+ [eventProcessor setRemoteEvents:remoteEvent pluginModule:pluginModule];
+ eventTimer = [NSTimer timerWithTimeInterval:EVENT_PROCESS_DELAY
+ target:eventProcessor selector:@selector(onTick)
+ userInfo:nil repeats:TRUE];
+ // Use NSEventTrackingRunLoopMode otherwise the timer will
+ // not fire during the right click menu.
+ [[NSRunLoop currentRunLoop] addTimer:eventTimer
+ forMode:NSEventTrackingRunLoopMode];
+ }
+
+ NSMenu* nsmenu = reinterpret_cast<NSMenu*>(aMenu);
+ NSPoint screen_point = ::NSMakePoint(aX, aY);
+
+ [nsmenu popUpMenuPositioningItem:nil atLocation:screen_point inView:nil];
+
+ if (pluginModule) {
+ [eventTimer invalidate];
+ [eventProcessor release];
+ }
+
+ return NPERR_NO_ERROR;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NPERR_GENERIC_ERROR);
+}
+
+void mozilla::plugins::PluginUtilsOSX::InvokeNativeEventLoop()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+ ::CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, true);
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+
+#define UNDOCUMENTED_SESSION_CONSTANT ((int)-2)
+namespace mozilla {
+namespace plugins {
+namespace PluginUtilsOSX {
+ static void *sApplicationASN = NULL;
+ static void *sApplicationInfoItem = NULL;
+} // namespace PluginUtilsOSX
+} // namespace plugins
+} // namespace mozilla
+
+bool mozilla::plugins::PluginUtilsOSX::SetProcessName(const char* aProcessName) {
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+ nsAutoreleasePool localPool;
+
+ if (!aProcessName || strcmp(aProcessName, "") == 0) {
+ return false;
+ }
+
+ NSString *currentName = [[[NSBundle mainBundle] localizedInfoDictionary]
+ objectForKey:(NSString *)kCFBundleNameKey];
+
+ char formattedName[1024];
+ SprintfLiteral(formattedName, "%s %s", [currentName UTF8String], aProcessName);
+
+ aProcessName = formattedName;
+
+ // This function is based on Chrome/Webkit's and relies on potentially dangerous SPI.
+ typedef CFTypeRef (*LSGetASNType)();
+ typedef OSStatus (*LSSetInformationItemType)(int, CFTypeRef,
+ CFStringRef,
+ CFStringRef,
+ CFDictionaryRef*);
+
+ CFBundleRef launchServices = ::CFBundleGetBundleWithIdentifier(
+ CFSTR("com.apple.LaunchServices"));
+ if (!launchServices) {
+ NS_WARNING("Failed to set process name: Could not open LaunchServices bundle");
+ return false;
+ }
+
+ if (!sApplicationASN) {
+ sApplicationASN = ::CFBundleGetFunctionPointerForName(launchServices,
+ CFSTR("_LSGetCurrentApplicationASN"));
+ }
+
+ LSGetASNType getASNFunc = reinterpret_cast<LSGetASNType>
+ (sApplicationASN);
+
+ if (!sApplicationInfoItem) {
+ sApplicationInfoItem = ::CFBundleGetFunctionPointerForName(launchServices,
+ CFSTR("_LSSetApplicationInformationItem"));
+ }
+
+ LSSetInformationItemType setInformationItemFunc
+ = reinterpret_cast<LSSetInformationItemType>
+ (sApplicationInfoItem);
+
+ void * displayNameKeyAddr = ::CFBundleGetDataPointerForName(launchServices,
+ CFSTR("_kLSDisplayNameKey"));
+
+ CFStringRef displayNameKey = nil;
+ if (displayNameKeyAddr) {
+ displayNameKey = reinterpret_cast<CFStringRef>(*(CFStringRef*)displayNameKeyAddr);
+ }
+
+ // Rename will fail without this
+ ProcessSerialNumber psn;
+ if (::GetCurrentProcess(&psn) != noErr) {
+ return false;
+ }
+
+ CFTypeRef currentAsn = getASNFunc();
+
+ if (!getASNFunc || !setInformationItemFunc ||
+ !displayNameKey || !currentAsn) {
+ NS_WARNING("Failed to set process name: Accessing launchServices failed");
+ return false;
+ }
+
+ CFStringRef processName = ::CFStringCreateWithCString(nil,
+ aProcessName,
+ kCFStringEncodingASCII);
+ if (!processName) {
+ NS_WARNING("Failed to set process name: Could not create CFStringRef");
+ return false;
+ }
+
+ OSErr err = setInformationItemFunc(UNDOCUMENTED_SESSION_CONSTANT, currentAsn,
+ displayNameKey, processName,
+ nil); // Optional out param
+ ::CFRelease(processName);
+ if (err != noErr) {
+ NS_WARNING("Failed to set process name: LSSetInformationItemType err");
+ return false;
+ }
+
+ return true;
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
+}
+
+namespace mozilla {
+namespace plugins {
+namespace PluginUtilsOSX {
+
+size_t nsDoubleBufferCARenderer::GetFrontSurfaceWidth() {
+ if (!HasFrontSurface()) {
+ return 0;
+ }
+
+ return mFrontSurface->GetWidth();
+}
+
+size_t nsDoubleBufferCARenderer::GetFrontSurfaceHeight() {
+ if (!HasFrontSurface()) {
+ return 0;
+ }
+
+ return mFrontSurface->GetHeight();
+}
+
+double nsDoubleBufferCARenderer::GetFrontSurfaceContentsScaleFactor() {
+ if (!HasFrontSurface()) {
+ return 1.0;
+ }
+
+ return mFrontSurface->GetContentsScaleFactor();
+}
+
+size_t nsDoubleBufferCARenderer::GetBackSurfaceWidth() {
+ if (!HasBackSurface()) {
+ return 0;
+ }
+
+ return mBackSurface->GetWidth();
+}
+
+size_t nsDoubleBufferCARenderer::GetBackSurfaceHeight() {
+ if (!HasBackSurface()) {
+ return 0;
+ }
+
+ return mBackSurface->GetHeight();
+}
+
+double nsDoubleBufferCARenderer::GetBackSurfaceContentsScaleFactor() {
+ if (!HasBackSurface()) {
+ return 1.0;
+ }
+
+ return mBackSurface->GetContentsScaleFactor();
+}
+
+IOSurfaceID nsDoubleBufferCARenderer::GetFrontSurfaceID() {
+ if (!HasFrontSurface()) {
+ return 0;
+ }
+
+ return mFrontSurface->GetIOSurfaceID();
+}
+
+bool nsDoubleBufferCARenderer::HasBackSurface() {
+ return !!mBackSurface;
+}
+
+bool nsDoubleBufferCARenderer::HasFrontSurface() {
+ return !!mFrontSurface;
+}
+
+bool nsDoubleBufferCARenderer::HasCALayer() {
+ return !!mCALayer;
+}
+
+void nsDoubleBufferCARenderer::SetCALayer(void *aCALayer) {
+ mCALayer = aCALayer;
+}
+
+bool nsDoubleBufferCARenderer::InitFrontSurface(size_t aWidth, size_t aHeight,
+ double aContentsScaleFactor,
+ AllowOfflineRendererEnum aAllowOfflineRenderer) {
+ if (!mCALayer) {
+ return false;
+ }
+
+ mContentsScaleFactor = aContentsScaleFactor;
+ mFrontSurface = MacIOSurface::CreateIOSurface(aWidth, aHeight, mContentsScaleFactor);
+ if (!mFrontSurface) {
+ mCARenderer = nullptr;
+ return false;
+ }
+
+ if (!mCARenderer) {
+ mCARenderer = new nsCARenderer();
+ if (!mCARenderer) {
+ mFrontSurface = nullptr;
+ return false;
+ }
+
+ mCARenderer->AttachIOSurface(mFrontSurface);
+
+ nsresult result = mCARenderer->SetupRenderer(mCALayer,
+ mFrontSurface->GetWidth(),
+ mFrontSurface->GetHeight(),
+ mContentsScaleFactor,
+ aAllowOfflineRenderer);
+
+ if (result != NS_OK) {
+ mCARenderer = nullptr;
+ mFrontSurface = nullptr;
+ return false;
+ }
+ } else {
+ mCARenderer->AttachIOSurface(mFrontSurface);
+ }
+
+ return true;
+}
+
+void nsDoubleBufferCARenderer::Render() {
+ if (!HasFrontSurface() || !mCARenderer) {
+ return;
+ }
+
+ mCARenderer->Render(GetFrontSurfaceWidth(), GetFrontSurfaceHeight(),
+ mContentsScaleFactor, nullptr);
+}
+
+void nsDoubleBufferCARenderer::SwapSurfaces() {
+ RefPtr<MacIOSurface> prevFrontSurface = mFrontSurface;
+ mFrontSurface = mBackSurface;
+ mBackSurface = prevFrontSurface;
+
+ if (mFrontSurface) {
+ mCARenderer->AttachIOSurface(mFrontSurface);
+ }
+}
+
+void nsDoubleBufferCARenderer::ClearFrontSurface() {
+ mFrontSurface = nullptr;
+ if (!mFrontSurface && !mBackSurface) {
+ mCARenderer = nullptr;
+ }
+}
+
+void nsDoubleBufferCARenderer::ClearBackSurface() {
+ mBackSurface = nullptr;
+ if (!mFrontSurface && !mBackSurface) {
+ mCARenderer = nullptr;
+ }
+}
+
+} // namespace PluginUtilsOSX
+} // namespace plugins
+} // namespace mozilla
+
diff --git a/dom/plugins/ipc/interpose/moz.build b/dom/plugins/ipc/interpose/moz.build
new file mode 100644
index 0000000000..319c325a70
--- /dev/null
+++ b/dom/plugins/ipc/interpose/moz.build
@@ -0,0 +1,12 @@
+# -*- 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/.
+
+SharedLibrary('plugin_child_interpose')
+
+SOURCES += [ "%s.mm" % (LIBRARY_NAME) ]
+
+OS_LIBS += ['-framework Carbon']
+
+DIST_INSTALL = True
diff --git a/dom/plugins/ipc/interpose/plugin_child_interpose.mm b/dom/plugins/ipc/interpose/plugin_child_interpose.mm
new file mode 100644
index 0000000000..58c39b0a1c
--- /dev/null
+++ b/dom/plugins/ipc/interpose/plugin_child_interpose.mm
@@ -0,0 +1,134 @@
+/* -*- 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/. */
+
+// Use "dyld interposing" to hook methods imported from other libraries in the
+// plugin child process. The basic technique is described at
+// http://books.google.com/books?id=K8vUkpOXhN4C&pg=PA73&lpg=PA73&dq=__interpose&source=bl&ots=OJnnXZYpZC&sig=o7I3lXvoduUi13SrPfOON7o3do4&hl=en&ei=AoehS9brCYGQNrvsmeUM&sa=X&oi=book_result&ct=result&resnum=6&ved=0CBsQ6AEwBQ#v=onepage&q=__interpose&f=false.
+// The idea of doing it for the plugin child process comes from Chromium code,
+// particularly from plugin_carbon_interpose_mac.cc
+// (http://codesearch.google.com/codesearch/p?hl=en#OAMlx_jo-ck/src/chrome/browser/plugin_carbon_interpose_mac.cc&q=nscursor&exact_package=chromium&d=1&l=168)
+// and from PluginProcessHost::Init() in plugin_process_host.cc
+// (http://codesearch.google.com/codesearch/p?hl=en#OAMlx_jo-ck/src/content/browser/plugin_process_host.cc&q=nscursor&exact_package=chromium&d=1&l=222).
+
+// These hooks are needed to make certain OS calls work from the child process
+// (a background process) that would normally only work when called in the
+// parent process (the foreground process). They allow us to serialize
+// information from the child process to the parent process, so that the same
+// (or equivalent) calls can be made from the parent process.
+
+// This file lives in a seperate module (libplugin_child_interpose.dylib),
+// which will get loaded by the OS before any other modules when the plugin
+// child process is launched (from GeckoChildProcessHost::
+// PerformAsyncLaunchInternal()). For this reason it shouldn't link in other
+// browser modules when loaded. Instead it should use dlsym() to load
+// pointers to the methods it wants to call in other modules.
+
+#if !defined(__LP64__)
+
+#include <dlfcn.h>
+#import <Carbon/Carbon.h>
+
+// The header file QuickdrawAPI.h is missing on OS X 10.7 and up (though the
+// QuickDraw APIs defined in it are still present) -- so we need to supply the
+// relevant parts of its contents here. It's likely that Apple will eventually
+// remove the APIs themselves (probably in OS X 10.8), so we need to make them
+// weak imports, and test for their presence before using them.
+#if !defined(__QUICKDRAWAPI__)
+
+struct Cursor;
+extern "C" void SetCursor(const Cursor * crsr) __attribute__((weak_import));
+
+#endif /* __QUICKDRAWAPI__ */
+
+BOOL (*OnSetThemeCursorPtr) (ThemeCursor) = NULL;
+BOOL (*OnSetCursorPtr) (const Cursor*) = NULL;
+BOOL (*OnHideCursorPtr) () = NULL;
+BOOL (*OnShowCursorPtr) () = NULL;
+
+static BOOL loadXULPtrs()
+{
+ if (!OnSetThemeCursorPtr) {
+ // mac_plugin_interposing_child_OnSetThemeCursor(ThemeCursor cursor) is in
+ // PluginInterposeOSX.mm
+ OnSetThemeCursorPtr = (BOOL(*)(ThemeCursor))
+ dlsym(RTLD_DEFAULT, "mac_plugin_interposing_child_OnSetThemeCursor");
+ }
+ if (!OnSetCursorPtr) {
+ // mac_plugin_interposing_child_OnSetCursor(const Cursor* cursor) is in
+ // PluginInterposeOSX.mm
+ OnSetCursorPtr = (BOOL(*)(const Cursor*))
+ dlsym(RTLD_DEFAULT, "mac_plugin_interposing_child_OnSetCursor");
+ }
+ if (!OnHideCursorPtr) {
+ // mac_plugin_interposing_child_OnHideCursor() is in PluginInterposeOSX.mm
+ OnHideCursorPtr = (BOOL(*)())
+ dlsym(RTLD_DEFAULT, "mac_plugin_interposing_child_OnHideCursor");
+ }
+ if (!OnShowCursorPtr) {
+ // mac_plugin_interposing_child_OnShowCursor() is in PluginInterposeOSX.mm
+ OnShowCursorPtr = (BOOL(*)())
+ dlsym(RTLD_DEFAULT, "mac_plugin_interposing_child_OnShowCursor");
+ }
+ return (OnSetCursorPtr && OnSetThemeCursorPtr && OnHideCursorPtr && OnShowCursorPtr);
+}
+
+static OSStatus MacPluginChildSetThemeCursor(ThemeCursor cursor)
+{
+ if (loadXULPtrs()) {
+ OnSetThemeCursorPtr(cursor);
+ }
+ return ::SetThemeCursor(cursor);
+}
+
+static void MacPluginChildSetCursor(const Cursor* cursor)
+{
+ if (::SetCursor) {
+ if (loadXULPtrs()) {
+ OnSetCursorPtr(cursor);
+ }
+ ::SetCursor(cursor);
+ }
+}
+
+static CGError MacPluginChildCGDisplayHideCursor(CGDirectDisplayID display)
+{
+ if (loadXULPtrs()) {
+ OnHideCursorPtr();
+ }
+ return ::CGDisplayHideCursor(display);
+}
+
+static CGError MacPluginChildCGDisplayShowCursor(CGDirectDisplayID display)
+{
+ if (loadXULPtrs()) {
+ OnShowCursorPtr();
+ }
+ return ::CGDisplayShowCursor(display);
+}
+
+#pragma mark -
+
+struct interpose_substitution {
+ const void* replacement;
+ const void* original;
+};
+
+#define INTERPOSE_FUNCTION(function) \
+ { reinterpret_cast<const void*>(MacPluginChild##function), \
+ reinterpret_cast<const void*>(function) }
+
+__attribute__((used)) static const interpose_substitution substitutions[]
+ __attribute__((section("__DATA, __interpose"))) = {
+ INTERPOSE_FUNCTION(SetThemeCursor),
+ INTERPOSE_FUNCTION(CGDisplayHideCursor),
+ INTERPOSE_FUNCTION(CGDisplayShowCursor),
+ // SetCursor() and other QuickDraw APIs will probably be removed in OS X
+ // 10.8. But this will make 'SetCursor' NULL, which will just stop the OS
+ // from interposing it (tested using an INTERPOSE_FUNCTION_BROKEN macro
+ // that just sets the second address of each tuple to NULL).
+ INTERPOSE_FUNCTION(SetCursor),
+};
+
+#endif // !__LP64__
diff --git a/dom/plugins/ipc/moz.build b/dom/plugins/ipc/moz.build
index 00f9a47128..9190d19272 100644
--- a/dom/plugins/ipc/moz.build
+++ b/dom/plugins/ipc/moz.build
@@ -3,6 +3,9 @@
# 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/.
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ DIRS += ['interpose']
+
EXPORTS.mozilla += [
'PluginLibrary.h',
]
@@ -13,6 +16,7 @@ EXPORTS.mozilla.plugins += [
'BrowserStreamParent.h',
'ChildAsyncCall.h',
'ChildTimer.h',
+ 'NPEventOSX.h',
'NPEventUnix.h',
'NPEventWindows.h',
'PluginAsyncSurrogate.h',
@@ -32,6 +36,7 @@ EXPORTS.mozilla.plugins += [
'PluginScriptableObjectUtils.h',
'PluginStreamChild.h',
'PluginStreamParent.h',
+ 'PluginUtilsOSX.h',
'PluginWidgetChild.h',
'PluginWidgetParent.h',
'StreamNotifyChild.h',
@@ -53,6 +58,11 @@ if CONFIG['OS_ARCH'] == 'WINNT':
'hangui',
]
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ EXPORTS.mozilla.plugins += [
+ 'PluginInterposeOSX.h',
+ ]
+
SOURCES += [
'BrowserStreamChild.cpp',
'BrowserStreamParent.cpp',
@@ -76,6 +86,12 @@ SOURCES += [
'PluginWidgetParent.cpp',
]
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'PluginInterposeOSX.mm',
+ 'PluginUtilsOSX.mm',
+ ]
+
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
SOURCES += [
'D3D11SurfaceHolder.cpp',
diff --git a/dom/system/OSFileConstants.cpp b/dom/system/OSFileConstants.cpp
index 2b8a7e5369..551ed5773f 100644
--- a/dom/system/OSFileConstants.cpp
+++ b/dom/system/OSFileConstants.cpp
@@ -24,6 +24,10 @@
#include <linux/fadvise.h>
#endif // defined(XP_LINUX)
+#if defined(XP_MACOSX)
+#include "copyfile.h"
+#endif // defined(XP_MACOSX)
+
#if defined(XP_WIN)
#include <windows.h>
#include <accctrl.h>
@@ -122,6 +126,22 @@ struct Paths {
nsString winStartMenuProgsDir;
#endif // defined(XP_WIN)
+#if defined(XP_MACOSX)
+ /**
+ * The user's Library directory.
+ */
+ nsString macUserLibDir;
+ /**
+ * The Application directory, that stores applications installed in the
+ * system.
+ */
+ nsString macLocalApplicationsDir;
+ /**
+ * The user's trash directory.
+ */
+ nsString macTrashDir;
+#endif // defined(XP_MACOSX)
+
Paths()
{
libDir.SetIsVoid(true);
@@ -136,6 +156,12 @@ struct Paths {
winAppDataDir.SetIsVoid(true);
winStartMenuProgsDir.SetIsVoid(true);
#endif // defined(XP_WIN)
+
+#if defined(XP_MACOSX)
+ macUserLibDir.SetIsVoid(true);
+ macLocalApplicationsDir.SetIsVoid(true);
+ macTrashDir.SetIsVoid(true);
+#endif // defined(XP_MACOSX)
}
};
@@ -280,6 +306,12 @@ nsresult InitOSFileConstants()
GetPathToSpecialDir(NS_WIN_PROGRAMS_DIR, paths->winStartMenuProgsDir);
#endif // defined(XP_WIN)
+#if defined(XP_MACOSX)
+ GetPathToSpecialDir(NS_MAC_USER_LIB_DIR, paths->macUserLibDir);
+ GetPathToSpecialDir(NS_OSX_LOCAL_APPLICATIONS_DIR, paths->macLocalApplicationsDir);
+ GetPathToSpecialDir(NS_MAC_TRASH_DIR, paths->macTrashDir);
+#endif // defined(XP_MACOSX)
+
gPaths = paths.forget();
// Get the umask from the system-info service.
@@ -933,12 +965,20 @@ bool DefineOSFileConstants(JSContext *cx, JS::Handle<JSObject*> global)
// Note that we don't actually provide the full path, only the name of the
// library, which is sufficient to link to the library using js-ctypes.
- // On all supported platforms, libxul is a library "xul" with regular
+#if defined(XP_MACOSX)
+ // Under MacOS X, for some reason, libxul is called simply "XUL",
+ // and we need to provide the full path.
+ nsAutoString libxul;
+ libxul.Append(gPaths->libDir);
+ libxul.AppendLiteral("/XUL");
+#else
+ // On other platforms, libxul is a library "xul" with regular
// library prefix/suffix.
nsAutoString libxul;
libxul.AppendLiteral(DLL_PREFIX);
libxul.AppendLiteral("xul");
libxul.AppendLiteral(DLL_SUFFIX);
+#endif // defined(XP_MACOSX)
if (!SetStringProperty(cx, objPath, "libxul", libxul)) {
return false;
@@ -986,6 +1026,20 @@ bool DefineOSFileConstants(JSContext *cx, JS::Handle<JSObject*> global)
}
#endif // defined(XP_WIN)
+#if defined(XP_MACOSX)
+ if (!SetStringProperty(cx, objPath, "macUserLibDir", gPaths->macUserLibDir)) {
+ return false;
+ }
+
+ if (!SetStringProperty(cx, objPath, "macLocalApplicationsDir", gPaths->macLocalApplicationsDir)) {
+ return false;
+ }
+
+ if (!SetStringProperty(cx, objPath, "macTrashDir", gPaths->macTrashDir)) {
+ return false;
+ }
+#endif // defined(XP_MACOSX)
+
// sqlite3 is always a shared lib
nsAutoString libsqlite3;
libsqlite3.AppendLiteral(DLL_PREFIX);
diff --git a/dom/system/mac/CoreLocationLocationProvider.h b/dom/system/mac/CoreLocationLocationProvider.h
new file mode 100644
index 0000000000..979bc916d8
--- /dev/null
+++ b/dom/system/mac/CoreLocationLocationProvider.h
@@ -0,0 +1,59 @@
+/* -*- 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();
+ };
+};
diff --git a/dom/system/mac/CoreLocationLocationProvider.mm b/dom/system/mac/CoreLocationLocationProvider.mm
new file mode 100644
index 0000000000..7a3feba97b
--- /dev/null
+++ b/dom/system/mac/CoreLocationLocationProvider.mm
@@ -0,0 +1,268 @@
+/* -*- 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;
+}
diff --git a/dom/system/mac/moz.build b/dom/system/mac/moz.build
new file mode 100644
index 0000000000..08b7c2151e
--- /dev/null
+++ b/dom/system/mac/moz.build
@@ -0,0 +1,14 @@
+# -*- 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',
+]
+
diff --git a/dom/system/moz.build b/dom/system/moz.build
index 31097d2481..7e42761e5e 100644
--- a/dom/system/moz.build
+++ b/dom/system/moz.build
@@ -7,6 +7,8 @@ toolkit = CONFIG['MOZ_WIDGET_TOOLKIT']
if toolkit == 'windows':
DIRS += ['windows']
+elif toolkit == 'cocoa':
+ DIRS += ['mac']
elif toolkit in ('gtk2', 'gtk3'):
DIRS += ['linux']
diff --git a/dom/xbl/builtin/mac/jar.mn b/dom/xbl/builtin/mac/jar.mn
new file mode 100644
index 0000000000..9f05c2dd6c
--- /dev/null
+++ b/dom/xbl/builtin/mac/jar.mn
@@ -0,0 +1,6 @@
+# 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)
diff --git a/dom/xbl/builtin/mac/moz.build b/dom/xbl/builtin/mac/moz.build
new file mode 100644
index 0000000000..635fa39c99
--- /dev/null
+++ b/dom/xbl/builtin/mac/moz.build
@@ -0,0 +1,6 @@
+# -*- 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'] \ No newline at end of file
diff --git a/dom/xbl/builtin/mac/platformHTMLBindings.xml b/dom/xbl/builtin/mac/platformHTMLBindings.xml
new file mode 100644
index 0000000000..b705923999
--- /dev/null
+++ b/dom/xbl/builtin/mac/platformHTMLBindings.xml
@@ -0,0 +1,72 @@
+<?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>
diff --git a/dom/xbl/builtin/moz.build b/dom/xbl/builtin/moz.build
index b6c41678c4..27d3e6c146 100644
--- a/dom/xbl/builtin/moz.build
+++ b/dom/xbl/builtin/moz.build
@@ -5,6 +5,8 @@
if CONFIG['OS_ARCH'] == 'WINNT':
DIRS += ['win']
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ DIRS += ['mac']
elif CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
DIRS += ['unix']
else:
diff --git a/dom/xbl/nsXBLPrototypeHandler.cpp b/dom/xbl/nsXBLPrototypeHandler.cpp
index 4872a850d7..963e6835c6 100644
--- a/dom/xbl/nsXBLPrototypeHandler.cpp
+++ b/dom/xbl/nsXBLPrototypeHandler.cpp
@@ -167,8 +167,13 @@ nsXBLPrototypeHandler::InitAccessKeys()
return;
}
- // Compiled-in defaults, in case we can't get the pref
+ // Compiled-in defaults, in case we can't get the pref --
+ // mac doesn't have menu shortcuts, other platforms use alt.
+#ifdef XP_MACOSX
+ kMenuAccessKey = 0;
+#else
kMenuAccessKey = nsIDOMKeyEvent::DOM_VK_ALT;
+#endif
// Get the menu access key value from prefs, overriding the default:
kMenuAccessKey =
diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp
index 93d422d1e9..2ae03e0b17 100644
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -523,6 +523,18 @@ nsXULElement::IsFocusableInternal(int32_t *aTabIndex, bool aWithMouse)
// elements are not focusable by default
bool shouldFocus = false;
+#ifdef XP_MACOSX
+ // on Mac, mouse interactions only focus the element if it's a list,
+ // or if it's a remote target, since the remote target must handle
+ // the focus.
+ if (aWithMouse &&
+ IsNonList(mNodeInfo) &&
+ !EventStateManager::IsRemoteTarget(this))
+ {
+ return false;
+ }
+#endif
+
nsCOMPtr<nsIDOMXULControlElement> xulControl = do_QueryObject(this);
if (xulControl) {
// a disabled element cannot be focused and is not part of the tab order