diff options
author | Brian Smith <brian@dbsoft.org> | 2022-04-26 11:24:42 -0500 |
---|---|---|
committer | Brian Smith <brian@dbsoft.org> | 2022-04-26 11:24:42 -0500 |
commit | c82b759d56454bcd3eac54c484569e1239f7d1cc (patch) | |
tree | 14b786bac9fd295d1fca6bd32683652b7ac9ef7e | |
parent | 9e2a89c71ddf67975da35eb100673f6b5546f292 (diff) | |
download | uxp-c82b759d56454bcd3eac54c484569e1239f7d1cc.tar.gz |
Issue #1829 - Revert "Issue #1751 -- Remove XP_MACOSX conditionals from /dom"
This reverts commit 0dd3424f774954627d6f53df9fb47379d9b5c871.
71 files changed, 5355 insertions, 105 deletions
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index 5ad763af76..096f9794a9 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -1393,6 +1393,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/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 cb586d68ac..5e75d09349 100644 --- a/dom/html/HTMLImageElement.cpp +++ b/dom/html/HTMLImageElement.cpp @@ -599,6 +599,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/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/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/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/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/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(¤tLevel); +#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/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 e96ebb1473..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 @@ -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,15 +1052,110 @@ 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, + int16_t* handled) +{ 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 @@ -3260,6 +3564,60 @@ 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; } @@ -3304,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. @@ -3324,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 @@ -4294,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 e07431c52a..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 @@ -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 85c95077aa..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) { @@ -447,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; @@ -476,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); @@ -486,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 @@ -841,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(); @@ -922,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; @@ -952,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(); @@ -961,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; @@ -983,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; } @@ -995,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() @@ -1254,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; @@ -1421,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; @@ -1514,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 e04e906382..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,24 +87,24 @@ 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 @@ -151,6 +156,11 @@ NPPVariableToString(NPPVariable aVar) VARSTR(NPPVpluginWantsAllNetworkStreams); +#ifdef XP_MACOSX + VARSTR(NPPVpluginDrawingModel); + VARSTR(NPPVpluginEventModel); +#endif + #ifdef XP_WIN VARSTR(NPPVpluginRequiresAudioDeviceChanges); #endif @@ -347,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 } @@ -372,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) && @@ -380,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; @@ -393,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; @@ -412,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> { @@ -424,6 +589,7 @@ struct ParamTraits<NSCursorInfo> return false; } }; +#endif // #ifdef XP_MACOSX template <> struct ParamTraits<mozilla::plugins::IPCByteRange> @@ -566,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/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 |