drop the suggestions if search engine not available
MozReview-Commit-ID: 6WxT1J5bWoJ
new file mode 100644
--- /dev/null
+++ b/browser/locales/searchjson.py
@@ -0,0 +1,23 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys
+import json
+
+engines = []
+
+locale = sys.argv[2]
+output_file = sys.argv[3]
+
+output = open(output_file, 'w')
+
+with open(sys.argv[1]) as f:
+ searchinfo = json.load(f)
+
+if locale in searchinfo["locales"]:
+ output.write(json.dumps(searchinfo["locales"][locale]))
+else:
+ output.write(json.dumps(searchinfo["default"]))
+
+output.close();
new file mode 100644
--- /dev/null
+++ b/browser/locales/searchplugins.py
@@ -0,0 +1,21 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys
+import json
+
+engines = []
+
+locale = sys.argv[2]
+
+with open(sys.argv[1]) as f:
+ searchinfo = json.load(f)
+
+if locale in searchinfo["locales"]:
+ for region in searchinfo["locales"][locale]:
+ engines = list(set(engines)|set(searchinfo["locales"][locale][region]["visibleDefaultEngines"]))
+else:
+ engines = searchinfo["default"]["visibleDefaultEngines"]
+
+print '\n'.join(engines)
new file mode 100644
--- /dev/null
+++ b/devtools/client/locales/en-US/promisedebugger.dtd
@@ -0,0 +1,15 @@
+<!-- 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/. -->
+
+<!-- LOCALIZATION NOTE : FILE This file contains the Promise debugger panel
+ strings. The Promise debugger panel is part of the debugger -->
+<!-- LOCALIZATION NOTE : FILE Do not translate commandkey -->
+
+<!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
+ - keep it in English, or another language commonly spoken among web developers.
+ - You want to make that choice consistent across the developer tools.
+ - A good criteria is the language in which you'd find the best
+ - documentation on web development on the web. -->
+
+<!ENTITY title "Promise Debugger">
new file mode 100644
--- /dev/null
+++ b/devtools/client/promisedebugger/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/promisedebugger/promise-controller.js
@@ -0,0 +1,102 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+/* global PromisesPanel */
+
+"use strict";
+
+var { utils: Cu } = Components;
+const { loader, require } =
+ Cu.import("resource://devtools/shared/Loader.jsm", {});
+
+const { Task } = require("devtools/shared/task");
+
+loader.lazyRequireGetter(this, "promise");
+loader.lazyRequireGetter(this, "EventEmitter",
+ "devtools/shared/event-emitter");
+loader.lazyRequireGetter(this, "DevToolsUtils",
+ "devtools/shared/DevToolsUtils");
+loader.lazyRequireGetter(this, "PromisesFront",
+ "devtools/server/actors/promises", true);
+
+// Global toolbox, set when startup is called.
+var gToolbox;
+
+/**
+ * Initialize the promise debugger controller and view upon loading the iframe.
+ */
+var startup = Task.async(function* (toolbox) {
+ gToolbox = toolbox;
+
+ yield PromisesController.initialize(toolbox);
+ yield PromisesPanel.initialize();
+});
+
+/**
+ * Destroy the promise debugger controller and view when unloading the iframe.
+ */
+var shutdown = Task.async(function* () {
+ yield PromisesController.destroy();
+ yield PromisesPanel.destroy();
+
+ gToolbox = null;
+});
+
+function setPanel(toolbox) {
+ return startup(toolbox).catch(e =>
+ DevToolsUtils.reportException("setPanel", e));
+}
+
+function destroy() {
+ return shutdown().catch(e => DevToolsUtils.reportException("destroy", e));
+}
+
+/**
+ * The promisedebugger controller's job is to retrieve PromisesFronts from the
+ * server.
+ */
+var PromisesController = {
+ initialize: Task.async(function* () {
+ if (this.initialized) {
+ return this.initialized.promise;
+ }
+
+ this.initialized = promise.defer();
+
+ let target = gToolbox.target;
+ this.promisesFront = new PromisesFront(target.client, target.form);
+ yield this.promisesFront.attach();
+
+ if (this.destroyed) {
+ console.warn("Could not fully initialize the PromisesController");
+ return null;
+ }
+
+ this.initialized.resolve();
+ }),
+
+ destroy: Task.async(function* () {
+ if (!this.initialized) {
+ return null;
+ }
+
+ if (this.destroyed) {
+ return this.destroyed.promise;
+ }
+
+ this.destroyed = promise.defer();
+
+ if (this.promisesFront) {
+ yield this.promisesFront.detach();
+ this.promisesFront.destroy();
+ this.promisesFront = null;
+ }
+
+ this.destroyed.resolve();
+ }),
+};
+
+EventEmitter.decorate(PromisesController);
new file mode 100644
--- /dev/null
+++ b/devtools/client/promisedebugger/promise-debugger.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mkozilla 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/. -->
+
+<!DOCTYPE html [
+ <!ENTITY % promisedebuggerDTD SYSTEM "chrome://devtools/locale/promisedebugger.dtd">
+ %promisedebuggerDTD;
+]>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>&title;</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <script type="application/javascript;version=1.8" src="chrome://devtools/content/shared/theme-switching.js"/>
+ </head>
+ <body class="devtools-monospace" role="application">
+ <script type="application/javascript;version=1.8" src="promise-controller.js"></script>
+ <script type="application/javascript;version=1.8" src="promise-panel.js"></script>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/promisedebugger/promise-panel.js
@@ -0,0 +1,45 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+/* global PromisesController, promise */
+/* import-globals-from promise-controller.js */
+
+"use strict";
+
+/**
+ * The main promise debugger UI.
+ */
+var PromisesPanel = {
+ PANEL_INITIALIZED: "panel-initialized",
+
+ initialize: Task.async(function* () {
+ if (PromisesController.destroyed) {
+ return null;
+ }
+ if (this.initialized) {
+ return this.initialized.promise;
+ }
+ this.initialized = promise.defer();
+
+ this.initialized.resolve();
+
+ this.emit(this.PANEL_INITIALIZED);
+ }),
+
+ destroy: Task.async(function* () {
+ if (!this.initialized) {
+ return null;
+ }
+ if (this.destroyed) {
+ return this.destroyed.promise;
+ }
+ this.destroyed = promise.defer();
+
+ this.destroyed.resolve();
+ }),
+};
+
+EventEmitter.decorate(PromisesPanel);
new file mode 100644
--- /dev/null
+++ b/devtools/client/promisedebugger/test/.eslintrc.js
@@ -0,0 +1,6 @@
+"use strict";
+
+module.exports = {
+ // Extend from the shared list of defined globals for mochitests.
+ "extends": "../../../.eslintrc.mochitests.js"
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/promisedebugger/test/head.js
@@ -0,0 +1,4 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
new file mode 100644
--- /dev/null
+++ b/dom/ipc/nsIBrowser.idl
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsISupports.idl"
+
+interface nsIDOMElement;
+
+[scriptable, uuid(14e5a0cb-e223-4202-95e8-fe53275193ea)]
+interface nsIBrowser : nsISupports
+{
+ /**
+ * Gets a related browser for a given browser (if any). If this exists, then
+ * we should attempt to use the same content parent as its frameLoader
+ * for any new tab parents.
+ */
+ readonly attribute nsIDOMElement relatedBrowser;
+
+ /*
+ * Called by the child to inform the parent that links are dropped into
+ * content area.
+ *
+ * @param linksCount length of links
+ * @param links a flat array of url, name, and type for each link
+ */
+ void dropLinks(in unsigned long linksCount,
+ [array, size_is(linksCount)] in wstring links);
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerMessageEvent.cpp
@@ -0,0 +1,166 @@
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/ServiceWorkerMessageEvent.h"
+#include "mozilla/dom/ServiceWorkerMessageEventBinding.h"
+#include "mozilla/dom/MessagePort.h"
+#include "mozilla/dom/MessagePortBinding.h"
+
+#include "mozilla/HoldDropJSObjects.h"
+#include "jsapi.h"
+
+#include "ServiceWorker.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(ServiceWorkerMessageEvent)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ServiceWorkerMessageEvent, Event)
+ tmp->mData.setUndefined();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mServiceWorker)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagePort)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mPorts)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServiceWorkerMessageEvent, Event)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorker)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagePort)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPorts)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ServiceWorkerMessageEvent, Event)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mData)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerMessageEvent)
+NS_INTERFACE_MAP_END_INHERITING(Event)
+
+NS_IMPL_ADDREF_INHERITED(ServiceWorkerMessageEvent, Event)
+NS_IMPL_RELEASE_INHERITED(ServiceWorkerMessageEvent, Event)
+
+ServiceWorkerMessageEvent::ServiceWorkerMessageEvent(EventTarget* aOwner,
+ nsPresContext* aPresContext,
+ WidgetEvent* aEvent)
+ : Event(aOwner, aPresContext, aEvent)
+ , mData(JS::UndefinedValue())
+{
+ mozilla::HoldJSObjects(this);
+}
+
+ServiceWorkerMessageEvent::~ServiceWorkerMessageEvent()
+{
+ mData.setUndefined();
+ DropJSObjects(this);
+}
+
+JSObject*
+ServiceWorkerMessageEvent::WrapObjectInternal(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto)
+{
+ return mozilla::dom::ServiceWorkerMessageEventBinding::Wrap(aCx, this, aGivenProto);
+}
+
+
+void
+ServiceWorkerMessageEvent::GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aData,
+ ErrorResult& aRv) const
+{
+ aData.set(mData);
+ if (!JS_WrapValue(aCx, aData)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ }
+}
+
+void
+ServiceWorkerMessageEvent::GetOrigin(nsAString& aOrigin) const
+{
+ aOrigin = mOrigin;
+}
+
+void
+ServiceWorkerMessageEvent::GetLastEventId(nsAString& aLastEventId) const
+{
+ aLastEventId = mLastEventId;
+}
+
+void
+ServiceWorkerMessageEvent::GetSource(Nullable<OwningServiceWorkerOrMessagePort>& aValue) const
+{
+ if (mServiceWorker) {
+ aValue.SetValue().SetAsServiceWorker() = mServiceWorker;
+ } else if (mMessagePort) {
+ aValue.SetValue().SetAsMessagePort() = mMessagePort;
+ }
+}
+
+void
+ServiceWorkerMessageEvent::SetSource(mozilla::dom::MessagePort* aPort)
+{
+ mMessagePort = aPort;
+}
+
+void
+ServiceWorkerMessageEvent::SetSource(workers::ServiceWorker* aServiceWorker)
+{
+ mServiceWorker = aServiceWorker;
+}
+
+void
+ServiceWorkerMessageEvent::GetPorts(nsTArray<RefPtr<MessagePort>>& aPorts)
+{
+ aPorts = mPorts;
+}
+
+void
+ServiceWorkerMessageEvent::SetPorts(nsTArray<RefPtr<MessagePort>>&& aPorts)
+{
+ MOZ_ASSERT(mPorts.IsEmpty());
+ mPorts = Move(aPorts);
+}
+
+/* static */ already_AddRefed<ServiceWorkerMessageEvent>
+ServiceWorkerMessageEvent::Constructor(const GlobalObject& aGlobal,
+ const nsAString& aType,
+ const ServiceWorkerMessageEventInit& aParam,
+ ErrorResult& aRv)
+{
+ nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
+ return Constructor(t, aType, aParam, aRv);
+}
+
+/* static */ already_AddRefed<ServiceWorkerMessageEvent>
+ServiceWorkerMessageEvent::Constructor(EventTarget* aEventTarget,
+ const nsAString& aType,
+ const ServiceWorkerMessageEventInit& aParam,
+ ErrorResult& aRv)
+{
+ RefPtr<ServiceWorkerMessageEvent> event =
+ new ServiceWorkerMessageEvent(aEventTarget, nullptr, nullptr);
+
+ event->InitEvent(aType, aParam.mBubbles, aParam.mCancelable);
+
+ bool trusted = event->Init(aEventTarget);
+ event->SetTrusted(trusted);
+
+ event->mData = aParam.mData;
+ event->mOrigin = aParam.mOrigin;
+ event->mLastEventId = aParam.mLastEventId;
+
+ if (!aParam.mSource.IsNull()) {
+ if (aParam.mSource.Value().IsServiceWorker()) {
+ event->mServiceWorker = aParam.mSource.Value().GetAsServiceWorker();
+ } else if (aParam.mSource.Value().IsMessagePort()) {
+ event->mMessagePort = aParam.mSource.Value().GetAsMessagePort();
+ }
+ }
+
+ event->mPorts.AppendElements(aParam.mPorts);
+
+ return event.forget();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerMessageEvent.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_serviceworkermessageevent_h__
+#define mozilla_dom_serviceworkermessageevent_h__
+
+#include "mozilla/dom/Event.h"
+
+namespace mozilla {
+namespace dom {
+
+struct ServiceWorkerMessageEventInit;
+class MessagePort;
+class OwningServiceWorkerOrMessagePort;
+
+namespace workers {
+
+class ServiceWorker;
+
+}
+
+class ServiceWorkerMessageEvent final : public Event
+{
+public:
+ ServiceWorkerMessageEvent(EventTarget* aOwner,
+ nsPresContext* aPresContext,
+ WidgetEvent* aEvent);
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ServiceWorkerMessageEvent, Event)
+
+ // Forward to base class
+ NS_FORWARD_TO_EVENT
+
+ virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+ void GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aData,
+ ErrorResult& aRv) const;
+
+ void GetOrigin(nsAString& aOrigin) const;
+
+ void GetLastEventId(nsAString& aLastEventId) const;
+
+ void GetSource(Nullable<OwningServiceWorkerOrMessagePort>& aValue) const;
+
+ void GetPorts(nsTArray<RefPtr<MessagePort>>& aPorts);
+
+ void SetSource(mozilla::dom::MessagePort* aPort);
+
+ void SetSource(workers::ServiceWorker* aServiceWorker);
+
+ void SetPorts(nsTArray<RefPtr<MessagePort>>&& aPorts);
+
+ static already_AddRefed<ServiceWorkerMessageEvent>
+ Constructor(const GlobalObject& aGlobal,
+ const nsAString& aType,
+ const ServiceWorkerMessageEventInit& aEventInit,
+ ErrorResult& aRv);
+
+ static already_AddRefed<ServiceWorkerMessageEvent>
+ Constructor(EventTarget* aEventTarget,
+ const nsAString& aType,
+ const ServiceWorkerMessageEventInit& aEventInit,
+ ErrorResult& aRv);
+
+protected:
+ ~ServiceWorkerMessageEvent();
+
+private:
+ JS::Heap<JS::Value> mData;
+ nsString mOrigin;
+ nsString mLastEventId;
+ RefPtr<workers::ServiceWorker> mServiceWorker;
+ RefPtr<MessagePort> mMessagePort;
+ nsTArray<RefPtr<MessagePort>> mPorts;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_workers_serviceworkermessageevent_h__ */
+
new file mode 100644
--- /dev/null
+++ b/gfx/gl/SharedSurfaceGralloc.cpp
@@ -0,0 +1,294 @@
+/* -*- Mode: c++; c-basic-offset: 4; 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/. */
+
+#include "mozilla/Preferences.h"
+#include "mozilla/UniquePtr.h"
+
+#include "SharedSurfaceGralloc.h"
+
+#include "GLContext.h"
+#include "SharedSurface.h"
+#include "GLLibraryEGL.h"
+#include "mozilla/layers/GrallocTextureClient.h"
+#include "mozilla/layers/ShadowLayers.h"
+
+#include "ui/GraphicBuffer.h"
+#include "../layers/ipc/ShadowLayers.h"
+#include "ScopedGLHelpers.h"
+
+#include "gfxPlatform.h"
+#include "gfxPrefs.h"
+
+#define DEBUG_GRALLOC
+#ifdef DEBUG_GRALLOC
+#define DEBUG_PRINT(...) do { printf_stderr(__VA_ARGS__); } while (0)
+#else
+#define DEBUG_PRINT(...) do { } while (0)
+#endif
+
+namespace mozilla {
+namespace gl {
+
+using namespace mozilla::layers;
+using namespace android;
+
+SurfaceFactory_Gralloc::SurfaceFactory_Gralloc(GLContext* prodGL, const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags)
+ : SurfaceFactory(SharedSurfaceType::Gralloc, prodGL, caps, allocator, flags)
+{
+ MOZ_ASSERT(mAllocator);
+}
+
+/*static*/ UniquePtr<SharedSurface_Gralloc>
+SharedSurface_Gralloc::Create(GLContext* prodGL,
+ const GLFormats& formats,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ layers::TextureFlags flags,
+ LayersIPCChannel* allocator)
+{
+ GLLibraryEGL* egl = &sEGLLibrary;
+ MOZ_ASSERT(egl);
+
+ UniquePtr<SharedSurface_Gralloc> ret;
+
+ DEBUG_PRINT("SharedSurface_Gralloc::Create -------\n");
+
+ if (!HasExtensions(egl, prodGL))
+ return Move(ret);
+
+ gfxContentType type = hasAlpha ? gfxContentType::COLOR_ALPHA
+ : gfxContentType::COLOR;
+
+ GrallocTextureData* texData = GrallocTextureData::CreateForGLRendering(
+ size, gfxPlatform::GetPlatform()->Optimal2DFormatForContent(type), allocator
+ );
+
+ if (!texData) {
+ return Move(ret);
+ }
+
+ RefPtr<TextureClient> grallocTC = new TextureClient(texData, flags, allocator);
+
+ sp<GraphicBuffer> buffer = texData->GetGraphicBuffer();
+
+ EGLDisplay display = egl->Display();
+ EGLClientBuffer clientBuffer = buffer->getNativeBuffer();
+ EGLint attrs[] = {
+ LOCAL_EGL_NONE, LOCAL_EGL_NONE
+ };
+ EGLImage image = egl->fCreateImage(display,
+ EGL_NO_CONTEXT,
+ LOCAL_EGL_NATIVE_BUFFER_ANDROID,
+ clientBuffer, attrs);
+ if (!image) {
+ return Move(ret);
+ }
+
+ prodGL->MakeCurrent();
+ GLuint prodTex = 0;
+ prodGL->fGenTextures(1, &prodTex);
+ ScopedBindTexture autoTex(prodGL, prodTex);
+
+ prodGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
+ prodGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
+ prodGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+ prodGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+
+ prodGL->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, image);
+
+ egl->fDestroyImage(display, image);
+
+ ret.reset( new SharedSurface_Gralloc(prodGL, size, hasAlpha, egl,
+ allocator, grallocTC,
+ prodTex) );
+
+ DEBUG_PRINT("SharedSurface_Gralloc::Create: success -- surface %p,"
+ " GraphicBuffer %p.\n",
+ ret.get(), buffer.get());
+
+ return Move(ret);
+}
+
+
+SharedSurface_Gralloc::SharedSurface_Gralloc(GLContext* prodGL,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ GLLibraryEGL* egl,
+ layers::LayersIPCChannel* allocator,
+ layers::TextureClient* textureClient,
+ GLuint prodTex)
+ : SharedSurface(SharedSurfaceType::Gralloc,
+ AttachmentType::GLTexture,
+ prodGL,
+ size,
+ hasAlpha,
+ true)
+ , mEGL(egl)
+ , mSync(0)
+ , mAllocator(allocator)
+ , mTextureClient(textureClient)
+ , mProdTex(prodTex)
+{
+}
+
+bool
+SharedSurface_Gralloc::HasExtensions(GLLibraryEGL* egl, GLContext* gl)
+{
+ return egl->HasKHRImageBase() &&
+ gl->IsExtensionSupported(GLContext::OES_EGL_image);
+}
+
+SharedSurface_Gralloc::~SharedSurface_Gralloc()
+{
+ DEBUG_PRINT("[SharedSurface_Gralloc %p] destroyed\n", this);
+
+ if (!mGL || !mGL->MakeCurrent())
+ return;
+
+ mGL->fDeleteTextures(1, &mProdTex);
+
+ if (mSync) {
+ MOZ_ALWAYS_TRUE( mEGL->fDestroySync(mEGL->Display(), mSync) );
+ mSync = 0;
+ }
+}
+
+void
+SharedSurface_Gralloc::ProducerReleaseImpl()
+{
+ if (mSync) {
+ MOZ_ALWAYS_TRUE( mEGL->fDestroySync(mEGL->Display(), mSync) );
+ mSync = 0;
+ }
+
+ bool disableSyncFence = false;
+ // Disable sync fence on AdrenoTM200.
+ // AdrenoTM200's sync fence does not work correctly. See Bug 1022205.
+ if (mGL->Renderer() == GLRenderer::AdrenoTM200) {
+ disableSyncFence = true;
+ }
+
+ // When Android native fences are available, try
+ // them first since they're more likely to work.
+ // Android native fences are also likely to perform better.
+ if (!disableSyncFence &&
+ mEGL->IsExtensionSupported(GLLibraryEGL::ANDROID_native_fence_sync))
+ {
+ mGL->MakeCurrent();
+ EGLSync sync = mEGL->fCreateSync(mEGL->Display(),
+ LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID,
+ nullptr);
+ if (sync) {
+ mGL->fFlush();
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+ int fenceFd = mEGL->fDupNativeFenceFDANDROID(mEGL->Display(), sync);
+ if (fenceFd != -1) {
+ mEGL->fDestroySync(mEGL->Display(), sync);
+ mTextureClient->SetAcquireFenceHandle(FenceHandle(new FenceHandle::FdObj(fenceFd)));
+ } else {
+ mSync = sync;
+ }
+#else
+ mSync = sync;
+#endif
+ return;
+ }
+ }
+
+ if (!disableSyncFence &&
+ mEGL->IsExtensionSupported(GLLibraryEGL::KHR_fence_sync))
+ {
+ mGL->MakeCurrent();
+ mSync = mEGL->fCreateSync(mEGL->Display(),
+ LOCAL_EGL_SYNC_FENCE,
+ nullptr);
+ if (mSync) {
+ mGL->fFlush();
+ return;
+ }
+ }
+
+ // We should be able to rely on genlock write locks/read locks.
+ // But they're broken on some configs, and even a glFinish doesn't
+ // work. glReadPixels seems to, though.
+ if (gfxPrefs::GrallocFenceWithReadPixels()) {
+ mGL->MakeCurrent();
+ UniquePtr<char[]> buf = MakeUnique<char[]>(4);
+ mGL->fReadPixels(0, 0, 1, 1, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, buf.get());
+ }
+}
+
+void
+SharedSurface_Gralloc::WaitForBufferOwnership()
+{
+ mTextureClient->WaitForBufferOwnership();
+}
+
+bool
+SharedSurface_Gralloc::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
+{
+ mTextureClient->mWorkaroundAnnoyingSharedSurfaceOwnershipIssues = true;
+ return mTextureClient->ToSurfaceDescriptor(*out_descriptor);
+}
+
+bool
+SharedSurface_Gralloc::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
+{
+ MOZ_ASSERT(out_surface);
+ sp<GraphicBuffer> buffer = static_cast<GrallocTextureData*>(
+ mTextureClient->GetInternalData()
+ )->GetGraphicBuffer();
+
+ const uint8_t* grallocData = nullptr;
+ auto result = buffer->lock(
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER,
+ const_cast<void**>(reinterpret_cast<const void**>(&grallocData))
+ );
+
+ if (result == BAD_VALUE) {
+ return false;
+ }
+
+ gfx::DataSourceSurface::ScopedMap map(out_surface, gfx::DataSourceSurface::WRITE);
+ if (!map.IsMapped()) {
+ buffer->unlock();
+ return false;
+ }
+
+ uint32_t stride = buffer->getStride() * android::bytesPerPixel(buffer->getPixelFormat());
+ uint32_t height = buffer->getHeight();
+ uint32_t width = buffer->getWidth();
+ for (uint32_t i = 0; i < height; i++) {
+ memcpy(map.GetData() + i * map.GetStride(),
+ grallocData + i * stride, width * 4);
+ }
+
+ buffer->unlock();
+
+ android::PixelFormat srcFormat = buffer->getPixelFormat();
+ MOZ_ASSERT(srcFormat == PIXEL_FORMAT_RGBA_8888 ||
+ srcFormat == PIXEL_FORMAT_BGRA_8888 ||
+ srcFormat == PIXEL_FORMAT_RGBX_8888);
+ bool isSrcRGB = srcFormat == PIXEL_FORMAT_RGBA_8888 ||
+ srcFormat == PIXEL_FORMAT_RGBX_8888;
+
+ gfx::SurfaceFormat destFormat = out_surface->GetFormat();
+ MOZ_ASSERT(destFormat == gfx::SurfaceFormat::R8G8B8X8 ||
+ destFormat == gfx::SurfaceFormat::R8G8B8A8 ||
+ destFormat == gfx::SurfaceFormat::B8G8R8X8 ||
+ destFormat == gfx::SurfaceFormat::B8G8R8A8);
+ bool isDestRGB = destFormat == gfx::SurfaceFormat::R8G8B8X8 ||
+ destFormat == gfx::SurfaceFormat::R8G8B8A8;
+
+ if (isSrcRGB != isDestRGB) {
+ SwapRAndBComponents(out_surface);
+ }
+ return true;
+}
+
+} // namespace gl
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/gl/SharedSurfaceGralloc.h
@@ -0,0 +1,104 @@
+/* -*- Mode: c++; c-basic-offset: 4; 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/. */
+
+#ifndef SHARED_SURFACE_GRALLOC_H_
+#define SHARED_SURFACE_GRALLOC_H_
+
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "SharedSurface.h"
+
+namespace mozilla {
+namespace layers {
+class LayersIPCChannel;
+class TextureClient;
+}
+
+namespace gl {
+class GLContext;
+class GLLibraryEGL;
+
+class SharedSurface_Gralloc
+ : public SharedSurface
+{
+public:
+ static UniquePtr<SharedSurface_Gralloc> Create(GLContext* prodGL,
+ const GLFormats& formats,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ layers::TextureFlags flags,
+ layers::LayersIPCChannel* allocator);
+
+ static SharedSurface_Gralloc* Cast(SharedSurface* surf) {
+ MOZ_ASSERT(surf->mType == SharedSurfaceType::Gralloc);
+
+ return (SharedSurface_Gralloc*)surf;
+ }
+
+protected:
+ GLLibraryEGL* const mEGL;
+ EGLSync mSync;
+ RefPtr<layers::LayersIPCChannel> mAllocator;
+ RefPtr<layers::TextureClient> mTextureClient;
+ const GLuint mProdTex;
+
+ SharedSurface_Gralloc(GLContext* prodGL,
+ const gfx::IntSize& size,
+ bool hasAlpha,
+ GLLibraryEGL* egl,
+ layers::LayersIPCChannel* allocator,
+ layers::TextureClient* textureClient,
+ GLuint prodTex);
+
+ static bool HasExtensions(GLLibraryEGL* egl, GLContext* gl);
+
+public:
+ virtual ~SharedSurface_Gralloc();
+
+ virtual void ProducerAcquireImpl() override {}
+ virtual void ProducerReleaseImpl() override;
+
+ virtual void WaitForBufferOwnership() override;
+
+ virtual void LockProdImpl() override {}
+ virtual void UnlockProdImpl() override {}
+
+ virtual GLuint ProdTexture() override {
+ return mProdTex;
+ }
+
+ layers::TextureClient* GetTextureClient() {
+ return mTextureClient;
+ }
+
+ virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
+
+ virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
+};
+
+class SurfaceFactory_Gralloc
+ : public SurfaceFactory
+{
+public:
+ SurfaceFactory_Gralloc(GLContext* prodGL, const SurfaceCaps& caps,
+ const RefPtr<layers::LayersIPCChannel>& allocator,
+ const layers::TextureFlags& flags);
+
+ virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) override {
+ bool hasAlpha = mReadCaps.alpha;
+
+ UniquePtr<SharedSurface> ret;
+ if (mAllocator) {
+ ret = SharedSurface_Gralloc::Create(mGL, mFormats, size, hasAlpha,
+ mFlags, mAllocator);
+ }
+ return Move(ret);
+ }
+};
+
+} /* namespace gl */
+} /* namespace mozilla */
+
+#endif /* SHARED_SURFACE_GRALLOC_H_ */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/harfbuzz.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: harfbuzz
+Description: Text shaping library
+Version: @VERSION@
+
+Libs: -L${libdir} -lharfbuzz
+Cflags: -I${includedir}/harfbuzz
new file mode 100644
--- /dev/null
+++ b/gfx/layers/GrallocImages.cpp
@@ -0,0 +1,478 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "GrallocImages.h"
+#include <stddef.h> // for size_t
+#include <stdint.h> // for int8_t, uint8_t, uint32_t, etc
+#include "nsDebug.h" // for NS_WARNING, NS_PRECONDITION
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/GrallocTextureClient.h"
+#include "gfx2DGlue.h"
+#include "YCbCrUtils.h" // for YCbCr conversions
+
+#include <ColorConverter.h>
+#include <OMX_IVCommon.h>
+
+
+using namespace mozilla::ipc;
+using namespace android;
+
+#define ALIGN(x, align) ((x + align - 1) & ~(align - 1))
+
+namespace mozilla {
+namespace layers {
+
+int32_t GrallocImage::sColorIdMap[] = {
+ HAL_PIXEL_FORMAT_YCbCr_420_P, OMX_COLOR_FormatYUV420Planar,
+ HAL_PIXEL_FORMAT_YCbCr_422_P, OMX_COLOR_FormatYUV422Planar,
+ HAL_PIXEL_FORMAT_YCbCr_420_SP, OMX_COLOR_FormatYUV420SemiPlanar,
+ HAL_PIXEL_FORMAT_YCrCb_420_SP, -1,
+ HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO, -1,
+ HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED, HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED,
+ HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS, HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS,
+ HAL_PIXEL_FORMAT_YV12, OMX_COLOR_FormatYUV420Planar,
+ HAL_PIXEL_FORMAT_RGBA_8888, -1,
+ 0, 0
+};
+
+struct GraphicBufferAutoUnlock {
+ android::sp<GraphicBuffer> mGraphicBuffer;
+
+ GraphicBufferAutoUnlock(android::sp<GraphicBuffer>& aGraphicBuffer)
+ : mGraphicBuffer(aGraphicBuffer) { }
+
+ ~GraphicBufferAutoUnlock() { mGraphicBuffer->unlock(); }
+};
+
+GrallocImage::GrallocImage()
+ : RecyclingPlanarYCbCrImage(nullptr)
+{
+ mFormat = ImageFormat::GRALLOC_PLANAR_YCBCR;
+}
+
+GrallocImage::~GrallocImage()
+{
+}
+
+bool
+GrallocImage::SetData(const Data& aData)
+{
+ MOZ_ASSERT(!mTextureClient, "TextureClient is already set");
+ NS_PRECONDITION(aData.mYSize.width % 2 == 0, "Image should have even width");
+ NS_PRECONDITION(aData.mYSize.height % 2 == 0, "Image should have even height");
+ NS_PRECONDITION(aData.mYStride % 16 == 0, "Image should have stride of multiple of 16 pixels");
+
+ mData = aData;
+ mSize = aData.mPicSize;
+
+ if (gfxPlatform::GetPlatform()->IsInGonkEmulator()) {
+ // Emulator does not support HAL_PIXEL_FORMAT_YV12.
+ return false;
+ }
+
+ RefPtr<LayersIPCChannel> allocator = ImageBridgeChild::GetSingleton();
+ GrallocTextureData* texData = GrallocTextureData::Create(mData.mYSize, HAL_PIXEL_FORMAT_YV12,
+ gfx::BackendType::NONE,
+ GraphicBuffer::USAGE_SW_READ_OFTEN |
+ GraphicBuffer::USAGE_SW_WRITE_OFTEN |
+ GraphicBuffer::USAGE_HW_TEXTURE,
+ allocator
+ );
+
+ if (!texData) {
+ return false;
+ }
+
+ mTextureClient = new TextureClient(texData, TextureFlags::DEFAULT, allocator);
+ sp<GraphicBuffer> graphicBuffer = texData->GetGraphicBuffer();
+
+ void* vaddr;
+ if (graphicBuffer->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN,
+ &vaddr) != OK) {
+ return false;
+ }
+
+ uint8_t* yChannel = static_cast<uint8_t*>(vaddr);
+ gfx::IntSize ySize = aData.mYSize;
+ int32_t yStride = graphicBuffer->getStride();
+
+ uint8_t* vChannel = yChannel + (yStride * ySize.height);
+ gfx::IntSize uvSize = gfx::IntSize(ySize.width / 2,
+ ySize.height / 2);
+ // Align to 16 bytes boundary
+ int32_t uvStride = ((yStride / 2) + 15) & ~0x0F;
+ uint8_t* uChannel = vChannel + (uvStride * uvSize.height);
+
+ // Memory outside of the image width may not writable. If the stride
+ // equals to the image width then we can use only one copy.
+ if (yStride == mData.mYStride &&
+ yStride == ySize.width) {
+ memcpy(yChannel, mData.mYChannel, yStride * ySize.height);
+ } else {
+ for (int i = 0; i < ySize.height; i++) {
+ memcpy(yChannel + i * yStride,
+ mData.mYChannel + i * mData.mYStride,
+ ySize.width);
+ }
+ }
+ if (uvStride == mData.mCbCrStride &&
+ uvStride == uvSize.width) {
+ memcpy(uChannel, mData.mCbChannel, uvStride * uvSize.height);
+ memcpy(vChannel, mData.mCrChannel, uvStride * uvSize.height);
+ } else {
+ for (int i = 0; i < uvSize.height; i++) {
+ memcpy(uChannel + i * uvStride,
+ mData.mCbChannel + i * mData.mCbCrStride,
+ uvSize.width);
+ memcpy(vChannel + i * uvStride,
+ mData.mCrChannel + i * mData.mCbCrStride,
+ uvSize.width);
+ }
+ }
+ graphicBuffer->unlock();
+ // Initialze the channels' addresses.
+ // Do not cache the addresses when gralloc buffer is not locked.
+ // gralloc hal could map gralloc buffer only when the buffer is locked,
+ // though some gralloc hals implementation maps it when it is allocated.
+ mData.mYChannel = nullptr;
+ mData.mCrChannel = nullptr;
+ mData.mCbChannel = nullptr;
+ return true;
+}
+
+void
+GrallocImage::AdoptData(TextureClient* aGraphicBuffer, const gfx::IntSize& aSize)
+{
+ mTextureClient = aGraphicBuffer;
+ mSize = aSize;
+}
+
+/**
+ * Converts YVU420 semi planar frames to RGB565, possibly taking different
+ * stride values.
+ * Needed because the Android ColorConverter class assumes that the Y and UV
+ * channels have equal stride.
+ */
+static void
+ConvertYVU420SPToRGB565(void *aYData, uint32_t aYStride,
+ void *aUData, void *aVData, uint32_t aUVStride,
+ void *aOut,
+ uint32_t aWidth, uint32_t aHeight)
+{
+ uint8_t *y = (uint8_t*)aYData;
+ bool isCbCr;
+ int8_t *uv;
+
+ if (aUData < aVData) {
+ // The color format is YCbCr
+ isCbCr = true;
+ uv = (int8_t*)aUData;
+ } else {
+ // The color format is YCrCb
+ isCbCr = false;
+ uv = (int8_t*)aVData;
+ }
+
+ uint16_t *rgb = (uint16_t*)aOut;
+
+ for (size_t i = 0; i < aHeight; i++) {
+ for (size_t j = 0; j < aWidth; j++) {
+ int8_t d, e;
+
+ if (isCbCr) {
+ d = uv[j & ~1] - 128;
+ e = uv[j | 1] - 128;
+ } else {
+ d = uv[j | 1] - 128;
+ e = uv[j & ~1] - 128;
+ }
+
+ // Constants taken from https://en.wikipedia.org/wiki/YUV
+ int32_t r = (298 * y[j] + 409 * e + 128) >> 11;
+ int32_t g = (298 * y[j] - 100 * d - 208 * e + 128) >> 10;
+ int32_t b = (298 * y[j] + 516 * d + 128) >> 11;
+
+ r = r > 0x1f ? 0x1f : r < 0 ? 0 : r;
+ g = g > 0x3f ? 0x3f : g < 0 ? 0 : g;
+ b = b > 0x1f ? 0x1f : b < 0 ? 0 : b;
+
+ *rgb++ = (uint16_t)(r << 11 | g << 5 | b);
+ }
+
+ y += aYStride;
+ if (i % 2) {
+ uv += aUVStride;
+ }
+ }
+}
+
+/**
+ * Converts the format of vendor-specific YVU420(planar and semi-planar)
+ * with the help of GraphicBuffer::lockYCbCr. In this way, we can convert
+ * the YUV color format without awaring actual definition/enumeration
+ * of vendor formats.
+ */
+static status_t
+ConvertVendorYUVFormatToRGB565(android::sp<GraphicBuffer>& aBuffer,
+ gfx::DataSourceSurface *aSurface,
+ gfx::DataSourceSurface::MappedSurface *aMappedSurface)
+{
+ status_t rv = BAD_VALUE;
+
+#if ANDROID_VERSION >= 18
+ android_ycbcr ycbcr;
+
+ // Check if the vendor provides explicit addresses of Y/Cb/Cr buffer from lockYCbCr
+ rv = aBuffer->lockYCbCr(android::GraphicBuffer::USAGE_SW_READ_OFTEN, &ycbcr);
+
+ if (rv != OK) {
+ NS_WARNING("Couldn't lock graphic buffer using lockYCbCr()");
+ return rv;
+ }
+
+ GraphicBufferAutoUnlock unlock(aBuffer);
+
+ uint32_t width = aSurface->GetSize().width;
+ uint32_t height = aSurface->GetSize().height;
+
+ if (ycbcr.chroma_step == 2) {
+ // From the system/core/include/system/graphics.h
+ // @chroma_step is the distance in bytes from one chroma pixel value to
+ // the next. This is 2 bytes for semiplanar (because chroma values are
+ // interleaved and each chroma value is one byte) and 1 for planar.
+ ConvertYVU420SPToRGB565(ycbcr.y, ycbcr.ystride,
+ ycbcr.cb, ycbcr.cr, ycbcr.cstride,
+ aMappedSurface->mData,
+ width, height);
+ } else {
+ layers::PlanarYCbCrData ycbcrData;
+ ycbcrData.mYChannel = static_cast<uint8_t*>(ycbcr.y);
+ ycbcrData.mYStride = ycbcr.ystride;
+ ycbcrData.mYSize = aSurface->GetSize();
+ ycbcrData.mCbChannel = static_cast<uint8_t*>(ycbcr.cb);
+ ycbcrData.mCrChannel = static_cast<uint8_t*>(ycbcr.cr);
+ ycbcrData.mCbCrStride = ycbcr.cstride;
+ ycbcrData.mCbCrSize = aSurface->GetSize() / 2;
+ ycbcrData.mPicSize = aSurface->GetSize();
+
+ gfx::ConvertYCbCrToRGB(ycbcrData,
+ aSurface->GetFormat(),
+ aSurface->GetSize(),
+ aMappedSurface->mData,
+ aMappedSurface->mStride);
+ }
+#endif
+
+ return rv;
+}
+
+static status_t
+ConvertOmxYUVFormatToRGB565(android::sp<GraphicBuffer>& aBuffer,
+ gfx::DataSourceSurface *aSurface,
+ gfx::DataSourceSurface::MappedSurface *aMappedSurface,
+ const layers::PlanarYCbCrData& aYcbcrData)
+{
+ uint32_t omxFormat =
+ GrallocImage::GetOmxFormat(aBuffer->getPixelFormat());
+ if (!omxFormat) {
+ NS_WARNING("Unknown color format");
+ return BAD_VALUE;
+ }
+
+ status_t rv;
+ uint8_t *buffer;
+
+ rv = aBuffer->lock(android::GraphicBuffer::USAGE_SW_READ_OFTEN,
+ reinterpret_cast<void **>(&buffer));
+ if (rv != OK) {
+ NS_WARNING("Couldn't lock graphic buffer");
+ return BAD_VALUE;
+ }
+
+ GraphicBufferAutoUnlock unlock(aBuffer);
+
+ uint32_t format = aBuffer->getPixelFormat();
+ uint32_t width = aSurface->GetSize().width;
+ uint32_t height = aSurface->GetSize().height;
+ uint32_t stride = aBuffer->getStride();
+
+ if (format == GrallocImage::HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO) {
+ // The Adreno hardware decoder aligns image dimensions to a multiple of 32,
+ // so we have to account for that here
+ uint32_t alignedWidth = ALIGN(width, 32);
+ uint32_t alignedHeight = ALIGN(height, 32);
+ uint32_t uvOffset = ALIGN(alignedHeight * alignedWidth, 4096);
+ uint32_t uvStride = 2 * ALIGN(width / 2, 32);
+ ConvertYVU420SPToRGB565(buffer, alignedWidth,
+ buffer + uvOffset + 1,
+ buffer + uvOffset,
+ uvStride,
+ aMappedSurface->mData,
+ width, height);
+ return OK;
+ }
+
+ if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
+ uint32_t uvOffset = height * stride;
+ ConvertYVU420SPToRGB565(buffer, stride,
+ buffer + uvOffset + 1,
+ buffer + uvOffset,
+ stride,
+ aMappedSurface->mData,
+ width, height);
+ return OK;
+ }
+
+ if (format == HAL_PIXEL_FORMAT_YV12) {
+ // Depend on platforms, it is possible for HW decoder to output YV12 format.
+ // It means the mData won't be configured during the SetData API because the
+ // yuv data has already stored in GraphicBuffer. Here we try to confgiure the
+ // mData if it doesn't contain valid configuration.
+ layers::PlanarYCbCrData ycbcrData = aYcbcrData;
+ if (!ycbcrData.mYChannel) {
+ ycbcrData.mYChannel = buffer;
+ ycbcrData.mYSkip = 0;
+ ycbcrData.mYStride = aBuffer->getStride();
+ ycbcrData.mYSize = aSurface->GetSize();
+ ycbcrData.mCbSkip = 0;
+ ycbcrData.mCbCrSize = aSurface->GetSize() / 2;
+ ycbcrData.mPicSize = aSurface->GetSize();
+ ycbcrData.mCrChannel = buffer + ycbcrData.mYStride * aBuffer->getHeight();
+ ycbcrData.mCrSkip = 0;
+ // Align to 16 bytes boundary
+ ycbcrData.mCbCrStride = ALIGN(ycbcrData.mYStride / 2, 16);
+ ycbcrData.mCbChannel = ycbcrData.mCrChannel + (ycbcrData.mCbCrStride * aBuffer->getHeight() / 2);
+ } else {
+ // Update channels' address.
+ // Gralloc buffer could map gralloc buffer only when the buffer is locked.
+ ycbcrData.mYChannel = buffer;
+ ycbcrData.mCrChannel = buffer + ycbcrData.mYStride * aBuffer->getHeight();
+ ycbcrData.mCbChannel = ycbcrData.mCrChannel + (ycbcrData.mCbCrStride * aBuffer->getHeight() / 2);
+ }
+ gfx::ConvertYCbCrToRGB(ycbcrData,
+ aSurface->GetFormat(),
+ aSurface->GetSize(),
+ aMappedSurface->mData,
+ aMappedSurface->mStride);
+ return OK;
+ }
+
+ if (format == HAL_PIXEL_FORMAT_RGBA_8888) {
+ uint32_t* src = (uint32_t*)(buffer);
+ uint16_t* dest = (uint16_t*)(aMappedSurface->mData);
+
+ // Convert RGBA8888 to RGB565
+ for (size_t i = 0; i < width * height; i++) {
+ uint32_t r = ((*src >> 0 ) & 0xFF);
+ uint32_t g = ((*src >> 8 ) & 0xFF);
+ uint32_t b = ((*src >> 16) & 0xFF);
+ *dest++ = ((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0);
+ src++;
+ }
+ return OK;
+ }
+
+ android::ColorConverter colorConverter((OMX_COLOR_FORMATTYPE)omxFormat,
+ OMX_COLOR_Format16bitRGB565);
+ if (!colorConverter.isValid()) {
+ NS_WARNING("Invalid color conversion");
+ return BAD_VALUE;
+ }
+
+ uint32_t pixelStride = aMappedSurface->mStride/gfx::BytesPerPixel(gfx::SurfaceFormat::R5G6B5_UINT16);
+ rv = colorConverter.convert(buffer, width, height,
+ 0, 0, width - 1, height - 1 /* source crop */,
+ aMappedSurface->mData, pixelStride, height,
+ 0, 0, width - 1, height - 1 /* dest crop */);
+ if (rv) {
+ NS_WARNING("OMX color conversion failed");
+ return BAD_VALUE;
+ }
+
+ return OK;
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+GetDataSourceSurfaceFrom(android::sp<android::GraphicBuffer>& aGraphicBuffer,
+ gfx::IntSize aSize,
+ const layers::PlanarYCbCrData& aYcbcrData)
+{
+ MOZ_ASSERT(aGraphicBuffer.get());
+
+ RefPtr<gfx::DataSourceSurface> surface =
+ gfx::Factory::CreateDataSourceSurface(aSize, gfx::SurfaceFormat::R5G6B5_UINT16);
+ if (NS_WARN_IF(!surface)) {
+ return nullptr;
+ }
+
+ gfx::DataSourceSurface::MappedSurface mappedSurface;
+ if (!surface->Map(gfx::DataSourceSurface::WRITE, &mappedSurface)) {
+ NS_WARNING("Could not map DataSourceSurface");
+ return nullptr;
+ }
+
+ int32_t rv;
+ rv = ConvertOmxYUVFormatToRGB565(aGraphicBuffer, surface, &mappedSurface, aYcbcrData);
+ if (rv == OK) {
+ surface->Unmap();
+ return surface.forget();
+ }
+
+ rv = ConvertVendorYUVFormatToRGB565(aGraphicBuffer, surface, &mappedSurface);
+ surface->Unmap();
+ if (rv != OK) {
+ NS_WARNING("Unknown color format");
+ return nullptr;
+ }
+
+ return surface.forget();
+}
+
+already_AddRefed<gfx::SourceSurface>
+GrallocImage::GetAsSourceSurface()
+{
+ if (!mTextureClient) {
+ return nullptr;
+ }
+
+ android::sp<GraphicBuffer> graphicBuffer = GetGraphicBuffer();
+
+ RefPtr<gfx::DataSourceSurface> surface =
+ GetDataSourceSurfaceFrom(graphicBuffer, mSize, mData);
+
+ return surface.forget();
+}
+
+android::sp<android::GraphicBuffer>
+GrallocImage::GetGraphicBuffer() const
+{
+ if (!mTextureClient) {
+ return nullptr;
+ }
+ return static_cast<GrallocTextureData*>(mTextureClient->GetInternalData())->GetGraphicBuffer();
+}
+
+void*
+GrallocImage::GetNativeBuffer()
+{
+ if (!mTextureClient) {
+ return nullptr;
+ }
+ android::sp<android::GraphicBuffer> graphicBuffer = GetGraphicBuffer();
+ if (!graphicBuffer.get()) {
+ return nullptr;
+ }
+ return graphicBuffer->getNativeBuffer();
+}
+
+TextureClient*
+GrallocImage::GetTextureClient(KnowsCompositor* aForwarder)
+{
+ return mTextureClient;
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/GrallocImages.h
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GRALLOCIMAGES_H
+#define GRALLOCIMAGES_H
+
+#ifdef MOZ_WIDGET_GONK
+
+#include "ImageLayers.h"
+#include "ImageContainer.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/layers/AtomicRefCountedWithFinalize.h"
+#include "mozilla/layers/FenceUtils.h"
+#include "mozilla/layers/LayersSurfaces.h"
+
+#include <ui/GraphicBuffer.h>
+
+namespace mozilla {
+namespace layers {
+
+class TextureClient;
+
+already_AddRefed<gfx::DataSourceSurface>
+GetDataSourceSurfaceFrom(android::sp<android::GraphicBuffer>& aGraphicBuffer,
+ gfx::IntSize aSize,
+ const layers::PlanarYCbCrData& aYcbcrData);
+
+/**
+ * The YUV format supported by Android HAL
+ *
+ * 4:2:0 - CbCr width and height is half that of Y.
+ *
+ * This format assumes
+ * - an even width
+ * - an even height
+ * - a horizontal stride multiple of 16 pixels
+ * - a vertical stride equal to the height
+ *
+ * y_size = stride * height
+ * c_size = ALIGN(stride/2, 16) * height/2
+ * size = y_size + c_size * 2
+ * cr_offset = y_size
+ * cb_offset = y_size + c_size
+ *
+ * The Image that is rendered is the picture region defined by
+ * mPicX, mPicY and mPicSize. The size of the rendered image is
+ * mPicSize, not mYSize or mCbCrSize.
+ */
+class GrallocImage : public RecyclingPlanarYCbCrImage
+{
+ typedef PlanarYCbCrData Data;
+ static int32_t sColorIdMap[];
+public:
+ GrallocImage();
+
+ virtual ~GrallocImage();
+
+ /**
+ * This makes a copy of the data buffers, in order to support functioning
+ * in all different layer managers.
+ */
+ virtual bool SetData(const Data& aData);
+
+ using RecyclingPlanarYCbCrImage::AdoptData;
+ /**
+ * Share the SurfaceDescriptor without making the copy, in order
+ * to support functioning in all different layer managers.
+ */
+ void AdoptData(TextureClient* aGraphicBuffer, const gfx::IntSize& aSize);
+
+ // From [android 4.0.4]/hardware/msm7k/libgralloc-qsd8k/gralloc_priv.h
+ enum {
+ /* OEM specific HAL formats */
+ HAL_PIXEL_FORMAT_YCbCr_422_P = 0x102,
+ HAL_PIXEL_FORMAT_YCbCr_420_P = 0x103,
+ HAL_PIXEL_FORMAT_YCbCr_420_SP = 0x109,
+ HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO = 0x10A,
+ HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED = 0x7FA30C03,
+ HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS = 0x7FA30C04,
+ };
+
+ enum {
+ GRALLOC_SW_UAGE = android::GraphicBuffer::USAGE_SOFTWARE_MASK,
+ };
+
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+
+ android::sp<android::GraphicBuffer> GetGraphicBuffer() const;
+
+ void* GetNativeBuffer();
+
+ virtual bool IsValid() { return !!mTextureClient; }
+
+ virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
+
+ virtual GrallocImage* AsGrallocImage() override
+ {
+ return this;
+ }
+
+ virtual uint8_t* GetBuffer()
+ {
+ return static_cast<uint8_t*>(GetNativeBuffer());
+ }
+
+ int GetUsage()
+ {
+ return (static_cast<ANativeWindowBuffer*>(GetNativeBuffer()))->usage;
+ }
+
+ static int GetOmxFormat(int aFormat)
+ {
+ uint32_t omxFormat = 0;
+
+ for (int i = 0; sColorIdMap[i]; i += 2) {
+ if (sColorIdMap[i] == aFormat) {
+ omxFormat = sColorIdMap[i + 1];
+ break;
+ }
+ }
+
+ return omxFormat;
+ }
+
+private:
+ RefPtr<TextureClient> mTextureClient;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
+
+#endif /* GRALLOCIMAGES_H */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/basic/GrallocTextureHostBasic.cpp
@@ -0,0 +1,306 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GrallocTextureHostBasic.h"
+#include "GrallocImages.h" // for GetDataSourceSurfaceFrom()
+#include "mozilla/layers/SharedBufferManagerParent.h"
+
+#if ANDROID_VERSION >= 17
+#include <ui/Fence.h>
+#endif
+
+namespace mozilla {
+namespace layers {
+
+static gfx::SurfaceFormat
+HalFormatToSurfaceFormat(int aHalFormat, TextureFlags aFlags)
+{
+ bool swapRB = bool(aFlags & TextureFlags::RB_SWAPPED);
+ switch (aHalFormat) {
+ case android::PIXEL_FORMAT_BGRA_8888:
+ return swapRB ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::B8G8R8A8;
+ case android::PIXEL_FORMAT_RGBA_8888:
+ return swapRB ? gfx::SurfaceFormat::B8G8R8A8 : gfx::SurfaceFormat::R8G8B8A8;
+ case android::PIXEL_FORMAT_RGBX_8888:
+ return swapRB ? gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::R8G8B8X8;
+ case android::PIXEL_FORMAT_RGB_565:
+ return gfx::SurfaceFormat::R5G6B5_UINT16;
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
+ case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
+ case HAL_PIXEL_FORMAT_YV12:
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+#endif
+ // Needs convert to RGB565
+ return gfx::SurfaceFormat::R5G6B5_UINT16;
+ default:
+ if (aHalFormat >= 0x100 && aHalFormat <= 0x1FF) {
+ // Reserved range for HAL specific formats.
+ // Needs convert to RGB565
+ return gfx::SurfaceFormat::R5G6B5_UINT16;
+ } else {
+ MOZ_CRASH("GFX: Unhandled HAL pixel format");
+ return gfx::SurfaceFormat::UNKNOWN; // not reached
+ }
+ }
+}
+
+static bool
+NeedsConvertFromYUVtoRGB565(int aHalFormat)
+{
+ switch (aHalFormat) {
+ case android::PIXEL_FORMAT_BGRA_8888:
+ case android::PIXEL_FORMAT_RGBA_8888:
+ case android::PIXEL_FORMAT_RGBX_8888:
+ case android::PIXEL_FORMAT_RGB_565:
+ return false;
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
+ case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
+ case HAL_PIXEL_FORMAT_YV12:
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+#endif
+ return true;
+ default:
+ if (aHalFormat >= 0x100 && aHalFormat <= 0x1FF) {
+ // Reserved range for HAL specific formats.
+ return true;
+ } else {
+ MOZ_CRASH("GFX: Unhandled HAL pixel format YUV");
+ return false; // not reached
+ }
+ }
+}
+
+GrallocTextureHostBasic::GrallocTextureHostBasic(
+ TextureFlags aFlags,
+ const SurfaceDescriptorGralloc& aDescriptor)
+ : TextureHost(aFlags)
+ , mGrallocHandle(aDescriptor)
+ , mSize(0, 0)
+ , mCropSize(0, 0)
+ , mFormat(gfx::SurfaceFormat::UNKNOWN)
+ , mIsOpaque(aDescriptor.isOpaque())
+{
+ android::GraphicBuffer* grallocBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
+ MOZ_ASSERT(grallocBuffer);
+
+ if (grallocBuffer) {
+ mFormat =
+ HalFormatToSurfaceFormat(grallocBuffer->getPixelFormat(),
+ aFlags & TextureFlags::RB_SWAPPED);
+ mSize = gfx::IntSize(grallocBuffer->getWidth(), grallocBuffer->getHeight());
+ mCropSize = mSize;
+ } else {
+ printf_stderr("gralloc buffer is nullptr\n");
+ }
+}
+
+bool
+GrallocTextureHostBasic::Lock()
+{
+ if (!mCompositor || !IsValid()) {
+ return false;
+ }
+
+ if (mTextureSource) {
+ return true;
+ }
+
+ android::sp<android::GraphicBuffer> graphicBuffer =
+ GetGraphicBufferFromDesc(mGrallocHandle);
+ MOZ_ASSERT(graphicBuffer.get());
+
+ RefPtr<gfx::DataSourceSurface> surf;
+ if (NeedsConvertFromYUVtoRGB565(graphicBuffer->getPixelFormat())) {
+ PlanarYCbCrData ycbcrData;
+ surf = GetDataSourceSurfaceFrom(graphicBuffer,
+ mCropSize,
+ ycbcrData);
+ } else {
+ uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN;
+ int32_t rv = graphicBuffer->lock(usage,
+ reinterpret_cast<void**>(&mMappedBuffer));
+ if (rv) {
+ mMappedBuffer = nullptr;
+ NS_WARNING("Couldn't lock graphic buffer");
+ return false;
+ }
+ surf = gfx::Factory::CreateWrappingDataSourceSurface(
+ mMappedBuffer,
+ graphicBuffer->getStride() * gfx::BytesPerPixel(mFormat),
+ mCropSize,
+ mFormat);
+ }
+ if (surf) {
+ mTextureSource = mCompositor->CreateDataTextureSource(mFlags);
+ mTextureSource->Update(surf, nullptr);
+ return true;
+ }
+ mMappedBuffer = nullptr;
+ return false;
+}
+
+bool
+GrallocTextureHostBasic::IsValid() const
+{
+ android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
+ return graphicBuffer != nullptr;
+}
+
+bool
+GrallocTextureHostBasic::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ aTexture = mTextureSource;
+ return !!aTexture;
+}
+
+void
+GrallocTextureHostBasic::UnbindTextureSource()
+{
+ TextureHost::UnbindTextureSource();
+ ClearTextureSource();
+}
+
+void
+GrallocTextureHostBasic::ClearTextureSource()
+{
+ mTextureSource = nullptr;
+ if (mMappedBuffer) {
+ android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
+ MOZ_ASSERT(graphicBuffer);
+ mMappedBuffer = nullptr;
+ graphicBuffer->unlock();
+ }
+}
+
+void
+GrallocTextureHostBasic::SetCompositor(Compositor* aCompositor)
+{
+ BasicCompositor* compositor = AssertBasicCompositor(aCompositor);
+ if (!compositor) {
+ return;
+ }
+
+ mCompositor = compositor;
+ if (mTextureSource) {
+ mTextureSource->SetCompositor(compositor);
+ }
+}
+
+Compositor*
+GrallocTextureHostBasic::GetCompositor()
+{
+ return mCompositor;
+}
+
+gfx::SurfaceFormat
+GrallocTextureHostBasic::GetFormat() const {
+ return mFormat;
+}
+
+void
+GrallocTextureHostBasic::WaitAcquireFenceHandleSyncComplete()
+{
+ if (!mAcquireFenceHandle.IsValid()) {
+ return;
+ }
+
+#if ANDROID_VERSION >= 17
+ RefPtr<FenceHandle::FdObj> fdObj = mAcquireFenceHandle.GetAndResetFdObj();
+ android::sp<android::Fence> fence(
+ new android::Fence(fdObj->GetAndResetFd()));
+
+ // Wait fece complete with timeout.
+ // If a source of the fence becomes invalid because of error,
+ // fene complete is not signaled. See Bug 1061435.
+ int rv = fence->wait(400 /*400 msec*/);
+ if (rv != android::OK) {
+ NS_ERROR("failed to wait fence complete");
+ }
+#endif
+}
+
+void
+GrallocTextureHostBasic::SetCropRect(nsIntRect aCropRect)
+{
+ MOZ_ASSERT(aCropRect.TopLeft() == gfx::IntPoint(0, 0));
+ MOZ_ASSERT(!aCropRect.IsEmpty());
+ MOZ_ASSERT(aCropRect.width <= mSize.width);
+ MOZ_ASSERT(aCropRect.height <= mSize.height);
+
+ gfx::IntSize cropSize(aCropRect.width, aCropRect.height);
+ if (mCropSize == cropSize) {
+ return;
+ }
+
+ mCropSize = cropSize;
+ ClearTextureSource();
+}
+
+void
+GrallocTextureHostBasic::DeallocateSharedData()
+{
+ ClearTextureSource();
+
+ if (mGrallocHandle.buffer().type() != MaybeMagicGrallocBufferHandle::Tnull_t) {
+ MaybeMagicGrallocBufferHandle handle = mGrallocHandle.buffer();
+ base::ProcessId owner;
+ if (handle.type() == MaybeMagicGrallocBufferHandle::TGrallocBufferRef) {
+ owner = handle.get_GrallocBufferRef().mOwner;
+ }
+ else {
+ owner = handle.get_MagicGrallocBufferHandle().mRef.mOwner;
+ }
+
+ SharedBufferManagerParent::DropGrallocBuffer(owner, mGrallocHandle);
+ }
+}
+
+void
+GrallocTextureHostBasic::ForgetSharedData()
+{
+ ClearTextureSource();
+}
+
+void
+GrallocTextureHostBasic::DeallocateDeviceData()
+{
+ ClearTextureSource();
+}
+
+LayerRenderState
+GrallocTextureHostBasic::GetRenderState()
+{
+ android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
+
+ if (graphicBuffer) {
+ LayerRenderStateFlags flags = LayerRenderStateFlags::LAYER_RENDER_STATE_DEFAULT;
+ if (mIsOpaque) {
+ flags |= LayerRenderStateFlags::OPAQUE;
+ }
+ if (mFlags & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+ flags |= LayerRenderStateFlags::ORIGIN_BOTTOM_LEFT;
+ }
+ if (mFlags & TextureFlags::RB_SWAPPED) {
+ flags |= LayerRenderStateFlags::FORMAT_RB_SWAP;
+ }
+ return LayerRenderState(graphicBuffer,
+ mCropSize,
+ flags,
+ this);
+ }
+
+ return LayerRenderState();
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/basic/GrallocTextureHostBasic.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_GRALLOCTEXTUREHOST_BASIC_H
+#define MOZILLA_GFX_GRALLOCTEXTUREHOST_BASIC_H
+
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/ShadowLayerUtilsGralloc.h"
+#include "mozilla/layers/TextureHostBasic.h"
+
+namespace mozilla {
+namespace layers {
+
+class BasicCompositor;
+
+/**
+ * A TextureHost for shared gralloc
+ *
+ * Most of the logic actually happens in GrallocTextureSourceBasic.
+ */
+class GrallocTextureHostBasic : public TextureHost
+{
+public:
+ GrallocTextureHostBasic(TextureFlags aFlags,
+ const SurfaceDescriptorGralloc& aDescriptor);
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override;
+
+ virtual bool Lock() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
+
+ virtual void UnbindTextureSource() override;
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ virtual void WaitAcquireFenceHandleSyncComplete() override;
+
+ virtual gfx::IntSize GetSize() const override { return mCropSize; }
+
+ virtual void SetCropRect(nsIntRect aCropRect) override;
+
+ virtual void DeallocateSharedData() override;
+
+ virtual void ForgetSharedData() override;
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual LayerRenderState GetRenderState() override;
+
+ bool IsValid() const;
+
+ void ClearTextureSource();
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ virtual const char* Name() override { return "GrallocTextureHostBasic"; }
+#endif
+
+protected:
+ RefPtr<BasicCompositor> mCompositor;
+ RefPtr<DataTextureSource> mTextureSource;
+ SurfaceDescriptorGralloc mGrallocHandle;
+ // gralloc buffer size.
+ gfx::IntSize mSize;
+ // Size reported by TextureClient, can be different in some cases (video?),
+ // used by LayerRenderState.
+ gfx::IntSize mCropSize;
+ gfx::SurfaceFormat mFormat;
+ bool mIsOpaque;
+ /**
+ * Points to a mapped gralloc buffer when TextureSource is valid.
+ */
+ uint8_t* mMappedBuffer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_GRALLOCTEXTUREHOST_BASIC_H
new file mode 100755
--- /dev/null
+++ b/gfx/layers/ipc/AsyncTransactionTracker.cpp
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 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/. */
+
+#include "AsyncTransactionTracker.h"
+
+#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
+#include "mozilla/gfx/Logging.h"
+
+namespace mozilla {
+namespace layers {
+
+void
+AsyncTransactionWaiter::WaitComplete()
+{
+ MOZ_ASSERT(!InImageBridgeChildThread());
+
+ MonitorAutoLock mon(mCompletedMonitor);
+ int count = 0;
+ const int maxCount = 5;
+ while (mWaitCount > 0 && (count < maxCount)) {
+ if (!NS_SUCCEEDED(mCompletedMonitor.Wait(PR_MillisecondsToInterval(10000)))) {
+ NS_WARNING("Failed to wait Monitor");
+ return;
+ }
+ if (count > 1) {
+ printf_stderr("Waiting async transaction complete.\n");
+ }
+ count++;
+ }
+
+ if (mWaitCount > 0) {
+ printf_stderr("Timeout of waiting transaction complete.");
+ }
+
+ if (count == maxCount) {
+ gfxDevCrash(gfx::LogReason::AsyncTransactionTimeout) << "Bug 1244883: AsyncTransactionWaiter timed out.";
+ }
+}
+
+Atomic<uint64_t> AsyncTransactionTracker::sSerialCounter(0);
+
+AsyncTransactionTracker::AsyncTransactionTracker(AsyncTransactionWaiter* aWaiter)
+ : mSerial(GetNextSerial())
+ , mWaiter(aWaiter)
+#ifdef DEBUG
+ , mCompleted(false)
+#endif
+{
+ if (mWaiter) {
+ mWaiter->IncrementWaitCount();
+ }
+}
+
+AsyncTransactionTracker::~AsyncTransactionTracker()
+{
+}
+
+void
+AsyncTransactionTracker::NotifyComplete()
+{
+ MOZ_ASSERT(!mCompleted);
+#ifdef DEBUG
+ mCompleted = true;
+#endif
+ Complete();
+ if (mWaiter) {
+ mWaiter->DecrementWaitCount();
+ }
+}
+
+void
+AsyncTransactionTracker::NotifyCancel()
+{
+ MOZ_ASSERT(!mCompleted);
+#ifdef DEBUG
+ mCompleted = true;
+#endif
+ Cancel();
+ if (mWaiter) {
+ mWaiter->DecrementWaitCount();
+ }
+}
+
+Atomic<uint64_t> AsyncTransactionTrackersHolder::sSerialCounter(0);
+
+AsyncTransactionTrackersHolder::AsyncTransactionTrackersHolder()
+ : mSerial(GetNextSerial())
+ , mIsTrackersHolderDestroyed(false)
+{
+ MOZ_COUNT_CTOR(AsyncTransactionTrackersHolder);
+}
+
+AsyncTransactionTrackersHolder::~AsyncTransactionTrackersHolder()
+{
+ if (!mIsTrackersHolderDestroyed) {
+ DestroyAsyncTransactionTrackersHolder();
+ }
+ MOZ_COUNT_DTOR(AsyncTransactionTrackersHolder);
+}
+
+void
+AsyncTransactionTrackersHolder::HoldUntilComplete(AsyncTransactionTracker* aTransactionTracker)
+{
+ if (!aTransactionTracker) {
+ return;
+ }
+
+ if (mIsTrackersHolderDestroyed && aTransactionTracker) {
+ aTransactionTracker->NotifyComplete();
+ return;
+ }
+
+ if (aTransactionTracker) {
+ mAsyncTransactionTrackers[aTransactionTracker->GetId()] = aTransactionTracker;
+ }
+}
+
+void
+AsyncTransactionTrackersHolder::TransactionCompleteted(uint64_t aTransactionId)
+{
+ TransactionCompletetedInternal(aTransactionId);
+}
+
+void
+AsyncTransactionTrackersHolder::TransactionCompletetedInternal(uint64_t aTransactionId)
+{
+ std::map<uint64_t, RefPtr<AsyncTransactionTracker> >::iterator it
+ = mAsyncTransactionTrackers.find(aTransactionId);
+ if (it != mAsyncTransactionTrackers.end()) {
+ it->second->NotifyComplete();
+ mAsyncTransactionTrackers.erase(it);
+ }
+}
+
+void
+AsyncTransactionTrackersHolder::SetReleaseFenceHandle(FenceHandle& aReleaseFenceHandle,
+ uint64_t aTransactionId)
+{
+ std::map<uint64_t, RefPtr<AsyncTransactionTracker> >::iterator it
+ = mAsyncTransactionTrackers.find(aTransactionId);
+ if (it != mAsyncTransactionTrackers.end()) {
+ it->second->SetReleaseFenceHandle(aReleaseFenceHandle);
+ }
+}
+
+void
+AsyncTransactionTrackersHolder::ClearAllAsyncTransactionTrackers()
+{
+ std::map<uint64_t, RefPtr<AsyncTransactionTracker> >::iterator it;
+ for (it = mAsyncTransactionTrackers.begin();
+ it != mAsyncTransactionTrackers.end(); it++) {
+ it->second->NotifyCancel();
+ }
+ mAsyncTransactionTrackers.clear();
+}
+
+void
+AsyncTransactionTrackersHolder::DestroyAsyncTransactionTrackersHolder() {
+ mIsTrackersHolderDestroyed = true;
+ ClearAllAsyncTransactionTrackers();
+}
+
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/AsyncTransactionTracker.h
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 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/. */
+
+#ifndef mozilla_layers_AsyncTransactionTracker_h
+#define mozilla_layers_AsyncTransactionTracker_h
+
+#include <map>
+
+#include "mozilla/Atomics.h"
+#include "mozilla/layers/FenceUtils.h" // for FenceHandle
+#include "mozilla/Monitor.h" // for Monitor
+#include "mozilla/RefPtr.h" // for AtomicRefCounted
+
+namespace mozilla {
+namespace layers {
+
+class TextureClient;
+class AsyncTransactionTrackersHolder;
+
+/**
+ * Object that lets you wait for one or more async transactions to complete.
+ */
+class AsyncTransactionWaiter
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncTransactionWaiter)
+
+ AsyncTransactionWaiter()
+ : mCompletedMonitor("AsyncTransactionWaiter")
+ , mWaitCount(0)
+ {}
+
+ void IncrementWaitCount()
+ {
+ MonitorAutoLock lock(mCompletedMonitor);
+ ++mWaitCount;
+ }
+ void DecrementWaitCount()
+ {
+ MonitorAutoLock lock(mCompletedMonitor);
+ MOZ_ASSERT(mWaitCount > 0);
+ --mWaitCount;
+ if (mWaitCount == 0) {
+ mCompletedMonitor.Notify();
+ }
+ }
+
+ /**
+ * Wait until asynchronous transactions complete.
+ */
+ void WaitComplete();
+
+ uint32_t GetWaitCount() { return mWaitCount; }
+
+private:
+ ~AsyncTransactionWaiter() {}
+
+ Monitor mCompletedMonitor;
+ uint32_t mWaitCount;
+};
+
+/**
+ * AsyncTransactionTracker tracks asynchronous transaction.
+ * It is typically used for asynchronous layer transaction handling.
+ */
+class AsyncTransactionTracker
+{
+ friend class AsyncTransactionTrackersHolder;
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncTransactionTracker)
+
+ explicit AsyncTransactionTracker(AsyncTransactionWaiter* aWaiter = nullptr);
+
+ /**
+ * Notify async transaction complete.
+ */
+ void NotifyComplete();
+
+ /**
+ * Notify async transaction cancel.
+ */
+ void NotifyCancel();
+
+ uint64_t GetId()
+ {
+ return mSerial;
+ }
+
+ /**
+ * Called when asynchronous transaction complete.
+ */
+ virtual void Complete()= 0;
+
+ /**
+ * Called when asynchronous transaction is cancelled.
+ * The cancel typically happens when IPC is disconnected
+ */
+ virtual void Cancel()= 0;
+
+ virtual void SetTextureClient(TextureClient* aTextureClient) {}
+
+ virtual void SetReleaseFenceHandle(FenceHandle& aReleaseFenceHandle) {}
+
+protected:
+ virtual ~AsyncTransactionTracker();
+
+ static uint64_t GetNextSerial()
+ {
+ return ++sSerialCounter;
+ }
+
+ uint64_t mSerial;
+ RefPtr<AsyncTransactionWaiter> mWaiter;
+#ifdef DEBUG
+ bool mCompleted;
+#endif
+
+ static Atomic<uint64_t> sSerialCounter;
+};
+
+class AsyncTransactionTrackersHolder
+{
+public:
+ AsyncTransactionTrackersHolder();
+ virtual ~AsyncTransactionTrackersHolder();
+
+ void HoldUntilComplete(AsyncTransactionTracker* aTransactionTracker);
+
+ void TransactionCompleteted(uint64_t aTransactionId);
+
+ static void TransactionCompleteted(uint64_t aHolderId, uint64_t aTransactionId);
+
+ static void SetReleaseFenceHandle(FenceHandle& aReleaseFenceHandle,
+ uint64_t aHolderId,
+ uint64_t aTransactionId);
+
+ uint64_t GetId()
+ {
+ return mSerial;
+ }
+
+ void DestroyAsyncTransactionTrackersHolder();
+
+protected:
+
+ static uint64_t GetNextSerial()
+ {
+ return ++sSerialCounter;
+ }
+
+ void TransactionCompletetedInternal(uint64_t aTransactionId);
+
+ void SetReleaseFenceHandle(FenceHandle& aReleaseFenceHandle, uint64_t aTransactionId);
+
+ void ClearAllAsyncTransactionTrackers();
+
+ const uint64_t mSerial;
+
+ bool mIsTrackersHolderDestroyed;
+ std::map<uint64_t, RefPtr<AsyncTransactionTracker> > mAsyncTransactionTrackers;
+
+ static Atomic<uint64_t> sSerialCounter;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_AsyncTransactionTracker_h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/FenceUtils.cpp
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 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/. */
+
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+#pragma GCC visibility push(default)
+#include "sync/sync.h" // for sync_merge
+#pragma GCC visibility pop
+#endif
+
+#include "FenceUtils.h"
+
+using namespace mozilla::layers;
+
+namespace IPC {
+
+void
+ParamTraits<FenceHandle>::Write(Message* aMsg,
+ const paramType& aParam)
+{
+ FenceHandle handle = aParam;
+
+ MOZ_ASSERT(handle.IsValid());
+
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+ RefPtr<FenceHandle::FdObj> fence = handle.GetAndResetFdObj();
+ aMsg->WriteFileDescriptor(base::FileDescriptor(fence->GetAndResetFd(), true));
+#endif
+}
+
+bool
+ParamTraits<FenceHandle>::Read(const Message* aMsg,
+ PickleIterator* aIter, paramType* aResult)
+{
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+ base::FileDescriptor fd;
+ if (aMsg->ReadFileDescriptor(aIter, &fd)) {
+ aResult->Merge(FenceHandle(new FenceHandle::FdObj(fd.fd)));
+ }
+#endif
+ return true;
+}
+
+} // namespace IPC
+
+namespace mozilla {
+namespace layers {
+
+FenceHandle::FenceHandle()
+ : mFence(new FdObj())
+{
+}
+
+FenceHandle::FenceHandle(FdObj* aFdObj)
+ : mFence(aFdObj)
+{
+ MOZ_ASSERT(aFdObj);
+}
+
+void
+FenceHandle::Merge(const FenceHandle& aFenceHandle)
+{
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+ if (!aFenceHandle.IsValid()) {
+ return;
+ }
+
+ if (!IsValid()) {
+ mFence = aFenceHandle.mFence;
+ } else {
+ int result = sync_merge("FenceHandle", mFence->mFd, aFenceHandle.mFence->mFd);
+ if (result == -1) {
+ mFence = aFenceHandle.mFence;
+ } else {
+ mFence = new FdObj(result);
+ }
+ }
+#endif
+}
+
+void
+FenceHandle::TransferToAnotherFenceHandle(FenceHandle& aFenceHandle)
+{
+ aFenceHandle.mFence = this->GetAndResetFdObj();
+}
+
+already_AddRefed<FenceHandle::FdObj>
+FenceHandle::GetAndResetFdObj()
+{
+ RefPtr<FdObj> fence = mFence;
+ mFence = new FdObj();
+ return fence.forget();
+}
+
+already_AddRefed<FenceHandle::FdObj>
+FenceHandle::GetDupFdObj()
+{
+ RefPtr<FdObj> fdObj;
+ if (IsValid()) {
+ fdObj = new FenceHandle::FdObj(dup(mFence->mFd));
+ } else {
+ fdObj = new FenceHandle::FdObj();
+ }
+ return fdObj.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/FenceUtils.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 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/. */
+
+#ifndef IPC_FencerUtils_h
+#define IPC_FencerUtils_h
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+
+namespace mozilla {
+namespace layers {
+
+class FenceHandle {
+public:
+ class FdObj {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FdObj)
+ friend class FenceHandle;
+ public:
+ FdObj()
+ : mFd(-1) {}
+ explicit FdObj(int aFd)
+ : mFd(aFd) {}
+ int GetAndResetFd()
+ {
+ int fd = mFd;
+ mFd = -1;
+ return fd;
+ }
+
+ private:
+ virtual ~FdObj() {
+ if (mFd != -1) {
+ close(mFd);
+ }
+ }
+
+ int mFd;
+ };
+
+ FenceHandle();
+
+ explicit FenceHandle(FdObj* aFdObj);
+
+ bool operator==(const FenceHandle& aOther) const {
+ return mFence.get() == aOther.mFence.get();
+ }
+
+ bool IsValid() const
+ {
+ return (mFence->mFd != -1);
+ }
+
+ void Merge(const FenceHandle& aFenceHandle);
+
+ void TransferToAnotherFenceHandle(FenceHandle& aFenceHandle);
+
+ already_AddRefed<FdObj> GetAndResetFdObj();
+
+ already_AddRefed<FdObj> GetDupFdObj();
+
+private:
+ RefPtr<FdObj> mFence;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::layers::FenceHandle> {
+ typedef mozilla::layers::FenceHandle paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam);
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult);
+};
+
+} // namespace IPC
+
+#endif // IPC_FencerUtils_h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/PSharedBufferManager.ipdl
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 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/. */
+
+include LayersSurfaces;
+include ProtocolTypes;
+
+include "mozilla/GfxMessageUtils.h";
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * This is a dedicated protocol to track/allocate/deallocate gralloc buffers.
+ */
+
+sync protocol PSharedBufferManager {
+parent:
+ sync AllocateGrallocBuffer(IntSize size, uint32_t format, uint32_t usage)
+ returns (MaybeMagicGrallocBufferHandle handle);
+both:
+ async DropGrallocBuffer(MaybeMagicGrallocBufferHandle handle);
+};
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 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/. */
+
+#include "mozilla/DebugOnly.h"
+
+#include "mozilla/gfx/Point.h"
+#include "mozilla/layers/LayerTransactionChild.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/SharedBufferManagerChild.h"
+#include "mozilla/layers/SharedBufferManagerParent.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "nsXULAppAPI.h"
+
+#include "ShadowLayerUtilsGralloc.h"
+
+#include "nsIMemoryReporter.h"
+
+#include "gfxPlatform.h"
+#include "gfx2DGlue.h"
+#include "GLContext.h"
+
+#include "GeckoProfiler.h"
+
+#include "cutils/properties.h"
+
+#include "MainThreadUtils.h"
+
+using namespace android;
+using namespace mozilla::layers;
+using namespace mozilla::gl;
+
+using base::FileDescriptor;
+
+namespace IPC {
+
+void
+ParamTraits<GrallocBufferRef>::Write(Message* aMsg,
+ const paramType& aParam)
+{
+ aMsg->WriteInt(aParam.mOwner);
+ aMsg->WriteInt64(aParam.mKey);
+}
+
+bool
+ParamTraits<GrallocBufferRef>::Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aParam)
+{
+ int owner;
+ int64_t index;
+ if (!aMsg->ReadInt(aIter, &owner) ||
+ !aMsg->ReadInt64(aIter, &index)) {
+ printf_stderr("ParamTraits<GrallocBufferRef>::Read() failed to read a message\n");
+ return false;
+ }
+ aParam->mOwner = owner;
+ aParam->mKey = index;
+ return true;
+}
+
+void
+ParamTraits<MagicGrallocBufferHandle>::Write(Message* aMsg,
+ const paramType& aParam)
+{
+#if ANDROID_VERSION >= 19
+ sp<GraphicBuffer> flattenable = aParam.mGraphicBuffer;
+#else
+ Flattenable *flattenable = aParam.mGraphicBuffer.get();
+#endif
+ size_t nbytes = flattenable->getFlattenedSize();
+ size_t nfds = flattenable->getFdCount();
+
+ char data[nbytes];
+ int fds[nfds];
+
+#if ANDROID_VERSION >= 19
+ // Make a copy of "data" and "fds" for flatten() to avoid casting problem
+ void *pdata = (void *)data;
+ int *pfds = fds;
+
+ flattenable->flatten(pdata, nbytes, pfds, nfds);
+
+ // In Kitkat, flatten() will change the value of nbytes and nfds, which dues
+ // to multiple parcelable object consumption. The actual size and fd count
+ // which returned by getFlattenedSize() and getFdCount() are not changed.
+ // So we change nbytes and nfds back by call corresponding calls.
+ nbytes = flattenable->getFlattenedSize();
+ nfds = flattenable->getFdCount();
+#else
+ flattenable->flatten(data, nbytes, fds, nfds);
+#endif
+ aMsg->WriteInt(aParam.mRef.mOwner);
+ aMsg->WriteInt64(aParam.mRef.mKey);
+ aMsg->WriteSize(nbytes);
+ aMsg->WriteBytes(data, nbytes);
+ for (size_t n = 0; n < nfds; ++n) {
+ // These buffers can't die in transit because they're created
+ // synchonously and the parent-side buffer can only be dropped if
+ // there's a crash.
+ aMsg->WriteFileDescriptor(FileDescriptor(fds[n], false));
+ }
+}
+
+bool
+ParamTraits<MagicGrallocBufferHandle>::Read(const Message* aMsg,
+ PickleIterator* aIter, paramType* aResult)
+{
+ MOZ_ASSERT(!aResult->mGraphicBuffer.get());
+ MOZ_ASSERT(aResult->mRef.mOwner == 0);
+ MOZ_ASSERT(aResult->mRef.mKey == -1);
+
+ size_t nbytes;
+ int owner;
+ int64_t index;
+
+ if (!aMsg->ReadInt(aIter, &owner) ||
+ !aMsg->ReadInt64(aIter, &index) ||
+ !aMsg->ReadSize(aIter, &nbytes)) {
+ printf_stderr("ParamTraits<MagicGrallocBufferHandle>::Read() failed to read a message\n");
+ return false;
+ }
+
+ auto data = mozilla::MakeUnique<char[]>(nbytes);
+
+ if (!aMsg->ReadBytesInto(aIter, data.get(), nbytes)) {
+ printf_stderr("ParamTraits<MagicGrallocBufferHandle>::Read() failed to read a message\n");
+ return false;
+ }
+
+ size_t nfds = aMsg->num_fds();
+ int fds[nfds];
+
+ for (size_t n = 0; n < nfds; ++n) {
+ FileDescriptor fd;
+ if (!aMsg->ReadFileDescriptor(aIter, &fd)) {
+ printf_stderr("ParamTraits<MagicGrallocBufferHandle>::Read() failed to read file descriptors\n");
+ return false;
+ }
+ // If the GraphicBuffer was shared cross-process, SCM_RIGHTS does
+ // the right thing and dup's the fd. If it's shared cross-thread,
+ // SCM_RIGHTS doesn't dup the fd.
+ // But in shared cross-thread, dup fd is not necessary because we get
+ // a pointer to the GraphicBuffer directly from SharedBufferManagerParent
+ // and don't create a new GraphicBuffer around the fd.
+ fds[n] = fd.fd;
+ }
+
+ aResult->mRef.mOwner = owner;
+ aResult->mRef.mKey = index;
+ if (XRE_IsParentProcess()) {
+ // If we are in chrome process, we can just get GraphicBuffer directly from
+ // SharedBufferManagerParent.
+ aResult->mGraphicBuffer = SharedBufferManagerParent::GetGraphicBuffer(aResult->mRef);
+ } else {
+ // Deserialize GraphicBuffer
+#if ANDROID_VERSION >= 19
+ sp<GraphicBuffer> buffer(new GraphicBuffer());
+ const void* datap = (const void*)data.get();
+ const int* fdsp = &fds[0];
+ if (NO_ERROR != buffer->unflatten(datap, nbytes, fdsp, nfds)) {
+ buffer = nullptr;
+ }
+#else
+ sp<GraphicBuffer> buffer(new GraphicBuffer());
+ Flattenable *flattenable = buffer.get();
+ if (NO_ERROR != flattenable->unflatten(data.get(), nbytes, fds, nfds)) {
+ buffer = nullptr;
+ }
+#endif
+ if (buffer.get()) {
+ aResult->mGraphicBuffer = buffer;
+ }
+ }
+
+ if (!aResult->mGraphicBuffer.get()) {
+ printf_stderr("ParamTraits<MagicGrallocBufferHandle>::Read() failed to get gralloc buffer\n");
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace IPC
+
+namespace mozilla {
+namespace layers {
+
+MagicGrallocBufferHandle::MagicGrallocBufferHandle(const sp<GraphicBuffer>& aGraphicBuffer, GrallocBufferRef ref)
+ : mGraphicBuffer(aGraphicBuffer)
+ , mRef(ref)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Parent process
+
+/*static*/ bool
+LayerManagerComposite::SupportsDirectTexturing()
+{
+ return true;
+}
+
+/*static*/ void
+LayerManagerComposite::PlatformSyncBeforeReplyUpdate()
+{
+ // Nothing to be done for gralloc.
+}
+
+//-----------------------------------------------------------------------------
+// Both processes
+
+/*static*/ sp<GraphicBuffer>
+GetGraphicBufferFrom(MaybeMagicGrallocBufferHandle aHandle)
+{
+ if (aHandle.type() != MaybeMagicGrallocBufferHandle::TMagicGrallocBufferHandle) {
+ if (aHandle.type() == MaybeMagicGrallocBufferHandle::TGrallocBufferRef) {
+ if (XRE_IsParentProcess()) {
+ return SharedBufferManagerParent::GetGraphicBuffer(aHandle.get_GrallocBufferRef());
+ }
+ return SharedBufferManagerChild::GetSingleton()->GetGraphicBuffer(aHandle.get_GrallocBufferRef().mKey);
+ }
+ } else {
+ MagicGrallocBufferHandle realHandle = aHandle.get_MagicGrallocBufferHandle();
+ return realHandle.mGraphicBuffer;
+ }
+ return nullptr;
+}
+
+android::sp<android::GraphicBuffer>
+GetGraphicBufferFromDesc(SurfaceDescriptor aDesc)
+{
+ MaybeMagicGrallocBufferHandle handle;
+ if (aDesc.type() == SurfaceDescriptor::TSurfaceDescriptorGralloc) {
+ handle = aDesc.get_SurfaceDescriptorGralloc().buffer();
+ }
+ return GetGraphicBufferFrom(handle);
+}
+
+/*static*/ void
+ShadowLayerForwarder::PlatformSyncBeforeUpdate()
+{
+ // Nothing to be done for gralloc.
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerUtilsGralloc.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 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/. */
+
+#ifndef mozilla_layers_ShadowLayerUtilsGralloc_h
+#define mozilla_layers_ShadowLayerUtilsGralloc_h
+
+#include <unistd.h>
+#include <ui/GraphicBuffer.h>
+
+#include "base/process.h"
+#include "ipc/IPCMessageUtils.h"
+
+#define MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+#define MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
+
+namespace mozilla {
+namespace layers {
+
+class MaybeMagicGrallocBufferHandle;
+class SurfaceDescriptor;
+
+struct GrallocBufferRef {
+ base::ProcessId mOwner;
+ int64_t mKey;
+
+ GrallocBufferRef()
+ : mOwner(0)
+ , mKey(-1)
+ {
+
+ }
+
+ bool operator== (const GrallocBufferRef rhs) const{
+ return mOwner == rhs.mOwner && mKey == rhs.mKey;
+ }
+};
+/**
+ * This class exists to share the underlying GraphicBuffer resources
+ * from one thread context to another. This requires going through
+ * slow paths in the kernel so can be somewhat expensive.
+ *
+ * This is not just platform-specific, but also
+ * gralloc-implementation-specific.
+ */
+struct MagicGrallocBufferHandle {
+ typedef android::GraphicBuffer GraphicBuffer;
+ MagicGrallocBufferHandle() {}
+
+ MagicGrallocBufferHandle(const android::sp<GraphicBuffer>& aGraphicBuffer, GrallocBufferRef ref);
+
+ // Default copy ctor and operator= are OK
+
+ bool operator==(const MagicGrallocBufferHandle& aOther) const {
+ return mGraphicBuffer == aOther.mGraphicBuffer;
+ }
+
+ android::sp<GraphicBuffer> mGraphicBuffer;
+ GrallocBufferRef mRef;
+};
+
+/**
+ * Util function to find GraphicBuffer from SurfaceDescriptor, caller of this function should check origin
+ * to make sure not corrupt others buffer
+ */
+android::sp<android::GraphicBuffer> GetGraphicBufferFrom(MaybeMagicGrallocBufferHandle aHandle);
+android::sp<android::GraphicBuffer> GetGraphicBufferFromDesc(SurfaceDescriptor aDesc);
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::layers::MagicGrallocBufferHandle> {
+ typedef mozilla::layers::MagicGrallocBufferHandle paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam);
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult);
+};
+
+template<>
+struct ParamTraits<mozilla::layers::GrallocBufferRef> {
+ typedef mozilla::layers::GrallocBufferRef paramType;
+ static void Write(Message* aMsg, const paramType& aParam);
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult);
+};
+
+
+} // namespace IPC
+
+#endif // mozilla_layers_ShadowLayerUtilsGralloc_h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/SharedBufferManagerChild.cpp
@@ -0,0 +1,352 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 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/. */
+
+#include "base/task.h" // for NewRunnableFunction, etc
+#include "base/thread.h" // for Thread
+#include "mozilla/gfx/Logging.h" // for gfxDebug
+#include "mozilla/layers/SharedBufferManagerChild.h"
+#include "mozilla/layers/SharedBufferManagerParent.h"
+#include "mozilla/Sprintf.h" // for SprintfLiteral
+#include "mozilla/StaticPtr.h" // for StaticRefPtr
+#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitor, etc
+#include "nsThreadUtils.h" // for NS_IsMainThread
+
+#ifdef MOZ_WIDGET_GONK
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "SBMChild", ## args)
+#endif
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+// Singleton
+SharedBufferManagerChild* SharedBufferManagerChild::sSharedBufferManagerChildSingleton = nullptr;
+SharedBufferManagerParent* SharedBufferManagerChild::sSharedBufferManagerParentSingleton = nullptr;
+base::Thread* SharedBufferManagerChild::sSharedBufferManagerChildThread = nullptr;
+
+SharedBufferManagerChild::SharedBufferManagerChild()
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+ : mBufferMutex("BufferMonitor")
+#endif
+{
+}
+
+static bool
+InSharedBufferManagerChildThread()
+{
+ return SharedBufferManagerChild::sSharedBufferManagerChildThread->thread_id() == PlatformThread::CurrentId();
+}
+
+// dispatched function
+static void
+DeleteSharedBufferManagerSync(ReentrantMonitor *aBarrier, bool *aDone)
+{
+ ReentrantMonitorAutoEnter autoMon(*aBarrier);
+
+ MOZ_ASSERT(InSharedBufferManagerChildThread(),
+ "Should be in SharedBufferManagerChild thread.");
+ SharedBufferManagerChild::sSharedBufferManagerChildSingleton = nullptr;
+ SharedBufferManagerChild::sSharedBufferManagerParentSingleton = nullptr;
+ *aDone = true;
+ aBarrier->NotifyAll();
+}
+
+// dispatched function
+static void
+ConnectSharedBufferManager(SharedBufferManagerChild *child, SharedBufferManagerParent *parent)
+{
+ MessageLoop *parentMsgLoop = parent->GetMessageLoop();
+ ipc::MessageChannel *parentChannel = parent->GetIPCChannel();
+ child->Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide);
+}
+
+base::Thread*
+SharedBufferManagerChild::GetThread() const
+{
+ return sSharedBufferManagerChildThread;
+}
+
+SharedBufferManagerChild*
+SharedBufferManagerChild::GetSingleton()
+{
+ return sSharedBufferManagerChildSingleton;
+}
+
+bool
+SharedBufferManagerChild::IsCreated()
+{
+ return GetSingleton() != nullptr;
+}
+
+void
+SharedBufferManagerChild::StartUp()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
+ SharedBufferManagerChild::StartUpOnThread(new base::Thread("BufferMgrChild"));
+}
+
+static void
+ConnectSharedBufferManagerInChildProcess(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherPid)
+{
+ // Bind the IPC channel to the shared buffer manager thread.
+ SharedBufferManagerChild::sSharedBufferManagerChildSingleton->Open(aTransport,
+ aOtherPid,
+ XRE_GetIOMessageLoop(),
+ ipc::ChildSide);
+
+}
+
+PSharedBufferManagerChild*
+SharedBufferManagerChild::StartUpInChildProcess(Transport* aTransport,
+ base::ProcessId aOtherPid)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
+
+ sSharedBufferManagerChildThread = new base::Thread("BufferMgrChild");
+ if (!sSharedBufferManagerChildThread->Start()) {
+ return nullptr;
+ }
+
+ sSharedBufferManagerChildSingleton = new SharedBufferManagerChild();
+ sSharedBufferManagerChildSingleton->GetMessageLoop()->PostTask(
+ NewRunnableFunction(ConnectSharedBufferManagerInChildProcess,
+ aTransport, aOtherPid));
+
+ return sSharedBufferManagerChildSingleton;
+}
+
+void
+SharedBufferManagerChild::ShutDown()
+{
+ NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
+ if (IsCreated()) {
+ SharedBufferManagerChild::DestroyManager();
+ delete sSharedBufferManagerChildThread;
+ sSharedBufferManagerChildThread = nullptr;
+ }
+}
+
+bool
+SharedBufferManagerChild::StartUpOnThread(base::Thread* aThread)
+{
+ MOZ_ASSERT(aThread, "SharedBufferManager needs a thread.");
+ if (sSharedBufferManagerChildSingleton != nullptr) {
+ return false;
+ }
+
+ sSharedBufferManagerChildThread = aThread;
+ if (!aThread->IsRunning()) {
+ aThread->Start();
+ }
+ sSharedBufferManagerChildSingleton = new SharedBufferManagerChild();
+ char thrname[128];
+ SprintfLiteral(thrname, "BufMgrParent#%d", base::Process::Current().pid());
+ sSharedBufferManagerParentSingleton = new SharedBufferManagerParent(
+ base::Process::Current().pid(), new base::Thread(thrname));
+ sSharedBufferManagerChildSingleton->ConnectAsync(sSharedBufferManagerParentSingleton);
+ return true;
+}
+
+void
+SharedBufferManagerChild::DestroyManager()
+{
+ MOZ_ASSERT(!InSharedBufferManagerChildThread(),
+ "This method must not be called in this thread.");
+ // ...because we are about to dispatch synchronous messages to the
+ // BufferManagerChild thread.
+
+ if (!IsCreated()) {
+ return;
+ }
+
+ ReentrantMonitor barrier("BufferManagerDestroyTask lock");
+ ReentrantMonitorAutoEnter autoMon(barrier);
+
+ bool done = false;
+ sSharedBufferManagerChildSingleton->GetMessageLoop()->PostTask(
+ NewRunnableFunction(&DeleteSharedBufferManagerSync, &barrier, &done));
+ while (!done) {
+ barrier.Wait();
+ }
+
+}
+
+MessageLoop *
+SharedBufferManagerChild::GetMessageLoop() const
+{
+ return sSharedBufferManagerChildThread != nullptr ?
+ sSharedBufferManagerChildThread->message_loop() :
+ nullptr;
+}
+
+void
+SharedBufferManagerChild::ConnectAsync(SharedBufferManagerParent* aParent)
+{
+ GetMessageLoop()->PostTask(NewRunnableFunction(&ConnectSharedBufferManager,
+ this, aParent));
+}
+
+// dispatched function
+void
+SharedBufferManagerChild::AllocGrallocBufferSync(const GrallocParam& aParam,
+ Monitor* aBarrier,
+ bool* aDone)
+{
+ MonitorAutoLock autoMon(*aBarrier);
+
+ sSharedBufferManagerChildSingleton->AllocGrallocBufferNow(aParam.size,
+ aParam.format,
+ aParam.usage,
+ aParam.buffer);
+ *aDone = true;
+ aBarrier->NotifyAll();
+}
+
+// dispatched function
+void
+SharedBufferManagerChild::DeallocGrallocBufferSync(const mozilla::layers::MaybeMagicGrallocBufferHandle& aBuffer)
+{
+ SharedBufferManagerChild::sSharedBufferManagerChildSingleton->
+ DeallocGrallocBufferNow(aBuffer);
+}
+
+bool
+SharedBufferManagerChild::AllocGrallocBuffer(const gfx::IntSize& aSize,
+ const uint32_t& aFormat,
+ const uint32_t& aUsage,
+ mozilla::layers::MaybeMagicGrallocBufferHandle* aBuffer)
+{
+ if (aSize.width <= 0 || aSize.height <= 0) {
+ gfxDebug() << "Asking for gralloc of invalid size " << aSize.width << "x" << aSize.height;
+ return false;
+ }
+
+ if (InSharedBufferManagerChildThread()) {
+ return SharedBufferManagerChild::AllocGrallocBufferNow(aSize, aFormat, aUsage, aBuffer);
+ }
+
+ Monitor barrier("AllocSurfaceDescriptorGralloc Lock");
+ MonitorAutoLock autoMon(barrier);
+ bool done = false;
+
+ GetMessageLoop()->PostTask(
+ NewRunnableFunction(&AllocGrallocBufferSync,
+ GrallocParam(aSize, aFormat, aUsage, aBuffer), &barrier, &done));
+
+ while (!done) {
+ barrier.Wait();
+ }
+ return true;
+}
+
+bool
+SharedBufferManagerChild::AllocGrallocBufferNow(const IntSize& aSize,
+ const uint32_t& aFormat,
+ const uint32_t& aUsage,
+ mozilla::layers::MaybeMagicGrallocBufferHandle* aHandle)
+{
+ // These are protected functions, we can just assert and ask the caller to test
+ MOZ_ASSERT(aSize.width >= 0 && aSize.height >= 0);
+
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+ mozilla::layers::MaybeMagicGrallocBufferHandle handle;
+ if (!SendAllocateGrallocBuffer(aSize, aFormat, aUsage, &handle)) {
+ return false;
+ }
+ if (handle.type() != mozilla::layers::MaybeMagicGrallocBufferHandle::TMagicGrallocBufferHandle) {
+ return false;
+ }
+ *aHandle = handle.get_MagicGrallocBufferHandle().mRef;
+
+ {
+ MutexAutoLock lock(mBufferMutex);
+ MOZ_ASSERT(mBuffers.count(handle.get_MagicGrallocBufferHandle().mRef.mKey)==0);
+ mBuffers[handle.get_MagicGrallocBufferHandle().mRef.mKey] = handle.get_MagicGrallocBufferHandle().mGraphicBuffer;
+ }
+ return true;
+#else
+ NS_RUNTIMEABORT("No GrallocBuffer for you");
+ return true;
+#endif
+}
+
+void
+SharedBufferManagerChild::DeallocGrallocBuffer(const mozilla::layers::MaybeMagicGrallocBufferHandle& aBuffer)
+{
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+ NS_ASSERTION(aBuffer.type() != mozilla::layers::MaybeMagicGrallocBufferHandle::TMagicGrallocBufferHandle, "We shouldn't try to do IPC with real buffer");
+ if (aBuffer.type() != mozilla::layers::MaybeMagicGrallocBufferHandle::TGrallocBufferRef) {
+ return;
+ }
+#endif
+
+ if (InSharedBufferManagerChildThread()) {
+ return SharedBufferManagerChild::DeallocGrallocBufferNow(aBuffer);
+ }
+
+ GetMessageLoop()->PostTask(NewRunnableFunction(&DeallocGrallocBufferSync, aBuffer));
+}
+
+void
+SharedBufferManagerChild::DeallocGrallocBufferNow(const mozilla::layers::MaybeMagicGrallocBufferHandle& aBuffer)
+{
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+ NS_ASSERTION(aBuffer.type() != mozilla::layers::MaybeMagicGrallocBufferHandle::TMagicGrallocBufferHandle, "We shouldn't try to do IPC with real buffer");
+
+ {
+ MutexAutoLock lock(mBufferMutex);
+ mBuffers.erase(aBuffer.get_GrallocBufferRef().mKey);
+ }
+ SendDropGrallocBuffer(aBuffer);
+#else
+ NS_RUNTIMEABORT("No GrallocBuffer for you");
+#endif
+}
+
+void
+SharedBufferManagerChild::DropGrallocBuffer(const mozilla::layers::MaybeMagicGrallocBufferHandle& aHandle)
+{
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+ int64_t bufferKey = -1;
+ if (aHandle.type() == mozilla::layers::MaybeMagicGrallocBufferHandle::TMagicGrallocBufferHandle) {
+ bufferKey = aHandle.get_MagicGrallocBufferHandle().mRef.mKey;
+ } else if (aHandle.type() == mozilla::layers::MaybeMagicGrallocBufferHandle::TGrallocBufferRef) {
+ bufferKey = aHandle.get_GrallocBufferRef().mKey;
+ } else {
+ return;
+ }
+
+ {
+ MutexAutoLock lock(mBufferMutex);
+ mBuffers.erase(bufferKey);
+ }
+#endif
+}
+
+bool SharedBufferManagerChild::RecvDropGrallocBuffer(const mozilla::layers::MaybeMagicGrallocBufferHandle& aHandle)
+{
+ DropGrallocBuffer(aHandle);
+ return true;
+}
+
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+android::sp<android::GraphicBuffer>
+SharedBufferManagerChild::GetGraphicBuffer(int64_t key)
+{
+ MutexAutoLock lock(mBufferMutex);
+ if (mBuffers.count(key) == 0) {
+ printf_stderr("SharedBufferManagerChild::GetGraphicBuffer -- invalid key");
+ return nullptr;
+ }
+ return mBuffers[key];
+}
+#endif
+
+} /* namespace layers */
+} /* namespace mozilla */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/SharedBufferManagerChild.h
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* 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 SharedBufferManagerCHILD_H_
+#define SharedBufferManagerCHILD_H_
+
+#include "mozilla/layers/PSharedBufferManagerChild.h"
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+#include "mozilla/Mutex.h"
+#endif
+
+namespace base {
+class Thread;
+} // namespace base
+
+namespace mozilla {
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+class Mutex;
+#endif
+
+namespace layers {
+class SharedBufferManagerParent;
+
+struct GrallocParam {
+ gfx::IntSize size;
+ uint32_t format;
+ uint32_t usage;
+ mozilla::layers::MaybeMagicGrallocBufferHandle* buffer;
+
+ GrallocParam(const gfx::IntSize& aSize,
+ const uint32_t& aFormat,
+ const uint32_t& aUsage,
+ mozilla::layers::MaybeMagicGrallocBufferHandle* aBuffer)
+ : size(aSize)
+ , format(aFormat)
+ , usage(aUsage)
+ , buffer(aBuffer)
+ {}
+};
+
+class SharedBufferManagerChild : public PSharedBufferManagerChild {
+public:
+ SharedBufferManagerChild();
+ /**
+ * Creates the gralloc buffer manager with a dedicated thread for SharedBufferManagerChild.
+ *
+ * We may want to use a specific thread in the future. In this case, use
+ * CreateWithThread instead.
+ */
+ static void StartUp();
+
+ static PSharedBufferManagerChild*
+ StartUpInChildProcess(Transport* aTransport, ProcessId aOtherProcess);
+
+ /**
+ * Creates the SharedBufferManagerChild manager protocol.
+ */
+ static bool StartUpOnThread(base::Thread* aThread);
+
+ /**
+ * Destroys The SharedBufferManager protocol.
+ *
+ * The actual destruction happens synchronously on the SharedBufferManagerChild thread
+ * which means that if this function is called from another thread, the current
+ * thread will be paused until the destruction is done.
+ */
+ static void DestroyManager();
+
+ /**
+ * Destroys the grallob buffer manager calling DestroyManager, and destroys the
+ * SharedBufferManager's thread.
+ *
+ * If you don't want to destroy the thread, call DestroyManager directly
+ * instead.
+ */
+ static void ShutDown();
+
+ /**
+ * returns the singleton instance.
+ *
+ * can be called from any thread.
+ */
+ static SharedBufferManagerChild* GetSingleton();
+
+ /**
+ * Dispatches a task to the SharedBufferManagerChild thread to do the connection
+ */
+ void ConnectAsync(SharedBufferManagerParent* aParent);
+
+ /**
+ * Allocate GrallocBuffer remotely.
+ */
+ bool
+ AllocGrallocBuffer(const gfx::IntSize&, const uint32_t&, const uint32_t&, mozilla::layers::MaybeMagicGrallocBufferHandle*);
+
+ /**
+ * Deallocate a remotely allocated gralloc buffer.
+ * As gralloc buffer life cycle controlled by sp, this just break the sharing status of the underlying buffer
+ * and decrease the reference count on both side.
+ */
+ void
+ DeallocGrallocBuffer(const mozilla::layers::MaybeMagicGrallocBufferHandle& aBuffer);
+
+ void
+ DropGrallocBuffer(const mozilla::layers::MaybeMagicGrallocBufferHandle& aHandle);
+
+ virtual bool RecvDropGrallocBuffer(const mozilla::layers::MaybeMagicGrallocBufferHandle& aHandle);
+
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+ android::sp<android::GraphicBuffer> GetGraphicBuffer(int64_t key);
+#endif
+
+ base::Thread* GetThread() const;
+
+ MessageLoop* GetMessageLoop() const;
+
+ static bool IsCreated();
+
+ static base::Thread* sSharedBufferManagerChildThread;
+ static SharedBufferManagerChild* sSharedBufferManagerChildSingleton;
+ static SharedBufferManagerParent* sSharedBufferManagerParentSingleton; // Only available in Chrome process
+
+protected:
+ /**
+ * Part of the allocation of gralloc SurfaceDescriptor that is
+ * executed on the SharedBufferManagerChild thread after invoking
+ * AllocSurfaceDescriptorGralloc.
+ *
+ * Must be called from the SharedBufferManagerChild thread.
+ */
+ bool
+ AllocGrallocBufferNow(const gfx::IntSize& aSize,
+ const uint32_t& aFormat,
+ const uint32_t& aUsage,
+ mozilla::layers::MaybeMagicGrallocBufferHandle* aBuffer);
+
+ // Dispatched function
+ static void
+ AllocGrallocBufferSync(const GrallocParam& aParam,
+ Monitor* aBarrier,
+ bool* aDone);
+
+ /**
+ * Part of the deallocation of gralloc SurfaceDescriptor that is
+ * executed on the SharedBufferManagerChild thread after invoking
+ * DeallocSurfaceDescriptorGralloc.
+ *
+ * Must be called from the SharedBufferManagerChild thread.
+ */
+ void
+ DeallocGrallocBufferNow(const mozilla::layers::MaybeMagicGrallocBufferHandle& handle);
+
+ // dispatched function
+ static void
+ DeallocGrallocBufferSync(const mozilla::layers::MaybeMagicGrallocBufferHandle& handle);
+
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+ std::map<int64_t, android::sp<android::GraphicBuffer> > mBuffers;
+ Mutex mBufferMutex;
+#endif
+};
+
+} /* namespace layers */
+} /* namespace mozilla */
+#endif /* SharedBufferManagerCHILD_H_*/
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/SharedBufferManagerParent.cpp
@@ -0,0 +1,364 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/SharedBufferManagerParent.h"
+#include "base/message_loop.h" // for MessageLoop
+#include "base/process.h" // for ProcessId
+#include "base/task.h" // for CancelableTask, DeleteTask, etc
+#include "base/thread.h"
+#include "mozilla/Sprintf.h" // for SprintfLiteral
+#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/Transport.h" // for Transport
+#include "mozilla/Sprintf.h"
+#include "mozilla/UniquePtr.h" // for UniquePtr
+#include "mozilla/Unused.h"
+#include "nsIMemoryReporter.h"
+#ifdef MOZ_WIDGET_GONK
+#include "mozilla/LinuxUtils.h"
+#include "ui/PixelFormat.h"
+#endif
+#include "nsPrintfCString.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla::ipc;
+#ifdef MOZ_WIDGET_GONK
+using namespace android;
+#endif
+using std::map;
+
+namespace mozilla {
+namespace layers {
+
+map<base::ProcessId, SharedBufferManagerParent* > SharedBufferManagerParent::sManagers;
+StaticAutoPtr<Monitor> SharedBufferManagerParent::sManagerMonitor;
+uint64_t SharedBufferManagerParent::sBufferKey(0);
+
+#ifdef MOZ_WIDGET_GONK
+class GrallocReporter final : public nsIMemoryReporter
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ if (SharedBufferManagerParent::sManagerMonitor) {
+ SharedBufferManagerParent::sManagerMonitor->Lock();
+ }
+ map<base::ProcessId, SharedBufferManagerParent*>::iterator it;
+ for (it = SharedBufferManagerParent::sManagers.begin(); it != SharedBufferManagerParent::sManagers.end(); it++) {
+ base::ProcessId pid = it->first;
+ SharedBufferManagerParent *mgr = it->second;
+ if (!mgr) {
+ printf_stderr("GrallocReporter::CollectReports(): mgr is nullptr");
+ continue;
+ }
+
+ nsAutoCString pidName;
+ LinuxUtils::GetThreadName(pid, pidName);
+
+ MutexAutoLock lock(mgr->mLock);
+ std::map<int64_t, android::sp<android::GraphicBuffer> >::iterator buf_it;
+ for (buf_it = mgr->mBuffers.begin(); buf_it != mgr->mBuffers.end(); buf_it++) {
+ android::sp<android::GraphicBuffer> gb = buf_it->second;
+ int bpp = android::bytesPerPixel(gb->getPixelFormat());
+ int stride = gb->getStride();
+ int height = gb->getHeight();
+ int amount = bpp > 0
+ ? (stride * height * bpp)
+ // Special case for BSP specific formats (mainly YUV formats, count it as normal YUV buffer).
+ : (stride * height * 3 / 2);
+
+ nsPrintfCString gpath("gralloc/%s (pid=%d)/buffer(width=%d, height=%d, bpp=%d, stride=%d)",
+ pidName.get(), pid, gb->getWidth(), height, bpp, stride);
+
+ aHandleReport->Callback(
+ EmptyCString(), gpath, KIND_OTHER, UNITS_BYTES, amount,
+ NS_LITERAL_CSTRING(
+"Special RAM that can be shared between processes and directly accessed by "
+"both the CPU and GPU. Gralloc memory is usually a relatively precious "
+"resource, with much less available than generic RAM. When it's exhausted, "
+"graphics performance can suffer. This value can be incorrect because of race "
+"conditions."),
+ aData);
+ }
+ }
+ if (SharedBufferManagerParent::sManagerMonitor) {
+ SharedBufferManagerParent::sManagerMonitor->Unlock();
+ }
+ return NS_OK;
+ }
+
+protected:
+ ~GrallocReporter() {}
+};
+
+NS_IMPL_ISUPPORTS(GrallocReporter, nsIMemoryReporter)
+#endif
+
+void InitGralloc() {
+ NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+#ifdef MOZ_WIDGET_GONK
+ RegisterStrongMemoryReporter(new GrallocReporter());
+#endif
+}
+
+/**
+ * Task that deletes SharedBufferManagerParent on a specified thread.
+ */
+class DeleteSharedBufferManagerParentTask : public Runnable
+{
+public:
+ explicit DeleteSharedBufferManagerParentTask(UniquePtr<SharedBufferManagerParent> aSharedBufferManager)
+ : mSharedBufferManager(Move(aSharedBufferManager)) {
+ }
+ NS_IMETHOD Run() override { return NS_OK; }
+private:
+ UniquePtr<SharedBufferManagerParent> mSharedBufferManager;
+};
+
+SharedBufferManagerParent::SharedBufferManagerParent(base::ProcessId aOwner, base::Thread* aThread)
+ : mThread(aThread)
+ , mDestroyed(false)
+ , mLock("SharedBufferManagerParent.mLock")
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sManagerMonitor) {
+ sManagerMonitor = new Monitor("Manager Monitor");
+ }
+
+ MonitorAutoLock lock(*sManagerMonitor.get());
+ NS_ASSERTION(NS_IsMainThread(), "Should be on main thread");
+ if (!aThread->IsRunning()) {
+ aThread->Start();
+ }
+
+ if (sManagers.count(aOwner) != 0) {
+ printf_stderr("SharedBufferManagerParent already exists.");
+ }
+ mOwner = aOwner;
+ sManagers[aOwner] = this;
+}
+
+SharedBufferManagerParent::~SharedBufferManagerParent()
+{
+ MonitorAutoLock lock(*sManagerMonitor.get());
+ sManagers.erase(mOwner);
+ delete mThread;
+}
+
+void
+SharedBufferManagerParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ MutexAutoLock lock(mLock);
+ mDestroyed = true;
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+ mBuffers.clear();
+#endif
+ RefPtr<DeleteSharedBufferManagerParentTask> task =
+ new DeleteSharedBufferManagerParentTask(UniquePtr<SharedBufferManagerParent>(this));
+ NS_DispatchToMainThread(task.forget());
+}
+
+static void
+ConnectSharedBufferManagerInParentProcess(SharedBufferManagerParent* aManager,
+ Transport* aTransport,
+ base::ProcessId aOtherPid)
+{
+ aManager->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ParentSide);
+}
+
+PSharedBufferManagerParent* SharedBufferManagerParent::Create(Transport* aTransport,
+ ProcessId aOtherPid)
+{
+ base::Thread* thread = nullptr;
+ char thrname[128];
+ SprintfLiteral(thrname, "BufMgrParent#%d", aOtherPid);
+ thread = new base::Thread(thrname);
+
+ SharedBufferManagerParent* manager = new SharedBufferManagerParent(aOtherPid, thread);
+ if (!thread->IsRunning()) {
+ thread->Start();
+ }
+ thread->message_loop()->PostTask(NewRunnableFunction(ConnectSharedBufferManagerInParentProcess,
+ manager, aTransport, aOtherPid));
+ return manager;
+}
+
+bool SharedBufferManagerParent::RecvAllocateGrallocBuffer(const IntSize& aSize, const uint32_t& aFormat, const uint32_t& aUsage, mozilla::layers::MaybeMagicGrallocBufferHandle* aHandle)
+{
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+
+ *aHandle = null_t();
+
+ if (aFormat == 0 || aUsage == 0) {
+ printf_stderr("SharedBufferManagerParent::RecvAllocateGrallocBuffer -- format and usage must be non-zero");
+ return false;
+ }
+
+ if (aSize.width <= 0 || aSize.height <= 0) {
+ printf_stderr("SharedBufferManagerParent::RecvAllocateGrallocBuffer -- requested gralloc buffer size is invalid");
+ return false;
+ }
+
+ // If the requested size is too big (i.e. exceeds the commonly used max GL texture size)
+ // then we risk OOMing the parent process. It's better to just deny the allocation and
+ // kill the child process, which is what the following code does.
+ // TODO: actually use GL_MAX_TEXTURE_SIZE instead of hardcoding 4096
+ if (aSize.width > 4096 || aSize.height > 4096) {
+ printf_stderr("SharedBufferManagerParent::RecvAllocateGrallocBuffer -- requested gralloc buffer is too big.");
+ return false;
+ }
+
+ sp<GraphicBuffer> outgoingBuffer = new GraphicBuffer(aSize.width, aSize.height, aFormat, aUsage);
+ if (!outgoingBuffer.get() || outgoingBuffer->initCheck() != NO_ERROR) {
+ printf_stderr("SharedBufferManagerParent::RecvAllocateGrallocBuffer -- gralloc buffer allocation failed");
+ return true;
+ }
+
+ int64_t bufferKey;
+ {
+ MonitorAutoLock lock(*sManagerMonitor.get());
+ bufferKey = ++sBufferKey;
+ }
+ GrallocBufferRef ref;
+ ref.mOwner = mOwner;
+ ref.mKey = bufferKey;
+ *aHandle = MagicGrallocBufferHandle(outgoingBuffer, ref);
+
+ {
+ MutexAutoLock lock(mLock);
+ mBuffers[bufferKey] = outgoingBuffer;
+ }
+#endif
+ return true;
+}
+
+bool SharedBufferManagerParent::RecvDropGrallocBuffer(const mozilla::layers::MaybeMagicGrallocBufferHandle& handle)
+{
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+ NS_ASSERTION(handle.type() == MaybeMagicGrallocBufferHandle::TGrallocBufferRef, "We shouldn't interact with the real buffer!");
+ int64_t bufferKey = handle.get_GrallocBufferRef().mKey;
+ sp<GraphicBuffer> buf = GetGraphicBuffer(bufferKey);
+ MOZ_ASSERT(buf.get());
+ MutexAutoLock lock(mLock);
+ NS_ASSERTION(mBuffers.count(bufferKey) == 1, "No such buffer");
+ mBuffers.erase(bufferKey);
+
+ if(!buf.get()) {
+ printf_stderr("SharedBufferManagerParent::RecvDropGrallocBuffer -- invalid buffer key.");
+ return true;
+ }
+
+#endif
+ return true;
+}
+
+/*static*/
+void SharedBufferManagerParent::DropGrallocBufferSync(SharedBufferManagerParent* mgr, mozilla::layers::SurfaceDescriptor aDesc)
+{
+ mgr->DropGrallocBufferImpl(aDesc);
+}
+
+/*static*/
+void SharedBufferManagerParent::DropGrallocBuffer(ProcessId id, mozilla::layers::SurfaceDescriptor aDesc)
+{
+ if (aDesc.type() != SurfaceDescriptor::TSurfaceDescriptorGralloc) {
+ return;
+ }
+
+ MonitorAutoLock lock(*sManagerMonitor.get());
+ SharedBufferManagerParent* mgr = SharedBufferManagerParent::GetInstance(id);
+ if (!mgr) {
+ return;
+ }
+
+ MutexAutoLock mgrlock(mgr->mLock);
+ if (mgr->mDestroyed) {
+ return;
+ }
+
+ if (PlatformThread::CurrentId() == mgr->mThread->thread_id()) {
+ MOZ_CRASH("GFX: SharedBufferManagerParent::DropGrallocBuffer should not be called on SharedBufferManagerParent thread");
+ } else {
+ mgr->mThread->message_loop()->PostTask(
+ NewRunnableFunction(&DropGrallocBufferSync, mgr, aDesc));
+ }
+ return;
+}
+
+void SharedBufferManagerParent::DropGrallocBufferImpl(mozilla::layers::SurfaceDescriptor aDesc)
+{
+ MutexAutoLock lock(mLock);
+ if (mDestroyed) {
+ return;
+ }
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+ int64_t key = -1;
+ MaybeMagicGrallocBufferHandle handle;
+ if (aDesc.type() == SurfaceDescriptor::TSurfaceDescriptorGralloc) {
+ handle = aDesc.get_SurfaceDescriptorGralloc().buffer();
+ } else {
+ return;
+ }
+
+ if (handle.type() == MaybeMagicGrallocBufferHandle::TGrallocBufferRef) {
+ key = handle.get_GrallocBufferRef().mKey;
+ } else if (handle.type() == MaybeMagicGrallocBufferHandle::TMagicGrallocBufferHandle) {
+ key = handle.get_MagicGrallocBufferHandle().mRef.mKey;
+ }
+
+ NS_ASSERTION(key != -1, "Invalid buffer key");
+ NS_ASSERTION(mBuffers.count(key) == 1, "No such buffer");
+ mBuffers.erase(key);
+ mozilla::Unused << SendDropGrallocBuffer(handle);
+#endif
+}
+
+MessageLoop* SharedBufferManagerParent::GetMessageLoop()
+{
+ return mThread->message_loop();
+}
+
+SharedBufferManagerParent* SharedBufferManagerParent::GetInstance(ProcessId id)
+{
+ NS_ASSERTION(sManagers.count(id) == 1, "No BufferManager for the process");
+ if (sManagers.count(id) == 1) {
+ return sManagers[id];
+ } else {
+ return nullptr;
+ }
+}
+
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+android::sp<android::GraphicBuffer>
+SharedBufferManagerParent::GetGraphicBuffer(int64_t key)
+{
+ MutexAutoLock lock(mLock);
+ if (mBuffers.count(key) == 1) {
+ return mBuffers[key];
+ } else {
+ // The buffer can be dropped, or invalid
+ printf_stderr("SharedBufferManagerParent::GetGraphicBuffer -- invalid key");
+ return nullptr;
+ }
+}
+
+android::sp<android::GraphicBuffer>
+SharedBufferManagerParent::GetGraphicBuffer(GrallocBufferRef aRef)
+{
+ MonitorAutoLock lock(*sManagerMonitor.get());
+ SharedBufferManagerParent* parent = GetInstance(aRef.mOwner);
+ if (!parent) {
+ return nullptr;
+ }
+ return parent->GetGraphicBuffer(aRef.mKey);
+}
+#endif
+
+} /* namespace layers */
+} /* namespace mozilla */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/SharedBufferManagerParent.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* 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 SharedBufferManagerPARENT_H_
+#define SharedBufferManagerPARENT_H_
+
+#include "mozilla/Atomics.h" // for Atomic
+#include "mozilla/layers/PSharedBufferManagerParent.h"
+#include "mozilla/StaticPtr.h"
+
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/Mutex.h" // for Mutex
+
+namespace android {
+class GraphicBuffer;
+}
+#endif
+
+namespace base {
+class Thread;
+} // namespace base
+
+namespace mozilla {
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+class Mutex;
+#endif
+
+namespace layers {
+
+class SharedBufferManagerParent : public PSharedBufferManagerParent
+{
+friend class GrallocReporter;
+public:
+ /**
+ * Create a SharedBufferManagerParent for child process, and link to the child side before leaving
+ */
+ static PSharedBufferManagerParent* Create(Transport* aTransport, ProcessId aOtherProcess);
+
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+ android::sp<android::GraphicBuffer> GetGraphicBuffer(int64_t key);
+ static android::sp<android::GraphicBuffer> GetGraphicBuffer(GrallocBufferRef aRef);
+#endif
+ /**
+ * Create a SharedBufferManagerParent but do not open the link
+ */
+ SharedBufferManagerParent(ProcessId aOwner, base::Thread* aThread);
+ virtual ~SharedBufferManagerParent();
+
+ /**
+ * When the IPC channel down or something bad make this Manager die, clear all the buffer reference!
+ */
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual bool RecvAllocateGrallocBuffer(const IntSize&, const uint32_t&, const uint32_t&, mozilla::layers::MaybeMagicGrallocBufferHandle*) override;
+ virtual bool RecvDropGrallocBuffer(const mozilla::layers::MaybeMagicGrallocBufferHandle& handle) override;
+
+ /**
+ * Break the buffer's sharing state, decrease buffer reference for both side
+ */
+ static void DropGrallocBuffer(ProcessId id, mozilla::layers::SurfaceDescriptor aDesc);
+
+ MessageLoop* GetMessageLoop();
+
+protected:
+
+ /**
+ * Break the buffer's sharing state, decrease buffer reference for both side
+ *
+ * Must be called from SharedBufferManagerParent's thread
+ */
+ void DropGrallocBufferImpl(mozilla::layers::SurfaceDescriptor aDesc);
+
+ // dispatched function
+ static void DropGrallocBufferSync(SharedBufferManagerParent* mgr, mozilla::layers::SurfaceDescriptor aDesc);
+
+ /**
+ * Function for find the buffer owner, most buffer passing on IPC contains only owner/key pair.
+ * Use these function to access the real buffer.
+ * Caller needs to hold sManagerMonitor.
+ */
+ static SharedBufferManagerParent* GetInstance(ProcessId id);
+
+ /**
+ * All living SharedBufferManager instances used to find the buffer owner, and parent->child IPCs
+ */
+ static std::map<base::ProcessId, SharedBufferManagerParent*> sManagers;
+
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+ /**
+ * Buffers owned by this SharedBufferManager pair
+ */
+ std::map<int64_t, android::sp<android::GraphicBuffer> > mBuffers;
+#endif
+
+ base::ProcessId mOwner;
+ base::Thread* mThread;
+ bool mDestroyed;
+ Mutex mLock;
+
+ static uint64_t sBufferKey;
+ static StaticAutoPtr<Monitor> sManagerMonitor;
+};
+
+} /* namespace layers */
+} /* namespace mozilla */
+#endif /* SharedBufferManagerPARENT_H_ */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/opengl/GrallocTextureClient.cpp
@@ -0,0 +1,460 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef MOZ_WIDGET_GONK
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/layers/AsyncTransactionTracker.h" // for AsyncTransactionTracker
+#include "mozilla/layers/GrallocTextureClient.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/ShadowLayerUtilsGralloc.h"
+#include "mozilla/layers/SharedBufferManagerChild.h"
+#include "gfx2DGlue.h"
+#include "gfxPrefs.h" // for gfxPrefs
+#include "SharedSurfaceGralloc.h"
+
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+#include <ui/Fence.h>
+#endif
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+using namespace android;
+
+static bool
+DisableGralloc(SurfaceFormat aFormat, const gfx::IntSize& aSizeHint)
+{
+ if (gfxPrefs::DisableGralloc()) {
+ return true;
+ }
+ if (aFormat == gfx::SurfaceFormat::A8) {
+ return true;
+ }
+
+ return false;
+}
+
+gfx::SurfaceFormat
+SurfaceFormatForPixelFormat(android::PixelFormat aFormat)
+{
+ switch (aFormat) {
+ case PIXEL_FORMAT_RGBA_8888:
+ return gfx::SurfaceFormat::R8G8B8A8;
+ case PIXEL_FORMAT_BGRA_8888:
+ return gfx::SurfaceFormat::B8G8R8A8;
+ case PIXEL_FORMAT_RGBX_8888:
+ return gfx::SurfaceFormat::R8G8B8X8;
+ case PIXEL_FORMAT_RGB_565:
+ return gfx::SurfaceFormat::R5G6B5_UINT16;
+ case HAL_PIXEL_FORMAT_YV12:
+ return gfx::SurfaceFormat::YUV;
+ default:
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+}
+
+bool
+IsGrallocRBSwapped(gfx::SurfaceFormat aFormat) {
+ switch (aFormat) {
+ case gfx::SurfaceFormat::B8G8R8A8:
+ case gfx::SurfaceFormat::B8G8R8X8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+uint32_t GetAndroidFormat(gfx::SurfaceFormat aFormat)
+{
+ switch (aFormat) {
+ case gfx::SurfaceFormat::R8G8B8A8:
+ case gfx::SurfaceFormat::B8G8R8A8:
+ return android::PIXEL_FORMAT_RGBA_8888;
+ case gfx::SurfaceFormat::R8G8B8X8:
+ case gfx::SurfaceFormat::B8G8R8X8:
+ return android::PIXEL_FORMAT_RGBX_8888;
+ case gfx::SurfaceFormat::R5G6B5_UINT16:
+ return android::PIXEL_FORMAT_RGB_565;
+ case gfx::SurfaceFormat::YUV:
+ return HAL_PIXEL_FORMAT_YV12;
+ case gfx::SurfaceFormat::A8:
+ NS_WARNING("gralloc does not support SurfaceFormat::A8");
+ default:
+ NS_WARNING("Unsupported surface format");
+ return android::PIXEL_FORMAT_UNKNOWN;
+ }
+}
+
+GrallocTextureData::GrallocTextureData(MaybeMagicGrallocBufferHandle aGrallocHandle,
+ gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2DBackend)
+: mSize(aSize)
+, mFormat(aFormat)
+, mMoz2DBackend(aMoz2DBackend)
+, mGrallocHandle(aGrallocHandle)
+, mMappedBuffer(nullptr)
+, mMediaBuffer(nullptr)
+{
+ mGraphicBuffer = GetGraphicBufferFrom(aGrallocHandle);
+ MOZ_COUNT_CTOR(GrallocTextureData);
+}
+
+GrallocTextureData::~GrallocTextureData()
+{
+ MOZ_COUNT_DTOR(GrallocTextureData);
+}
+
+void
+GrallocTextureData::Deallocate(LayersIPCChannel* aAllocator)
+{
+ MOZ_ASSERT(aAllocator);
+ if (aAllocator && aAllocator->IPCOpen()) {
+ SharedBufferManagerChild::GetSingleton()->DeallocGrallocBuffer(mGrallocHandle);
+ }
+
+ mGrallocHandle = null_t();
+ mGraphicBuffer = nullptr;
+}
+
+void
+GrallocTextureData::Forget(LayersIPCChannel* aAllocator)
+{
+ MOZ_ASSERT(aAllocator);
+ if (aAllocator && aAllocator->IPCOpen()) {
+ SharedBufferManagerChild::GetSingleton()->DropGrallocBuffer(mGrallocHandle);
+ }
+
+ mGrallocHandle = null_t();
+ mGraphicBuffer = nullptr;
+}
+
+void
+GrallocTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ aInfo.format = mFormat;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = true;
+ aInfo.supportsMoz2D = true;
+ aInfo.canExposeMappedData = true;
+}
+
+bool
+GrallocTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ aOutDescriptor = SurfaceDescriptorGralloc(mGrallocHandle, gfx::IsOpaque(mFormat));
+ return true;
+}
+
+void
+GrallocTextureData::WaitForFence(FenceHandle* aFence)
+{
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION < 21 && ANDROID_VERSION >= 17
+ if (aFence && aFence->IsValid()) {
+ RefPtr<FenceHandle::FdObj> fdObj = aFence->GetAndResetFdObj();
+ android::sp<Fence> fence = new Fence(fdObj->GetAndResetFd());
+#if ANDROID_VERSION == 17
+ fence->waitForever(1000, "GrallocTextureData::Lock");
+ // 1000 is what Android uses. It is a warning timeout in ms.
+ // This timeout was removed in ANDROID_VERSION 18.
+#else
+ fence->waitForever("GrallocTextureData::Lock");
+#endif
+ }
+#endif
+}
+
+bool
+GrallocTextureData::Lock(OpenMode aMode, FenceHandle* aReleaseFence)
+{
+ MOZ_ASSERT(!mMappedBuffer);
+
+ uint32_t usage = 0;
+ if (aMode & OpenMode::OPEN_READ) {
+ usage |= GRALLOC_USAGE_SW_READ_OFTEN;
+ }
+ if (aMode & OpenMode::OPEN_WRITE) {
+ usage |= GRALLOC_USAGE_SW_WRITE_OFTEN;
+ }
+
+ void** mappedBufferPtr = reinterpret_cast<void**>(&mMappedBuffer);
+
+ int32_t rv = 0;
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
+ if (aReleaseFence) {
+ RefPtr<FenceHandle::FdObj> fdObj = aReleaseFence->GetAndResetFdObj();
+ rv = mGraphicBuffer->lockAsync(usage, mappedBufferPtr,
+ fdObj->GetAndResetFd());
+ } else {
+ rv = mGraphicBuffer->lock(usage, mappedBufferPtr);
+ }
+#else
+ // older versions of android don't have lockAsync
+ WaitForFence(aReleaseFence);
+ rv = mGraphicBuffer->lock(usage, mappedBufferPtr);
+#endif
+
+ if (rv) {
+ mMappedBuffer = nullptr;
+ NS_WARNING("Couldn't lock graphic buffer");
+ return false;
+ }
+
+ return true;
+}
+
+void
+GrallocTextureData::Unlock()
+{
+ MOZ_ASSERT(mMappedBuffer);
+ mMappedBuffer = nullptr;
+ mGraphicBuffer->unlock();
+}
+
+already_AddRefed<gfx::DrawTarget>
+GrallocTextureData::BorrowDrawTarget()
+{
+ MOZ_ASSERT(mMappedBuffer);
+ if (!mMappedBuffer) {
+ return nullptr;
+ }
+ long byteStride = mGraphicBuffer->getStride() * BytesPerPixel(mFormat);
+ return gfxPlatform::CreateDrawTargetForData(mMappedBuffer, mSize,
+ byteStride, mFormat);
+}
+
+bool
+GrallocTextureData::BorrowMappedData(MappedTextureData& aMap)
+{
+ if (mFormat == gfx::SurfaceFormat::YUV || !mMappedBuffer) {
+ return false;
+ }
+
+ aMap.data = mMappedBuffer;
+ aMap.size = mSize;
+ aMap.stride = mGraphicBuffer->getStride() * BytesPerPixel(mFormat);
+ aMap.format = mFormat;
+
+ return true;
+}
+
+bool
+GrallocTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ MOZ_ASSERT(mMappedBuffer, "Calling TextureClient::BorrowDrawTarget without locking :(");
+
+ if (!mMappedBuffer) {
+ return false;
+ }
+
+ RefPtr<DataSourceSurface> srcSurf = aSurface->GetDataSurface();
+
+ if (!srcSurf) {
+ gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (GTC).";
+ return false;
+ }
+
+ gfx::SurfaceFormat format = SurfaceFormatForPixelFormat(mGraphicBuffer->getPixelFormat());
+ if (mSize != srcSurf->GetSize() || mFormat != srcSurf->GetFormat()) {
+ gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format! This: " << mSize << " " << format << " Other: " << srcSurf->GetSize() << " " << srcSurf->GetFormat();
+ return false;
+ }
+
+ long pixelStride = mGraphicBuffer->getStride();
+ long byteStride = pixelStride * BytesPerPixel(format);
+
+ DataSourceSurface::MappedSurface sourceMap;
+
+ if (!srcSurf->Map(DataSourceSurface::READ, &sourceMap)) {
+ gfxCriticalError() << "Failed to map source surface for UpdateFromSurface (GTC).";
+ return false;
+ }
+
+ for (int y = 0; y < srcSurf->GetSize().height; y++) {
+ memcpy(mMappedBuffer + byteStride * y,
+ sourceMap.mData + sourceMap.mStride * y,
+ srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
+ }
+
+ srcSurf->Unmap();
+
+ return true;
+}
+
+// static
+GrallocTextureData*
+GrallocTextureData::Create(gfx::IntSize aSize, AndroidFormat aAndroidFormat,
+ gfx::BackendType aMoz2dBackend, uint32_t aUsage,
+ LayersIPCChannel* aAllocator)
+{
+ if (!aAllocator || !aAllocator->IPCOpen()) {
+ return nullptr;
+ }
+ gfx::SurfaceFormat format;
+ switch (aAndroidFormat) {
+ case android::PIXEL_FORMAT_RGBA_8888:
+ format = gfx::SurfaceFormat::B8G8R8A8;
+ break;
+ case android::PIXEL_FORMAT_BGRA_8888:
+ format = gfx::SurfaceFormat::B8G8R8A8;
+ break;
+ case android::PIXEL_FORMAT_RGBX_8888:
+ format = gfx::SurfaceFormat::B8G8R8X8;
+ break;
+ case android::PIXEL_FORMAT_RGB_565:
+ format = gfx::SurfaceFormat::R5G6B5_UINT16;
+ break;
+ case HAL_PIXEL_FORMAT_YV12:
+ format = gfx::SurfaceFormat::YUV;
+ break;
+ default:
+ format = gfx::SurfaceFormat::UNKNOWN;
+ }
+
+ if (DisableGralloc(format, aSize)) {
+ return nullptr;
+ }
+
+ MaybeMagicGrallocBufferHandle handle;
+ if (!SharedBufferManagerChild::GetSingleton()->AllocGrallocBuffer(aSize, aAndroidFormat, aUsage, &handle)) {
+ return nullptr;
+ }
+
+ sp<GraphicBuffer> graphicBuffer = GetGraphicBufferFrom(handle);
+ if (!graphicBuffer.get()) {
+ return nullptr;
+ }
+
+ if (graphicBuffer->initCheck() != NO_ERROR) {
+ return nullptr;
+ }
+
+ return new GrallocTextureData(handle, aSize, format, aMoz2dBackend);
+}
+
+// static
+GrallocTextureData*
+GrallocTextureData::CreateForDrawing(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2dBackend,
+ LayersIPCChannel* aAllocator)
+{
+ if (DisableGralloc(aFormat, aSize)) {
+ return nullptr;
+ }
+
+#if ANDROID_VERSION <= 15
+ // Adreno 200 has a problem of drawing gralloc buffer width less than 64 and
+ // drawing gralloc buffer with a height 9px-16px.
+ // See Bug 983971.
+ // We only have this restriction in TextureClients that we'll use for drawing
+ // (not with WebGL for instance). Not sure why that's OK, but we have tests that
+ // rely on being able to create 32x32 webgl canvases with gralloc, so moving
+ // this check in DisableGralloc will break them.
+ if (aSize.width < 64 || aSize.height < 32) {
+ return nullptr;
+ }
+#endif
+
+ uint32_t usage = android::GraphicBuffer::USAGE_SW_READ_OFTEN |
+ android::GraphicBuffer::USAGE_SW_WRITE_OFTEN |
+ android::GraphicBuffer::USAGE_HW_TEXTURE;
+ auto data = GrallocTextureData::Create(aSize, GetAndroidFormat(aFormat),
+ aMoz2dBackend, usage, aAllocator);
+
+ if (!data) {
+ return nullptr;
+ }
+
+ DebugOnly<gfx::SurfaceFormat> grallocFormat =
+ SurfaceFormatForPixelFormat(data->mGraphicBuffer->getPixelFormat());
+ // mFormat may be different from the format the graphic buffer reports if we
+ // swap the R and B channels but we should always have at least the same bytes
+ // per pixel!
+ MOZ_ASSERT(BytesPerPixel(data->mFormat) == BytesPerPixel(grallocFormat));
+
+ return data;
+}
+
+TextureFlags
+GrallocTextureData::GetTextureFlags() const
+{
+ if (IsGrallocRBSwapped(mFormat)) {
+ return TextureFlags::RB_SWAPPED;
+ }
+ return TextureFlags::NO_FLAGS;
+}
+
+
+// static
+GrallocTextureData*
+GrallocTextureData::CreateForYCbCr(gfx::IntSize aYSize, gfx::IntSize aCbCrSize,
+ LayersIPCChannel* aAllocator)
+{
+ MOZ_ASSERT(aYSize.width == aCbCrSize.width * 2);
+ MOZ_ASSERT(aYSize.height == aCbCrSize.height * 2);
+ return GrallocTextureData::Create(aYSize, HAL_PIXEL_FORMAT_YV12,
+ gfx::BackendType::NONE,
+ android::GraphicBuffer::USAGE_SW_READ_OFTEN,
+ aAllocator);
+}
+
+// static
+GrallocTextureData*
+GrallocTextureData::CreateForGLRendering(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ LayersIPCChannel* aAllocator)
+{
+ if (aFormat == gfx::SurfaceFormat::YUV) {
+ return nullptr;
+ }
+ uint32_t usage = android::GraphicBuffer::USAGE_HW_RENDER |
+ android::GraphicBuffer::USAGE_HW_TEXTURE;
+ return GrallocTextureData::Create(aSize, GetAndroidFormat(aFormat),
+ gfx::BackendType::NONE, usage, aAllocator);
+}
+
+// static
+already_AddRefed<TextureClient>
+GrallocTextureData::TextureClientFromSharedSurface(gl::SharedSurface* abstractSurf,
+ TextureFlags flags)
+{
+ auto surf = gl::SharedSurface_Gralloc::Cast(abstractSurf);
+
+ RefPtr<TextureClient> ret = surf->GetTextureClient();
+
+ TextureFlags mask = TextureFlags::ORIGIN_BOTTOM_LEFT |
+ TextureFlags::RB_SWAPPED |
+ TextureFlags::NON_PREMULTIPLIED;
+ TextureFlags required = flags & mask;
+ TextureFlags present = ret->GetFlags() & mask;
+
+ if (present != required) {
+ printf_stderr("Present flags: 0x%x. Required: 0x%x.\n",
+ (uint32_t)present,
+ (uint32_t)required);
+ MOZ_CRASH("Flag requirement mismatch.");
+ }
+ return ret.forget();
+}
+
+TextureData*
+GrallocTextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ TextureAllocationFlags aAllocFlags) const
+{
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ return GrallocTextureData::CreateForYCbCr(mSize, mSize*2, aAllocator);
+ } else {
+ return GrallocTextureData::CreateForDrawing(mSize, mFormat, mMoz2DBackend, aAllocator);
+ }
+}
+
+} // namesapace layers
+} // namesapace mozilla
+
+#endif // MOZ_WIDGET_GONK
new file mode 100644
--- /dev/null
+++ b/gfx/layers/opengl/GrallocTextureClient.h
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_GRALLOCTEXTURECLIENT_H
+#define MOZILLA_GFX_GRALLOCTEXTURECLIENT_H
+#ifdef MOZ_WIDGET_GONK
+
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/FenceUtils.h" // for FenceHandle
+#include "mozilla/layers/ShadowLayerUtilsGralloc.h"
+#include <ui/GraphicBuffer.h>
+
+
+namespace android {
+class MediaBuffer;
+};
+
+namespace mozilla {
+namespace gl {
+class SharedSurface;
+}
+
+namespace layers {
+
+/// A TextureData implementation based on android::GraphicBuffer (also referred to
+/// as "gralloc").
+///
+/// Gralloc lets us map texture data in memory (accessible through pointers)
+/// and also use it directly as an OpenGL texture without the cost of texture
+/// uploads.
+/// Gralloc buffers can also be shared accros processes.
+///
+/// More info about Gralloc here: https://wiki.mozilla.org/Platform/GFX/Gralloc
+///
+/// This is only used in Firefox OS
+class GrallocTextureData : public TextureData {
+public:
+ typedef uint32_t AndroidFormat;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual bool Lock(OpenMode aMode, FenceHandle* aFence) override;
+
+ virtual void Unlock() override;
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ virtual bool BorrowMappedData(MappedTextureData& aMap) override;
+
+ virtual void Deallocate(LayersIPCChannel*) override;
+
+ virtual void Forget(LayersIPCChannel*) override;
+
+ static GrallocTextureData* CreateForDrawing(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2dBackend,
+ LayersIPCChannel* aAllocator);
+
+ static GrallocTextureData* CreateForYCbCr(gfx::IntSize aYSize, gfx::IntSize aCbCrSize,
+ LayersIPCChannel* aAllocator);
+
+ static GrallocTextureData* CreateForGLRendering(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ LayersIPCChannel* aAllocator);
+
+ static GrallocTextureData* Create(gfx::IntSize aSize, AndroidFormat aFormat,
+ gfx::BackendType aMoz2DBackend, uint32_t aUsage,
+ LayersIPCChannel* aAllocator);
+
+
+ static already_AddRefed<TextureClient>
+ TextureClientFromSharedSurface(gl::SharedSurface* abstractSurf, TextureFlags flags);
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ // use TextureClient's default implementation
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ /// Hold android::MediaBuffer.
+ /// MediaBuffer needs to be add refed to keep MediaBuffer alive while the texture
+ /// is in use.
+ ///
+ /// TODO - ideally we should be able to put the MediaBuffer in the texture's
+ /// constructor and not expose these methods.
+ void SetMediaBuffer(android::MediaBuffer* aMediaBuffer) { mMediaBuffer = aMediaBuffer; }
+ android::MediaBuffer* GetMediaBuffer() { return mMediaBuffer; }
+
+ android::sp<android::GraphicBuffer> GetGraphicBuffer() { return mGraphicBuffer; }
+
+ virtual void WaitForFence(FenceHandle* aFence) override;
+
+ ~GrallocTextureData();
+
+ virtual TextureFlags GetTextureFlags() const override;
+
+ virtual GrallocTextureData* AsGrallocTextureData() { return this; }
+
+protected:
+ GrallocTextureData(MaybeMagicGrallocBufferHandle aGrallocHandle,
+ gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aMoz2DBackend);
+
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ gfx::BackendType mMoz2DBackend;
+
+ MaybeMagicGrallocBufferHandle mGrallocHandle;
+ android::sp<android::GraphicBuffer> mGraphicBuffer;
+
+ // Points to a mapped gralloc buffer between calls to lock and unlock.
+ // Should be null outside of the lock-unlock pair.
+ uint8_t* mMappedBuffer;
+
+ android::MediaBuffer* mMediaBuffer;
+};
+
+gfx::SurfaceFormat SurfaceFormatForPixelFormat(android::PixelFormat aFormat);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZ_WIDGET_GONK
+#endif
new file mode 100644
--- /dev/null
+++ b/gfx/layers/opengl/GrallocTextureHost.cpp
@@ -0,0 +1,480 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "base/process.h"
+#include "GLContext.h"
+#include "gfx2DGlue.h"
+#include <ui/GraphicBuffer.h>
+#include "GrallocImages.h" // for GrallocImage
+#include "GLLibraryEGL.h" // for GLLibraryEGL
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/DataSurfaceHelpers.h"
+#include "mozilla/layers/GrallocTextureHost.h"
+#include "mozilla/layers/SharedBufferManagerParent.h"
+#include "EGLImageHelpers.h"
+#include "GLReadTexImageHelper.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace android;
+using namespace mozilla::gl;
+
+static gfx::SurfaceFormat
+SurfaceFormatForAndroidPixelFormat(android::PixelFormat aFormat,
+ TextureFlags aFlags)
+{
+ bool swapRB = bool(aFlags & TextureFlags::RB_SWAPPED);
+ switch (aFormat) {
+ case android::PIXEL_FORMAT_BGRA_8888:
+ return swapRB ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::B8G8R8A8;
+ case android::PIXEL_FORMAT_RGBA_8888:
+ return swapRB ? gfx::SurfaceFormat::B8G8R8A8 : gfx::SurfaceFormat::R8G8B8A8;
+ case android::PIXEL_FORMAT_RGBX_8888:
+ return swapRB ? gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::R8G8B8X8;
+ case android::PIXEL_FORMAT_RGB_565:
+ return gfx::SurfaceFormat::R5G6B5_UINT16;
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
+ case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
+ case HAL_PIXEL_FORMAT_YV12:
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+#endif
+ return gfx::SurfaceFormat::R8G8B8A8; // yup, use SurfaceFormat::R8G8B8A8 even though it's a YUV texture. This is an external texture.
+ default:
+ if (aFormat >= 0x100 && aFormat <= 0x1FF) {
+ // Reserved range for HAL specific formats.
+ return gfx::SurfaceFormat::R8G8B8A8;
+ } else {
+ // This is not super-unreachable, there's a bunch of hypothetical pixel
+ // formats we don't deal with.
+ // We only want to abort in debug builds here, since if we crash here
+ // we'll take down the compositor process and thus the phone. This seems
+ // like undesirable behaviour. We'd rather have a subtle artifact.
+ printf_stderr(" xxxxx unknow android format %i\n", (int)aFormat);
+ MOZ_ASSERT(false, "Unknown Android pixel format.");
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+ }
+}
+
+static GLenum
+TextureTargetForAndroidPixelFormat(android::PixelFormat aFormat)
+{
+ switch (aFormat) {
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
+ case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
+ case HAL_PIXEL_FORMAT_YV12:
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+#endif
+ return LOCAL_GL_TEXTURE_EXTERNAL;
+ case android::PIXEL_FORMAT_BGRA_8888:
+ case android::PIXEL_FORMAT_RGBA_8888:
+ case android::PIXEL_FORMAT_RGBX_8888:
+ case android::PIXEL_FORMAT_RGB_565:
+ return LOCAL_GL_TEXTURE_2D;
+ default:
+ if (aFormat >= 0x100 && aFormat <= 0x1FF) {
+ // Reserved range for HAL specific formats.
+ return LOCAL_GL_TEXTURE_EXTERNAL;
+ } else {
+ // This is not super-unreachable, there's a bunch of hypothetical pixel
+ // formats we don't deal with.
+ // We only want to abort in debug builds here, since if we crash here
+ // we'll take down the compositor process and thus the phone. This seems
+ // like undesirable behaviour. We'd rather have a subtle artifact.
+ MOZ_ASSERT(false, "Unknown Android pixel format.");
+ return LOCAL_GL_TEXTURE_EXTERNAL;
+ }
+ }
+}
+
+GrallocTextureHostOGL::GrallocTextureHostOGL(TextureFlags aFlags,
+ const SurfaceDescriptorGralloc& aDescriptor)
+ : TextureHost(aFlags)
+ , mGrallocHandle(aDescriptor)
+ , mSize(0, 0)
+ , mCropSize(0, 0)
+ , mFormat(gfx::SurfaceFormat::UNKNOWN)
+ , mEGLImage(EGL_NO_IMAGE)
+ , mIsOpaque(aDescriptor.isOpaque())
+{
+ android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
+ MOZ_ASSERT(graphicBuffer);
+
+ if (graphicBuffer) {
+ mFormat =
+ SurfaceFormatForAndroidPixelFormat(graphicBuffer->getPixelFormat(),
+ aFlags & TextureFlags::RB_SWAPPED);
+ mSize = gfx::IntSize(graphicBuffer->getWidth(), graphicBuffer->getHeight());
+ mCropSize = mSize;
+ } else {
+ printf_stderr("gralloc buffer is nullptr\n");
+ }
+}
+
+GrallocTextureHostOGL::~GrallocTextureHostOGL()
+{
+ DestroyEGLImage();
+}
+
+void
+GrallocTextureHostOGL::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = AssertGLCompositor(aCompositor);
+ if (mGLTextureSource) {
+ mGLTextureSource->SetCompositor(mCompositor);
+ }
+
+ if (mCompositor && aCompositor != mCompositor) {
+ DestroyEGLImage();
+ }
+}
+
+bool
+GrallocTextureHostOGL::Lock()
+{
+ return IsValid();
+}
+
+void
+GrallocTextureHostOGL::Unlock()
+{
+ // Unlock is done internally by binding the texture to another gralloc buffer
+}
+
+bool
+GrallocTextureHostOGL::IsValid() const
+{
+ android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
+ return graphicBuffer != nullptr;
+}
+
+gfx::SurfaceFormat
+GrallocTextureHostOGL::GetFormat() const
+{
+ return mFormat;
+}
+
+void
+GrallocTextureHostOGL::DeallocateSharedData()
+{
+ if (mGLTextureSource) {
+ mGLTextureSource = nullptr;
+ }
+
+ DestroyEGLImage();
+
+ if (mGrallocHandle.buffer().type() != MaybeMagicGrallocBufferHandle::Tnull_t) {
+ MaybeMagicGrallocBufferHandle handle = mGrallocHandle.buffer();
+ base::ProcessId owner;
+ if (handle.type() == MaybeMagicGrallocBufferHandle::TGrallocBufferRef) {
+ owner = handle.get_GrallocBufferRef().mOwner;
+ }
+ else {
+ owner = handle.get_MagicGrallocBufferHandle().mRef.mOwner;
+ }
+
+ SharedBufferManagerParent::DropGrallocBuffer(owner, mGrallocHandle);
+ }
+}
+
+void
+GrallocTextureHostOGL::ForgetSharedData()
+{
+ if (mGLTextureSource) {
+ mGLTextureSource = nullptr;
+ }
+}
+
+void
+GrallocTextureHostOGL::DeallocateDeviceData()
+{
+ if (mGLTextureSource) {
+ mGLTextureSource = nullptr;
+ }
+ DestroyEGLImage();
+}
+
+LayerRenderState
+GrallocTextureHostOGL::GetRenderState()
+{
+ android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
+
+ if (graphicBuffer) {
+ LayerRenderStateFlags flags = LayerRenderStateFlags::LAYER_RENDER_STATE_DEFAULT;
+ if (mIsOpaque) {
+ flags |= LayerRenderStateFlags::OPAQUE;
+ }
+ if (mFlags & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+ flags |= LayerRenderStateFlags::ORIGIN_BOTTOM_LEFT;
+ }
+ if (mFlags & TextureFlags::RB_SWAPPED) {
+ flags |= LayerRenderStateFlags::FORMAT_RB_SWAP;
+ }
+ return LayerRenderState(graphicBuffer,
+ mCropSize,
+ flags,
+ this);
+ }
+
+ return LayerRenderState();
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+GrallocTextureHostOGL::GetAsSurface() {
+ android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
+ if (!graphicBuffer) {
+ return nullptr;
+ }
+ uint8_t* grallocData;
+ int32_t rv = graphicBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&grallocData));
+ if (rv) {
+ return nullptr;
+ }
+ RefPtr<gfx::DataSourceSurface> grallocTempSurf =
+ gfx::Factory::CreateWrappingDataSourceSurface(grallocData,
+ graphicBuffer->getStride() * android::bytesPerPixel(graphicBuffer->getPixelFormat()),
+ GetSize(), GetFormat());
+ if (!grallocTempSurf) {
+ graphicBuffer->unlock();
+ return nullptr;
+ }
+ RefPtr<gfx::DataSourceSurface> surf = CreateDataSourceSurfaceByCloning(grallocTempSurf);
+
+ graphicBuffer->unlock();
+
+ return surf.forget();
+}
+
+void
+GrallocTextureHostOGL::UnbindTextureSource()
+{
+ TextureHost::UnbindTextureSource();
+ // Clear the reference to the TextureSource (if any), because we know that
+ // another TextureHost is being bound to the TextureSource. This means that
+ // we will have to re-do gl->fEGLImageTargetTexture2D next time we go through
+ // BindTextureSource (otherwise we would have skipped it).
+ // Note that this doesn't "unlock" the gralloc buffer or force it to be
+ // detached, Although decreasing the refcount of the TextureSource may lead
+ // to the gl handle being destroyed, which would unlock the gralloc buffer.
+ // That said, this method is called before another TextureHost attaches to the
+ // TextureSource, which has the effect of unlocking the gralloc buffer. So when
+ // this is called we know we are going to be unlocked soon.
+ mGLTextureSource = nullptr;
+}
+
+GLenum GetTextureTarget(gl::GLContext* aGL, android::PixelFormat aFormat) {
+ MOZ_ASSERT(aGL);
+ if (aGL->Renderer() == gl::GLRenderer::SGX530 ||
+ aGL->Renderer() == gl::GLRenderer::SGX540) {
+ // SGX has a quirk that only TEXTURE_EXTERNAL works and any other value will
+ // result in black pixels when trying to draw from bound textures.
+ // Unfortunately, using TEXTURE_EXTERNAL on Adreno has a terrible effect on
+ // performance.
+ // See Bug 950050.
+ return LOCAL_GL_TEXTURE_EXTERNAL;
+ } else {
+ return TextureTargetForAndroidPixelFormat(aFormat);
+ }
+}
+
+void
+GrallocTextureHostOGL::DestroyEGLImage()
+{
+ // Only called when we want to get rid of the gralloc buffer, usually
+ // around the end of life of the TextureHost.
+ if (mEGLImage != EGL_NO_IMAGE && GetGLContext()) {
+ EGLImageDestroy(GetGLContext(), mEGLImage);
+ mEGLImage = EGL_NO_IMAGE;
+ }
+}
+
+void
+GrallocTextureHostOGL::PrepareTextureSource(CompositableTextureSourceRef& aTextureSource)
+{
+ // This happens during the layers transaction.
+ // All of the gralloc magic goes here. The only thing that happens externally
+ // and that is good to keep in mind is that when the TextureSource is deleted,
+ // it destroys its gl texture handle which is important for genlock.
+
+ // If this TextureHost's mGLTextureSource member is non-null, it means we are
+ // still bound to the TextureSource, in which case we can skip the driver
+ // overhead of binding the texture again (fEGLImageTargetTexture2D)
+ // As a result, if the TextureHost is used with several CompositableHosts,
+ // it will be bound to only one TextureSource, and we'll do the driver work
+ // only once, which is great. This means that all of the compositables that
+ // use this TextureHost will keep a reference to this TextureSource at least
+ // for the duration of this frame.
+
+ // If the compositable already has a TextureSource (the aTextureSource parameter),
+ // that is compatible and is not in use by several compositable, we try to
+ // attach to it. This has the effect of unlocking the previous TextureHost that
+ // we attached to the TextureSource (the previous frame)
+
+ // If the TextureSource used by the compositable is also used by other
+ // compositables (see NumCompositableRefs), we have to create a new TextureSource,
+ // because otherwise we would be modifying the content of every layer that uses
+ // the TextureSource in question, even thoug they don't use this TextureHost.
+
+ android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
+
+ MOZ_ASSERT(graphicBuffer);
+ if (!graphicBuffer) {
+ mGLTextureSource = nullptr;
+ return;
+ }
+
+ if (mGLTextureSource && !mGLTextureSource->IsValid()) {
+ mGLTextureSource = nullptr;
+ }
+
+ if (mGLTextureSource) {
+ // We are already attached to a TextureSource, nothing to do except tell
+ // the compositable to use it.
+ aTextureSource = mGLTextureSource.get();
+ return;
+ }
+
+ gl::GLContext* gl = GetGLContext();
+ if (!gl || !gl->MakeCurrent()) {
+ mGLTextureSource = nullptr;
+ return;
+ }
+
+ if (mEGLImage == EGL_NO_IMAGE) {
+ gfx::IntSize cropSize(0, 0);
+ if (mCropSize != mSize) {
+ cropSize = mCropSize;
+ }
+ // Should only happen the first time.
+ mEGLImage = EGLImageCreateFromNativeBuffer(gl, graphicBuffer->getNativeBuffer(), cropSize);
+ }
+
+ GLenum textureTarget = GetTextureTarget(gl, graphicBuffer->getPixelFormat());
+
+ GLTextureSource* glSource = aTextureSource.get() ?
+ aTextureSource->AsSourceOGL()->AsGLTextureSource() : nullptr;
+
+ bool shouldCreateTextureSource = !glSource || !glSource->IsValid()
+ || glSource->NumCompositableRefs() > 1
+ || glSource->GetTextureTarget() != textureTarget;
+
+ if (shouldCreateTextureSource) {
+ GLuint textureHandle;
+ gl->fGenTextures(1, &textureHandle);
+ gl->fBindTexture(textureTarget, textureHandle);
+ gl->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+ gl->fTexParameteri(textureTarget, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+ gl->fEGLImageTargetTexture2D(textureTarget, mEGLImage);
+
+ mGLTextureSource = new GLTextureSource(mCompositor, textureHandle, textureTarget,
+ mSize, mFormat);
+ aTextureSource = mGLTextureSource.get();
+ } else {
+ gl->fBindTexture(textureTarget, glSource->GetTextureHandle());
+
+ gl->fEGLImageTargetTexture2D(textureTarget, mEGLImage);
+ glSource->SetSize(mSize);
+ glSource->SetFormat(mFormat);
+ mGLTextureSource = glSource;
+ }
+}
+
+void
+GrallocTextureHostOGL::WaitAcquireFenceHandleSyncComplete()
+{
+ if (!mAcquireFenceHandle.IsValid()) {
+ return;
+ }
+
+ RefPtr<FenceHandle::FdObj> fence = mAcquireFenceHandle.GetAndResetFdObj();
+ int fenceFd = fence->GetAndResetFd();
+
+ EGLint attribs[] = {
+ LOCAL_EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd,
+ LOCAL_EGL_NONE
+ };
+
+ EGLSync sync = sEGLLibrary.fCreateSync(EGL_DISPLAY(),
+ LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID,
+ attribs);
+ if (!sync) {
+ NS_WARNING("failed to create native fence sync");
+ return;
+ }
+
+ // Wait sync complete with timeout.
+ // If a source of the fence becomes invalid because of error,
+ // fene complete is not signaled. See Bug 1061435.
+ EGLint status = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(),
+ sync,
+ 0,
+ 400000000 /*400 msec*/);
+ if (status != LOCAL_EGL_CONDITION_SATISFIED) {
+ NS_ERROR("failed to wait native fence sync");
+ }
+ MOZ_ALWAYS_TRUE( sEGLLibrary.fDestroySync(EGL_DISPLAY(), sync) );
+}
+
+void
+GrallocTextureHostOGL::SetCropRect(nsIntRect aCropRect)
+{
+ MOZ_ASSERT(aCropRect.TopLeft() == gfx::IntPoint(0, 0));
+ MOZ_ASSERT(!aCropRect.IsEmpty());
+ MOZ_ASSERT(aCropRect.width <= mSize.width);
+ MOZ_ASSERT(aCropRect.height <= mSize.height);
+
+ gfx::IntSize cropSize(aCropRect.width, aCropRect.height);
+ if (mCropSize == cropSize) {
+ return;
+ }
+
+ mCropSize = cropSize;
+ mGLTextureSource = nullptr;
+}
+
+bool
+GrallocTextureHostOGL::BindTextureSource(CompositableTextureSourceRef& aTextureSource)
+{
+ // This happens at composition time.
+
+ // If mGLTextureSource is null it means PrepareTextureSource failed.
+ if (!mGLTextureSource) {
+ return false;
+ }
+
+ // If Prepare didn't fail, we expect our TextureSource to be the same as aTextureSource,
+ // otherwise it means something has fiddled with the TextureSource between Prepare and
+ // now.
+ MOZ_ASSERT(mGLTextureSource == aTextureSource);
+ aTextureSource = mGLTextureSource.get();
+
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+ // Wait until it's ready.
+ WaitAcquireFenceHandleSyncComplete();
+#endif
+ return true;
+}
+
+FenceHandle
+GrallocTextureHostOGL::GetCompositorReleaseFence()
+{
+ if (!mCompositor) {
+ return FenceHandle();
+ }
+ return mCompositor->GetReleaseFence();
+}
+
+
+} // namepsace layers
+} // namepsace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/opengl/GrallocTextureHost.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_GRALLOCTEXTUREHOST_H
+#define MOZILLA_GFX_GRALLOCTEXTUREHOST_H
+#ifdef MOZ_WIDGET_GONK
+
+#include "mozilla/layers/CompositorOGL.h"
+#include "mozilla/layers/TextureHostOGL.h"
+#include "mozilla/layers/ShadowLayerUtilsGralloc.h"
+#include <ui/GraphicBuffer.h>
+
+namespace mozilla {
+namespace layers {
+
+class GrallocTextureHostOGL : public TextureHost
+{
+ friend class GrallocBufferActor;
+public:
+ GrallocTextureHostOGL(TextureFlags aFlags,
+ const SurfaceDescriptorGralloc& aDescriptor);
+
+ virtual ~GrallocTextureHostOGL();
+
+ virtual bool Lock() override;
+
+ virtual void Unlock() override;
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ virtual void DeallocateSharedData() override;
+
+ virtual void ForgetSharedData() override;
+
+ virtual void DeallocateDeviceData() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const;
+
+ virtual gfx::IntSize GetSize() const override { return mCropSize; }
+
+ virtual LayerRenderState GetRenderState() override;
+
+ virtual void PrepareTextureSource(CompositableTextureSourceRef& aTextureSource) override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTextureSource) override;
+
+ virtual void UnbindTextureSource() override;
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+ virtual void WaitAcquireFenceHandleSyncComplete() override;
+
+ virtual void SetCropRect(nsIntRect aCropRect) override;
+
+ bool IsValid() const;
+
+ virtual const char* Name() override { return "GrallocTextureHostOGL"; }
+
+ gl::GLContext* GetGLContext() const { return mCompositor ? mCompositor->gl() : nullptr; }
+
+ virtual bool NeedsFenceHandle() override
+ {
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+ return true;
+#else
+ return false;
+#endif
+ }
+
+ virtual FenceHandle GetCompositorReleaseFence() override;
+
+ virtual GrallocTextureHostOGL* AsGrallocTextureHostOGL() override { return this; }
+
+private:
+ void DestroyEGLImage();
+
+ SurfaceDescriptorGralloc mGrallocHandle;
+ RefPtr<GLTextureSource> mGLTextureSource;
+ RefPtr<CompositorOGL> mCompositor;
+ // Size reported by the GraphicBuffer
+ gfx::IntSize mSize;
+ // Size reported by TextureClient, can be different in some cases (video?),
+ // used by LayerRenderState.
+ gfx::IntSize mCropSize;
+ gfx::SurfaceFormat mFormat;
+ EGLImage mEGLImage;
+ bool mIsOpaque;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
+#endif
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/ChromeCast.java
@@ -0,0 +1,509 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko;
+
+import java.io.IOException;
+
+import org.mozilla.gecko.util.EventCallback;
+import org.json.JSONObject;
+import org.json.JSONException;
+
+import com.google.android.gms.cast.Cast.MessageReceivedCallback;
+import com.google.android.gms.cast.ApplicationMetadata;
+import com.google.android.gms.cast.Cast;
+import com.google.android.gms.cast.Cast.ApplicationConnectionResult;
+import com.google.android.gms.cast.CastDevice;
+import com.google.android.gms.cast.CastMediaControlIntent;
+import com.google.android.gms.cast.MediaInfo;
+import com.google.android.gms.cast.MediaMetadata;
+import com.google.android.gms.cast.MediaStatus;
+import com.google.android.gms.cast.RemoteMediaPlayer;
+import com.google.android.gms.cast.RemoteMediaPlayer.MediaChannelResult;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.common.api.Status;
+import com.google.android.gms.common.GooglePlayServicesUtil;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v7.media.MediaRouter.RouteInfo;
+import android.util.Log;
+
+/* Implementation of GeckoMediaPlayer for talking to ChromeCast devices */
+class ChromeCast implements GeckoMediaPlayer {
+ private static final boolean SHOW_DEBUG = false;
+
+ static final String MIRROR_RECEIVER_APP_ID = "08FF1091";
+
+ private final Context context;
+ private final RouteInfo route;
+ private GoogleApiClient apiClient;
+ private RemoteMediaPlayer remoteMediaPlayer;
+ private final boolean canMirror;
+ private String mSessionId;
+ private MirrorChannel mMirrorChannel;
+ private boolean mApplicationStarted = false;
+
+ // EventCallback which is actually a GeckoEventCallback is sometimes being invoked more
+ // than once. That causes the IllegalStateException to be thrown. To prevent a crash,
+ // catch the exception and report it as an error to the log.
+ private static void sendSuccess(final EventCallback callback, final String msg) {
+ try {
+ callback.sendSuccess(msg);
+ } catch (final IllegalStateException e) {
+ Log.e(LOGTAG, "Attempting to invoke callback.sendSuccess more than once.", e);
+ }
+ }
+
+ private static void sendError(final EventCallback callback, final String msg) {
+ try {
+ callback.sendError(msg);
+ } catch (final IllegalStateException e) {
+ Log.e(LOGTAG, "Attempting to invoke callback.sendError more than once.", e);
+ }
+ }
+
+ // Callback to start playback of a url on a remote device
+ private class VideoPlayCallback implements ResultCallback<ApplicationConnectionResult>,
+ RemoteMediaPlayer.OnStatusUpdatedListener,
+ RemoteMediaPlayer.OnMetadataUpdatedListener {
+ private final String url;
+ private final String type;
+ private final String title;
+ private final EventCallback callback;
+
+ public VideoPlayCallback(String url, String type, String title, EventCallback callback) {
+ this.url = url;
+ this.type = type;
+ this.title = title;
+ this.callback = callback;
+ }
+
+ @Override
+ public void onStatusUpdated() {
+ MediaStatus mediaStatus = remoteMediaPlayer.getMediaStatus();
+
+ switch (mediaStatus.getPlayerState()) {
+ case MediaStatus.PLAYER_STATE_PLAYING:
+ GeckoAppShell.notifyObservers("MediaPlayer:Playing", null);
+ break;
+ case MediaStatus.PLAYER_STATE_PAUSED:
+ GeckoAppShell.notifyObservers("MediaPlayer:Paused", null);
+ break;
+ case MediaStatus.PLAYER_STATE_IDLE:
+ // TODO: Do we want to shutdown when there are errors?
+ if (mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_FINISHED) {
+ GeckoAppShell.notifyObservers("Casting:Stop", null);
+ }
+ break;
+ default:
+ // TODO: Do we need to handle other status such as buffering / unknown?
+ break;
+ }
+ }
+
+ @Override
+ public void onMetadataUpdated() { }
+
+ @Override
+ public void onResult(ApplicationConnectionResult result) {
+ Status status = result.getStatus();
+ debug("ApplicationConnectionResultCallback.onResult: statusCode" + status.getStatusCode());
+ if (status.isSuccess()) {
+ remoteMediaPlayer = new RemoteMediaPlayer();
+ remoteMediaPlayer.setOnStatusUpdatedListener(this);
+ remoteMediaPlayer.setOnMetadataUpdatedListener(this);
+ mSessionId = result.getSessionId();
+ if (!verifySession(callback)) {
+ return;
+ }
+
+ try {
+ Cast.CastApi.setMessageReceivedCallbacks(apiClient, remoteMediaPlayer.getNamespace(), remoteMediaPlayer);
+ } catch (IOException e) {
+ debug("Exception while creating media channel", e);
+ }
+
+ startPlayback();
+ } else {
+ sendError(callback, status.toString());
+ }
+ }
+
+ private void startPlayback() {
+ MediaMetadata mediaMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
+ mediaMetadata.putString(MediaMetadata.KEY_TITLE, title);
+ MediaInfo mediaInfo = new MediaInfo.Builder(url)
+ .setContentType(type)
+ .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
+ .setMetadata(mediaMetadata)
+ .build();
+ try {
+ remoteMediaPlayer.load(apiClient, mediaInfo, true).setResultCallback(new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
+ @Override
+ public void onResult(MediaChannelResult result) {
+ if (result.getStatus().isSuccess()) {
+ sendSuccess(callback, null);
+ debug("Media loaded successfully");
+ return;
+ }
+
+ debug("Media load failed " + result.getStatus());
+ sendError(callback, result.getStatus().toString());
+ }
+ });
+
+ return;
+ } catch (IllegalStateException e) {
+ debug("Problem occurred with media during loading", e);
+ } catch (Exception e) {
+ debug("Problem opening media during loading", e);
+ }
+
+ sendError(callback, "");
+ }
+ }
+
+ public ChromeCast(Context context, RouteInfo route) {
+ int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(context);
+ if (status != ConnectionResult.SUCCESS) {
+ throw new IllegalStateException("Play services are required for Chromecast support (got status code " + status + ")");
+ }
+
+ this.context = context;
+ this.route = route;
+ this.canMirror = route.supportsControlCategory(CastMediaControlIntent.categoryForCast(MIRROR_RECEIVER_APP_ID));
+ }
+
+ /**
+ * This dumps everything we can find about the device into JSON. This will hopefully make it
+ * easier to filter out duplicate devices from different sources in JS.
+ * Returns null if the device can't be found.
+ */
+ @Override
+ public JSONObject toJSON() {
+ final JSONObject obj = new JSONObject();
+ try {
+ final CastDevice device = CastDevice.getFromBundle(route.getExtras());
+ if (device == null) {
+ return null;
+ }
+
+ obj.put("uuid", route.getId());
+ obj.put("version", device.getDeviceVersion());
+ obj.put("friendlyName", device.getFriendlyName());
+ obj.put("location", device.getIpAddress().toString());
+ obj.put("modelName", device.getModelName());
+ obj.put("mirror", canMirror);
+ // For now we just assume all of these are Google devices
+ obj.put("manufacturer", "Google Inc.");
+ } catch (JSONException ex) {
+ debug("Error building route", ex);
+ }
+
+ return obj;
+ }
+
+ @Override
+ public void load(final String title, final String url, final String type, final EventCallback callback) {
+ final CastDevice device = CastDevice.getFromBundle(route.getExtras());
+ Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions.builder(device, new Cast.Listener() {
+ @Override
+ public void onApplicationStatusChanged() { }
+
+ @Override
+ public void onVolumeChanged() { }
+
+ @Override
+ public void onApplicationDisconnected(int errorCode) { }
+ });
+
+ apiClient = new GoogleApiClient.Builder(context)
+ .addApi(Cast.API, apiOptionsBuilder.build())
+ .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
+ @Override
+ public void onConnected(Bundle connectionHint) {
+ // Sometimes apiClient is null here. See bug 1061032
+ if (apiClient != null && !apiClient.isConnected()) {
+ debug("Connection failed");
+ sendError(callback, "Not connected");
+ return;
+ }
+
+ // Launch the media player app and launch this url once its loaded
+ try {
+ Cast.CastApi.launchApplication(apiClient, CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID, true)
+ .setResultCallback(new VideoPlayCallback(url, type, title, callback));
+ } catch (Exception e) {
+ debug("Failed to launch application", e);
+ }
+ }
+
+ @Override
+ public void onConnectionSuspended(int cause) {
+ debug("suspended");
+ }
+ }).build();
+
+ apiClient.connect();
+ }
+
+ @Override
+ public void start(final EventCallback callback) {
+ // Nothing to be done here
+ sendSuccess(callback, null);
+ }
+
+ @Override
+ public void stop(final EventCallback callback) {
+ // Nothing to be done here
+ sendSuccess(callback, null);
+ }
+
+ public boolean verifySession(final EventCallback callback) {
+ String msg = null;
+ if (apiClient == null || !apiClient.isConnected()) {
+ msg = "Not connected";
+ }
+
+ if (mSessionId == null) {
+ msg = "No session";
+ }
+
+ if (msg != null) {
+ debug(msg);
+ if (callback != null) {
+ sendError(callback, msg);
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void play(final EventCallback callback) {
+ if (!verifySession(callback)) {
+ return;
+ }
+
+ try {
+ remoteMediaPlayer.play(apiClient).setResultCallback(new ResultCallback<MediaChannelResult>() {
+ @Override
+ public void onResult(MediaChannelResult result) {
+ Status status = result.getStatus();
+ if (!status.isSuccess()) {
+ debug("Unable to play: " + status.getStatusCode());
+ sendError(callback, status.toString());
+ } else {
+ sendSuccess(callback, null);
+ }
+ }
+ });
+ } catch (IllegalStateException ex) {
+ // The media player may throw if the session has been killed. For now, we're just catching this here.
+ sendError(callback, "Error playing");
+ }
+ }
+
+ @Override
+ public void pause(final EventCallback callback) {
+ if (!verifySession(callback)) {
+ return;
+ }
+
+ try {
+ remoteMediaPlayer.pause(apiClient).setResultCallback(new ResultCallback<MediaChannelResult>() {
+ @Override
+ public void onResult(MediaChannelResult result) {
+ Status status = result.getStatus();
+ if (!status.isSuccess()) {
+ debug("Unable to pause: " + status.getStatusCode());
+ sendError(callback, status.toString());
+ } else {
+ sendSuccess(callback, null);
+ }
+ }
+ });
+ } catch (IllegalStateException ex) {
+ // The media player may throw if the session has been killed. For now, we're just catching this here.
+ sendError(callback, "Error pausing");
+ }
+ }
+
+ @Override
+ public void end(final EventCallback callback) {
+ if (!verifySession(callback)) {
+ return;
+ }
+
+ try {
+ Cast.CastApi.stopApplication(apiClient).setResultCallback(new ResultCallback<Status>() {
+ @Override
+ public void onResult(Status result) {
+ if (result.isSuccess()) {
+ try {
+ Cast.CastApi.removeMessageReceivedCallbacks(apiClient, remoteMediaPlayer.getNamespace());
+ remoteMediaPlayer = null;
+ mSessionId = null;
+ apiClient.disconnect();
+ apiClient = null;
+
+ if (callback != null) {
+ sendSuccess(callback, null);
+ }
+
+ return;
+ } catch (Exception ex) {
+ debug("Error ending", ex);
+ }
+ }
+
+ if (callback != null) {
+ sendError(callback, result.getStatus().toString());
+ }
+ }
+ });
+ } catch (IllegalStateException ex) {
+ // The media player may throw if the session has been killed. For now, we're just catching this here.
+ sendError(callback, "Error stopping");
+ }
+ }
+
+ class MirrorChannel implements MessageReceivedCallback {
+ /**
+ * @return custom namespace
+ */
+ public String getNamespace() {
+ return "urn:x-cast:org.mozilla.mirror";
+ }
+
+ /*
+ * Receive message from the receiver app
+ */
+ @Override
+ public void onMessageReceived(CastDevice castDevice, String namespace,
+ String message) {
+ GeckoAppShell.notifyObservers("MediaPlayer:Response", message);
+ }
+
+ public void sendMessage(String message) {
+ if (apiClient != null && mMirrorChannel != null) {
+ try {
+ Cast.CastApi.sendMessage(apiClient, mMirrorChannel.getNamespace(), message)
+ .setResultCallback(
+ new ResultCallback<Status>() {
+ @Override
+ public void onResult(Status result) {
+ }
+ });
+ } catch (Exception e) {
+ Log.e(LOGTAG, "Exception while sending message", e);
+ }
+ }
+ }
+ }
+ private class MirrorCallback implements ResultCallback<ApplicationConnectionResult> {
+ final EventCallback callback;
+ MirrorCallback(final EventCallback callback) {
+ this.callback = callback;
+ }
+
+
+ @Override
+ public void onResult(ApplicationConnectionResult result) {
+ Status status = result.getStatus();
+ if (status.isSuccess()) {
+ ApplicationMetadata applicationMetadata = result.getApplicationMetadata();
+ mSessionId = result.getSessionId();
+ String applicationStatus = result.getApplicationStatus();
+ boolean wasLaunched = result.getWasLaunched();
+ mApplicationStarted = true;
+
+ // Create the custom message
+ // channel
+ mMirrorChannel = new MirrorChannel();
+ try {
+ Cast.CastApi.setMessageReceivedCallbacks(apiClient,
+ mMirrorChannel
+ .getNamespace(),
+ mMirrorChannel);
+ sendSuccess(callback, null);
+ } catch (IOException e) {
+ Log.e(LOGTAG, "Exception while creating channel", e);
+ }
+
+ GeckoAppShell.notifyObservers("Casting:Mirror", route.getId());
+ } else {
+ sendError(callback, status.toString());
+ }
+ }
+ }
+
+ @Override
+ public void message(String msg, final EventCallback callback) {
+ if (mMirrorChannel != null) {
+ mMirrorChannel.sendMessage(msg);
+ }
+ }
+
+ @Override
+ public void mirror(final EventCallback callback) {
+ final CastDevice device = CastDevice.getFromBundle(route.getExtras());
+ Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions.builder(device, new Cast.Listener() {
+ @Override
+ public void onApplicationStatusChanged() { }
+
+ @Override
+ public void onVolumeChanged() { }
+
+ @Override
+ public void onApplicationDisconnected(int errorCode) { }
+ });
+
+ apiClient = new GoogleApiClient.Builder(context)
+ .addApi(Cast.API, apiOptionsBuilder.build())
+ .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
+ @Override
+ public void onConnected(Bundle connectionHint) {
+ // Sometimes apiClient is null here. See bug 1061032
+ if (apiClient == null || !apiClient.isConnected()) {
+ return;
+ }
+
+ // Launch the media player app and launch this url once its loaded
+ try {
+ Cast.CastApi.launchApplication(apiClient, MIRROR_RECEIVER_APP_ID, true)
+ .setResultCallback(new MirrorCallback(callback));
+ } catch (Exception e) {
+ debug("Failed to launch application", e);
+ }
+ }
+
+ @Override
+ public void onConnectionSuspended(int cause) {
+ debug("suspended");
+ }
+ }).build();
+
+ apiClient.connect();
+ }
+
+ private static final String LOGTAG = "GeckoChromeCast";
+ private void debug(String msg, Exception e) {
+ if (SHOW_DEBUG) {
+ Log.e(LOGTAG, msg, e);
+ }
+ }
+
+ private void debug(String msg) {
+ if (SHOW_DEBUG) {
+ Log.d(LOGTAG, msg);
+ }
+ }
+
+}
--- a/mobile/android/base/java/org/mozilla/gecko/home/BrowserSearch.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/BrowserSearch.java
@@ -244,16 +244,18 @@ public class BrowserSearch extends HomeF
mSearchEngines = new ArrayList<SearchEngine>();
mSearchHistorySuggestions = new ArrayList<>();
}
@Override
public void onDestroy() {
super.onDestroy();
+
+ mSearchEngines = null;
}
@Override
public void onHiddenChanged(boolean hidden) {
if (!hidden) {
final Tab tab = Tabs.getInstance().getSelectedTab();
final boolean isPrivate = (tab != null && tab.isPrivate());
@@ -680,18 +682,23 @@ public class BrowserSearch extends HomeF
mSearchHistorySuggestionLoaderCallback = new SearchHistorySuggestionLoaderCallbacks();
}
getLoaderManager().restartLoader(LOADER_ID_SAVED_SUGGESTION, null, mSearchHistorySuggestionLoaderCallback);
}
private void setSuggestions(ArrayList<String> suggestions) {
ThreadUtils.assertOnUiThread();
- mSearchEngines.get(0).setSuggestions(suggestions);
- mAdapter.notifyDataSetChanged();
+ // mSearchEngines may be null if the setSuggestions calls after onDestroy (bug 1310621).
+ // So drop the suggestions if search engines are not available
+ if (mSearchEngines != null && !mSearchEngines.isEmpty()) {
+ mSearchEngines.get(0).setSuggestions(suggestions);
+ mAdapter.notifyDataSetChanged();
+ }
+
}
private void setSavedSuggestions(ArrayList<String> savedSuggestions) {
ThreadUtils.assertOnUiThread();
mSearchHistorySuggestions = savedSuggestions;
mAdapter.notifyDataSetChanged();
}
new file mode 100644
--- /dev/null
+++ b/security/sandbox/linux/SandboxHooks.cpp
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include <dlfcn.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "mozilla/Types.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+// Signal number used to enable seccomp on each thread.
+extern int gSeccompTsyncBroadcastSignum;
+
+// This file defines a hook for sigprocmask() and pthread_sigmask().
+// Bug 1176099: some threads block SIGSYS signal which breaks our seccomp-bpf
+// sandbox. To avoid this, we intercept the call and remove SIGSYS.
+//
+// ENOSYS indicates an error within the hook function itself.
+static int HandleSigset(int (*aRealFunc)(int, const sigset_t*, sigset_t*),
+ int aHow, const sigset_t* aSet,
+ sigset_t* aOldSet, bool aUseErrno)
+{
+ if (!aRealFunc) {
+ if (aUseErrno) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ENOSYS;
+ }
+
+ // Avoid unnecessary work
+ if (aSet == NULL || aHow == SIG_UNBLOCK) {
+ return aRealFunc(aHow, aSet, aOldSet);
+ }
+
+ sigset_t newSet = *aSet;
+ if (sigdelset(&newSet, SIGSYS) != 0 ||
+ (gSeccompTsyncBroadcastSignum &&
+ sigdelset(&newSet, gSeccompTsyncBroadcastSignum) != 0)) {
+ if (aUseErrno) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ENOSYS;
+ }
+
+ return aRealFunc(aHow, &newSet, aOldSet);
+}
+
+extern "C" MOZ_EXPORT int
+sigprocmask(int how, const sigset_t* set, sigset_t* oldset)
+{
+ static auto sRealFunc = (int (*)(int, const sigset_t*, sigset_t*))
+ dlsym(RTLD_NEXT, "sigprocmask");
+
+ return HandleSigset(sRealFunc, how, set, oldset, true);
+}
+
+extern "C" MOZ_EXPORT int
+pthread_sigmask(int how, const sigset_t* set, sigset_t* oldset)
+{
+ static auto sRealFunc = (int (*)(int, const sigset_t*, sigset_t*))
+ dlsym(RTLD_NEXT, "pthread_sigmask");
+
+ return HandleSigset(sRealFunc, how, set, oldset, false);
+}