Merge m-c to m-i

This commit is contained in:
Phil Ringnalda 2012-09-29 00:03:32 -07:00
commit 7c24bbe9a8
67 changed files with 7818 additions and 489 deletions

View File

@ -16,7 +16,7 @@
<ShortName>Google</ShortName>
<Description>Google Search</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16">data:image/png;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9FsfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2FPtft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2FggM%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJvvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYSBHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWcTxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4jwA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsggA7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFEMwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCTIYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesAAN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOcAAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA</Image>
<Image width="16" height="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABUUlEQVR42pWTzUsCYRCH9y9zu3SooCCkjhIRRLeIykXokiWCJ7PvDpZRlz6si1lIQZ3SQxQdOhREpgSm0JeQvfu0+i6I7LKLh4F5h5nnnRl+o6jTdHn8omAYbVqhXqvYFXcEBKFDwcoZZB8B4LkEB9cwGGmFKHb01A1EU9JXzfdvDYZi1lwLwBcVAIwsNWPesIwls7gDtB2Z7N9ujVe+IX2LO2AgItB1OL9vJqsmILDrOoK02IkBAdYy4FsQJC5h+VQCHQDWTqYSgo8fuHuRxS4Ae3stQ7UGE5ttAHqCUgfxC7m4ryrowOyeO6CxqHwZxtYFqtYc5+kNan/gDTsAeueEIRj7n/rmRQMwueUAGF0VAAT3rQBTC0Y3DoDOGbm00icML4oWHYSTgo0MFqjlmPpDgqMcFCuQf4erBzjOwXjcriu9qHg0uutO2+es6fl67T9ptebvFRjBVgAAAABJRU5ErkJggg==</Image>
<Url type="application/x-suggestions+json" method="GET" template="https://www.google.com/complete/search?client=firefox&amp;q={searchTerms}"/>
<Url type="text/html" method="GET" template="https://www.google.com/search">
#expand __GOOGLE_PARAMS__

View File

@ -1,3 +1,4 @@
dnl -*- Mode: Autoconf; tab-width: 4; indent-tabs-mode: nil; -*-
dnl vi: set tabstop=4 shiftwidth=4 expandtab syntax=m4:
dnl This Source Code Form is subject to the terms of the Mozilla Public
@ -197,7 +198,7 @@ if test -n "$gonkdir" ; then
;;
esac
CPPFLAGS="-DANDROID -isystem $gonkdir/bionic/libc/$ARCH_DIR/include -isystem $gonkdir/bionic/libc/include/ -isystem $gonkdir/bionic/libc/kernel/common -isystem $gonkdir/bionic/libc/kernel/$ARCH_DIR -isystem $gonkdir/bionic/libm/include -I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/hardware/libhardware/include -I$gonkdir/hardware/libhardware_legacy/include -I$gonkdir/system -I$gonkdir/system/core/include -isystem $gonkdir/bionic -I$gonkdir/frameworks/base/include -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib $CPPFLAGS -I$gonkdir/frameworks/base/services/sensorservice -I$gonkdir/frameworks/base/services/camera -I$gonkdir/system/media/wilhelm/include"
CPPFLAGS="-DANDROID -isystem $gonkdir/bionic/libc/$ARCH_DIR/include -isystem $gonkdir/bionic/libc/include/ -isystem $gonkdir/bionic/libc/kernel/common -isystem $gonkdir/bionic/libc/kernel/$ARCH_DIR -isystem $gonkdir/bionic/libm/include -I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/hardware/libhardware/include -I$gonkdir/hardware/libhardware_legacy/include -I$gonkdir/system -I$gonkdir/system/core/include -isystem $gonkdir/bionic -I$gonkdir/frameworks/base/include -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib $CPPFLAGS -I$gonkdir/frameworks/base/services/sensorservice -I$gonkdir/frameworks/base/services/camera -I$gonkdir/system/media/wilhelm/include -I$gonkdir/frameworks/base/include/media/stagefright -I$gonkdir/frameworks/base/include/media/stagefright/openmax -I$gonkdir/frameworks/base/media/libstagefright/rtsp -I$gonkdir/frameworks/base/media/libstagefright/include -I$gonkdir/dalvik/libnativehelper/include/nativehelper"
CFLAGS="-mandroid -fno-short-enums -fno-exceptions $CFLAGS"
CXXFLAGS="-mandroid -fno-short-enums -fno-exceptions -Wno-psabi $CXXFLAGS $STLPORT_CPPFLAGS"
dnl Add -llog by default, since we use it all over the place.

View File

@ -2,7 +2,7 @@
<body onpopstate='opener.page1Popstate();' onload='opener.page1Load();'
onpageshow='opener.page1PageShow();'>
<div style='height:300%' id='div1'>This is a very tall div.</div>
<div style='height:10000px' id='div1'>This is a very tall div.</div>
</body>
</html>

View File

@ -206,42 +206,20 @@ const ContentPanning = {
let metrics = data.json;
let displayPort = metrics.displayPort;
let screenWidth = metrics.screenSize.width;
let screenHeight = metrics.screenSize.height;
let compositionWidth = metrics.compositionBounds.width;
let compositionHeight = metrics.compositionBounds.height;
let x = metrics.x;
let y = metrics.y;
this._zoom = metrics.zoom;
this._viewport = new Rect(x, y,
screenWidth / metrics.zoom,
screenHeight / metrics.zoom);
compositionWidth / metrics.zoom,
compositionHeight / metrics.zoom);
this._cssPageRect = new Rect(metrics.cssPageRect.x,
metrics.cssPageRect.y,
metrics.cssPageRect.width,
metrics.cssPageRect.height);
let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
if (this._screenWidth != screenWidth || this._screenHeight != screenHeight) {
cwu.setCSSViewport(screenWidth, screenHeight);
this._screenWidth = screenWidth;
this._screenHeight = screenHeight;
}
// Set scroll position
cwu.setScrollPositionClampingScrollPortSize(
screenWidth / metrics.zoom, screenHeight / metrics.zoom);
content.scrollTo(x, y);
cwu.setResolution(displayPort.resolution, displayPort.resolution);
let element = null;
if (content.document && (element = content.document.documentElement)) {
cwu.setDisplayPortForElement(displayPort.left,
displayPort.top,
displayPort.width,
displayPort.height,
element);
}
},
_recvDoubleTap: function(data) {
@ -272,7 +250,7 @@ const ContentPanning = {
let cssPageRect = this._cssPageRect;
let viewport = this._viewport;
let bRect = new Rect(Math.max(cssPageRect.left, rect.x - margin),
let bRect = new Rect(Math.max(cssPageRect.x, rect.x - margin),
rect.y,
rect.w + 2 * margin,
rect.h);

View File

@ -0,0 +1,179 @@
/*
* Copyright (C) 2006-2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "AudioParameter"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
#include <media/AudioParameter.h>
namespace android {
const char *AudioParameter::keyRouting = "routing";
const char *AudioParameter::keySamplingRate = "sampling_rate";
const char *AudioParameter::keyFormat = "format";
const char *AudioParameter::keyChannels = "channels";
const char *AudioParameter::keyFrameCount = "frame_count";
const char *AudioParameter::keyInputSource = "input_source";
AudioParameter::AudioParameter(const String8& keyValuePairs)
{
char *str = new char[keyValuePairs.length()+1];
mKeyValuePairs = keyValuePairs;
strcpy(str, keyValuePairs.string());
char *pair = strtok(str, ";");
while (pair != NULL) {
if (strlen(pair) != 0) {
size_t eqIdx = strcspn(pair, "=");
String8 key = String8(pair, eqIdx);
String8 value;
if (eqIdx == strlen(pair)) {
value = String8("");
} else {
value = String8(pair + eqIdx + 1);
}
if (mParameters.indexOfKey(key) < 0) {
mParameters.add(key, value);
} else {
mParameters.replaceValueFor(key, value);
}
} else {
LOGV("AudioParameter() cstor empty key value pair");
}
pair = strtok(NULL, ";");
}
delete[] str;
}
AudioParameter::~AudioParameter()
{
mParameters.clear();
}
String8 AudioParameter::toString()
{
String8 str = String8("");
size_t size = mParameters.size();
for (size_t i = 0; i < size; i++) {
str += mParameters.keyAt(i);
str += "=";
str += mParameters.valueAt(i);
if (i < (size - 1)) str += ";";
}
return str;
}
status_t AudioParameter::add(const String8& key, const String8& value)
{
if (mParameters.indexOfKey(key) < 0) {
mParameters.add(key, value);
return NO_ERROR;
} else {
mParameters.replaceValueFor(key, value);
return ALREADY_EXISTS;
}
}
status_t AudioParameter::addInt(const String8& key, const int value)
{
char str[12];
if (snprintf(str, 12, "%d", value) > 0) {
String8 str8 = String8(str);
return add(key, str8);
} else {
return BAD_VALUE;
}
}
status_t AudioParameter::addFloat(const String8& key, const float value)
{
char str[23];
if (snprintf(str, 23, "%.10f", value) > 0) {
String8 str8 = String8(str);
return add(key, str8);
} else {
return BAD_VALUE;
}
}
status_t AudioParameter::remove(const String8& key)
{
if (mParameters.indexOfKey(key) >= 0) {
mParameters.removeItem(key);
return NO_ERROR;
} else {
return BAD_VALUE;
}
}
status_t AudioParameter::get(const String8& key, String8& value)
{
if (mParameters.indexOfKey(key) >= 0) {
value = mParameters.valueFor(key);
return NO_ERROR;
} else {
return BAD_VALUE;
}
}
status_t AudioParameter::getInt(const String8& key, int& value)
{
String8 str8;
status_t result = get(key, str8);
value = 0;
if (result == NO_ERROR) {
int val;
if (sscanf(str8.string(), "%d", &val) == 1) {
value = val;
} else {
result = INVALID_OPERATION;
}
}
return result;
}
status_t AudioParameter::getFloat(const String8& key, float& value)
{
String8 str8;
status_t result = get(key, str8);
value = 0;
if (result == NO_ERROR) {
float val;
if (sscanf(str8.string(), "%f", &val) == 1) {
value = val;
} else {
result = INVALID_OPERATION;
}
}
return result;
}
status_t AudioParameter::getAt(size_t index, String8& key, String8& value)
{
if (mParameters.size() > index) {
key = mParameters.keyAt(index);
value = mParameters.valueAt(index);
return NO_ERROR;
} else {
return BAD_VALUE;
}
}
}; // namespace android

View File

@ -196,9 +196,9 @@ CameraControlImpl::TakePicture(CameraSize aSize, int32_t aRotation, const nsAStr
}
nsresult
CameraControlImpl::StartRecording(CameraSize aSize, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
CameraControlImpl::StartRecording(nsIDOMDeviceStorage* aStorageArea, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
{
nsCOMPtr<nsIRunnable> startRecordingTask = new StartRecordingTask(this, aSize, onSuccess, onError);
nsCOMPtr<nsIRunnable> startRecordingTask = new StartRecordingTask(this, aStorageArea, aFilename, onSuccess, onError);
return mCameraThread->Dispatch(startRecordingTask, NS_DISPATCH_NORMAL);
}
@ -223,6 +223,13 @@ CameraControlImpl::StopPreview()
mCameraThread->Dispatch(stopPreviewTask, NS_DISPATCH_NORMAL);
}
nsresult
CameraControlImpl::GetPreviewStreamVideoMode(CameraRecordingOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
{
nsCOMPtr<nsIRunnable> getPreviewStreamVideoModeTask = new GetPreviewStreamVideoModeTask(this, *aOptions, onSuccess, onError);
return mCameraThread->Dispatch(getPreviewStreamVideoModeTask, NS_DISPATCH_NORMAL);
}
bool
CameraControlImpl::ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder)
{

View File

@ -8,6 +8,7 @@
#include "nsCOMPtr.h"
#include "nsDOMFile.h"
#include "DictionaryHelpers.h"
#include "nsIDOMDeviceStorage.h"
#include "nsIDOMCameraManager.h"
#include "ICameraControl.h"
#include "CameraCommon.h"
@ -25,6 +26,7 @@ class StartRecordingTask;
class StopRecordingTask;
class SetParameterTask;
class GetParameterTask;
class GetPreviewStreamVideoModeTask;
class DOMCameraPreview;
@ -39,6 +41,7 @@ class CameraControlImpl : public ICameraControl
friend class StopRecordingTask;
friend class SetParameterTask;
friend class GetParameterTask;
friend class GetPreviewStreamVideoModeTask;
public:
CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread)
@ -64,8 +67,9 @@ public:
void StopPreview();
nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError);
nsresult TakePicture(CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError);
nsresult StartRecording(CameraSize aSize, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError);
nsresult StartRecording(nsIDOMDeviceStorage* aStorageArea, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError);
nsresult StopRecording();
nsresult GetPreviewStreamVideoMode(CameraRecordingOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
nsresult Set(uint32_t aKey, const nsAString& aValue);
nsresult Get(uint32_t aKey, nsAString& aValue);
@ -111,6 +115,7 @@ protected:
virtual nsresult StopRecordingImpl(StopRecordingTask* aStopRecording) = 0;
virtual nsresult PushParametersImpl() = 0;
virtual nsresult PullParametersImpl() = 0;
virtual nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode) = 0;
uint32_t mCameraId;
nsCOMPtr<nsIThread> mCameraThread;
@ -347,9 +352,8 @@ public:
class StartRecordingResult : public nsRunnable
{
public:
StartRecordingResult(nsIDOMMediaStream* aStream, nsICameraStartRecordingCallback* onSuccess)
: mStream(aStream)
, mOnSuccessCb(onSuccess)
StartRecordingResult(nsICameraStartRecordingCallback* onSuccess)
: mOnSuccessCb(onSuccess)
{ }
virtual ~StartRecordingResult() { }
@ -359,13 +363,12 @@ public:
MOZ_ASSERT(NS_IsMainThread());
if (mOnSuccessCb) {
mOnSuccessCb->HandleEvent(mStream);
mOnSuccessCb->HandleEvent();
}
return NS_OK;
}
protected:
nsCOMPtr<nsIDOMMediaStream> mStream;
nsCOMPtr<nsICameraStartRecordingCallback> mOnSuccessCb;
};
@ -373,9 +376,10 @@ protected:
class StartRecordingTask : public nsRunnable
{
public:
StartRecordingTask(CameraControlImpl* aCameraControl, CameraSize aSize, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
: mSize(aSize)
, mCameraControl(aCameraControl)
StartRecordingTask(CameraControlImpl* aCameraControl, nsIDOMDeviceStorage* aStorageArea, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
: mCameraControl(aCameraControl)
, mStorageArea(aStorageArea)
, mFilename(aFilename)
, mOnSuccessCb(onSuccess)
, mOnErrorCb(onError)
{
@ -391,17 +395,21 @@ public:
{
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
nsresult rv = mCameraControl->StartRecordingImpl(this);
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
DOM_CAMERA_LOGT("%s:%d : result %d\n", __func__, __LINE__, rv);
if (NS_FAILED(rv) && mOnErrorCb) {
if (NS_SUCCEEDED(rv)) {
if (mOnSuccessCb) {
rv = NS_DispatchToMainThread(new StartRecordingResult(mOnSuccessCb));
}
} else if (mOnErrorCb) {
rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE")));
NS_ENSURE_SUCCESS(rv, rv);
}
return rv;
}
CameraSize mSize;
nsRefPtr<CameraControlImpl> mCameraControl;
nsCOMPtr<nsIDOMDeviceStorage> mStorageArea;
nsString mFilename;
nsCOMPtr<nsICameraStartRecordingCallback> mOnSuccessCb;
nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
};
@ -491,6 +499,67 @@ public:
nsRefPtr<CameraControlImpl> mCameraControl;
};
// Return the resulting preview stream to JS. Runs on the main thread.
class GetPreviewStreamVideoModeResult : public nsRunnable
{
public:
GetPreviewStreamVideoModeResult(nsIDOMMediaStream* aStream, nsICameraPreviewStreamCallback* onSuccess)
: mStream(aStream)
, mOnSuccessCb(onSuccess)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
virtual ~GetPreviewStreamVideoModeResult()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
if (mOnSuccessCb) {
mOnSuccessCb->HandleEvent(mStream);
}
return NS_OK;
}
protected:
nsCOMPtr<nsIDOMMediaStream> mStream;
nsCOMPtr<nsICameraPreviewStreamCallback> mOnSuccessCb;
};
// Get the video mode preview stream.
class GetPreviewStreamVideoModeTask : public nsRunnable
{
public:
GetPreviewStreamVideoModeTask(CameraControlImpl* aCameraControl, CameraRecordingOptions aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
: mCameraControl(aCameraControl)
, mOptions(aOptions)
, mOnSuccessCb(onSuccess)
, mOnErrorCb(onError)
{ }
NS_IMETHOD Run()
{
DOM_CAMERA_LOGI("%s:%d -- BEFORE IMPL\n", __func__, __LINE__);
nsresult rv = mCameraControl->GetPreviewStreamVideoModeImpl(this);
DOM_CAMERA_LOGI("%s:%d -- AFTER IMPL : rv = %d\n", __func__, __LINE__, rv);
if (NS_FAILED(rv) && mOnErrorCb) {
rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE")));
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
nsRefPtr<CameraControlImpl> mCameraControl;
CameraRecordingOptions mOptions;
nsCOMPtr<nsICameraPreviewStreamCallback> mOnSuccessCb;
nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
};
} // namespace mozilla
#endif // DOM_CAMERA_CAMERACONTROLIMPL_H

View File

@ -9,6 +9,7 @@
#include "nsThread.h"
#include "mozilla/Services.h"
#include "nsIObserverService.h"
#include "nsIDOMDeviceStorage.h"
#include "DOMCameraManager.h"
#include "DOMCameraCapabilities.h"
#include "DOMCameraControl.h"
@ -218,16 +219,12 @@ nsDOMCameraControl::SetOnShutter(nsICameraShutterCallback* aOnShutter)
return NS_ERROR_NOT_IMPLEMENTED;
}
/* void startRecording (in jsval aOptions, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
/* [implicit_jscontext] void startRecording (in nsIDOMDeviceStorage storageArea, in DOMString filename, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
NS_IMETHODIMP
nsDOMCameraControl::StartRecording(const JS::Value& aOptions, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
nsDOMCameraControl::StartRecording(nsIDOMDeviceStorage* storageArea, const nsAString& filename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
{
NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
CameraSize size;
nsresult rv = size.Init(cx, &aOptions);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) {
NS_WARNING("Could not get the Observer service for CameraControl::StartRecording.");
@ -238,7 +235,7 @@ nsDOMCameraControl::StartRecording(const JS::Value& aOptions, nsICameraStartReco
"recording-device-events",
NS_LITERAL_STRING("starting").get());
return mCameraControl->StartRecording(size, onSuccess, onError);
return mCameraControl->StartRecording(storageArea, filename, onSuccess, onError);
}
/* void stopRecording (); */
@ -316,6 +313,19 @@ nsDOMCameraControl::TakePicture(const JS::Value& aOptions, nsICameraTakePictureC
return mCameraControl->TakePicture(size, options.rotation, options.fileFormat, pos, onSuccess, onError);
}
/* [implicit_jscontext] void GetPreviewStreamVideoMode (in jsval aOptions, in nsICameraPreviewStreamCallback onSuccess, [optional] in nsICameraErrorCallback onError); */
NS_IMETHODIMP
nsDOMCameraControl::GetPreviewStreamVideoMode(const JS::Value& aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError, JSContext* cx)
{
NS_ENSURE_TRUE(onSuccess, NS_ERROR_INVALID_ARG);
CameraRecordingOptions options;
nsresult rv = options.Init(cx, &aOptions);
NS_ENSURE_SUCCESS(rv, rv);
return mCameraControl->GetPreviewStreamVideoMode(&options, onSuccess, onError);
}
class GetCameraResult : public nsRunnable
{
public:

View File

@ -15,6 +15,10 @@
*/
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <libgen.h>
#include "base/basictypes.h"
#include "libcameraservice/CameraHardwareInterface.h"
#include "camera/CameraParameters.h"
@ -23,6 +27,8 @@
#include "nsMemory.h"
#include "jsapi.h"
#include "nsThread.h"
#include <media/MediaProfiles.h>
#include "nsDirectoryServiceDefs.h" // for NS_GetSpecialDirectory
#include "nsPrintfCString.h"
#include "DOMCameraManager.h"
#include "GonkCameraHwMgr.h"
@ -547,19 +553,27 @@ nsGonkCameraControl::StartPreviewImpl(StartPreviewTask* aStartPreview)
}
nsresult
nsGonkCameraControl::StopPreviewImpl(StopPreviewTask* aStopPreview)
nsGonkCameraControl::StopPreviewInternal(bool aForced)
{
DOM_CAMERA_LOGI("%s: stopping preview\n", __func__);
// StopPreview() is a synchronous call--it doesn't return
// until the camera preview thread exits.
if (mDOMPreview) {
GonkCameraHardware::StopPreview(mHwHandle);
mDOMPreview->Stopped();
mDOMPreview->Stopped(aForced);
mDOMPreview = nullptr;
}
return NS_OK;
}
nsresult
nsGonkCameraControl::StopPreviewImpl(StopPreviewTask* aStopPreview)
{
return StopPreviewInternal();
}
nsresult
nsGonkCameraControl::AutoFocusImpl(AutoFocusTask* aAutoFocus)
{
@ -691,13 +705,71 @@ nsGonkCameraControl::PullParametersImpl()
nsresult
nsGonkCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording)
{
return NS_ERROR_NOT_IMPLEMENTED;
mStartRecordingOnSuccessCb = aStartRecording->mOnSuccessCb;
mStartRecordingOnErrorCb = aStartRecording->mOnErrorCb;
/**
* We need to pull in the base path from aStartRecording->mStorageArea
* once that feature lands. See bug 795201.
*
* For now, we just assume /sdcard/Movies.
*
* Also, the camera app needs to provide the file extension '.3gp' for now.
* See bug 795202.
*/
#if 1
nsCOMPtr<nsIFile> filename;
aStartRecording->mStorageArea->GetRootDirectory(getter_AddRefs(filename));
filename->Append(aStartRecording->mFilename);
nsAutoCString pathname;
filename->GetNativePath(pathname);
#else
nsAutoCString pathname(NS_LITERAL_CSTRING("/sdcard/Movies/"));
nsAutoCString filename(NS_ConvertUTF16toUTF8(aStartRecording->mFilename));
// Make sure that the file name doesn't contain any directory components.
if (strcmp(filename.get(), basename(filename.get())) != 0) {
DOM_CAMERA_LOGE("Video filename '%s' is not valid\n", filename.get());
return NS_ERROR_INVALID_ARG;
}
pathname.Append(filename);
#endif
DOM_CAMERA_LOGI("Video pathname is '%s'\n", pathname.get());
int fd = open(pathname.get(), O_RDWR | O_CREAT, 0644);
if (fd < 0) {
DOM_CAMERA_LOGE("Couldn't create file '%s' with error (%d) %s\n", pathname.get(), errno, strerror(errno));
return NS_ERROR_FAILURE;
}
if (SetupRecording(fd) != NS_OK) {
DOM_CAMERA_LOGE("SetupRecording() failed\n");
close(fd);
return NS_ERROR_FAILURE;
}
if (mRecorder->start() != OK) {
DOM_CAMERA_LOGE("mRecorder->start() failed\n");
close(fd);
return NS_ERROR_FAILURE;
}
// dispatch the callback
nsCOMPtr<nsIRunnable> startRecordingResult = new StartRecordingResult(mStartRecordingOnSuccessCb);
nsresult rv = NS_DispatchToMainThread(startRecordingResult);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to dispatch start recording result to main thread (%d)!", rv);
}
return NS_OK;
}
nsresult
nsGonkCameraControl::StopRecordingImpl(StopRecordingTask* aStopRecording)
{
return NS_ERROR_NOT_IMPLEMENTED;
mRecorder->stop();
delete mRecorder;
mRecorder = nullptr;
return NS_OK;
}
void
@ -809,6 +881,152 @@ nsGonkCameraControl::SetPreviewSize(uint32_t aWidth, uint32_t aHeight)
PushParameters();
}
nsresult
nsGonkCameraControl::SetupVideoMode()
{
// read preferences for camcorder
mMediaProfiles = MediaProfiles::getInstance();
/**
* Right now default to profile 3, which is 352x288 on Otoro. In the
* future, allow the application to select a recording quality and
* configuration.
*
* See bug 795379.
*/
int quality = 3; // cif:352x288
camcorder_quality q = static_cast<camcorder_quality>(quality);
mDuration = mMediaProfiles->getCamcorderProfileParamByName("duration", (int)mCameraId, q);
mVideoFileFormat = mMediaProfiles->getCamcorderProfileParamByName("file.format", (int)mCameraId, q);
mVideoCodec = mMediaProfiles->getCamcorderProfileParamByName("vid.codec", (int)mCameraId, q);
mVideoBitRate = mMediaProfiles->getCamcorderProfileParamByName("vid.bps", (int)mCameraId, q);
mVideoFrameRate = mMediaProfiles->getCamcorderProfileParamByName("vid.fps", (int)mCameraId, q);
mVideoFrameWidth = mMediaProfiles->getCamcorderProfileParamByName("vid.width", (int)mCameraId, q);
mVideoFrameHeight = mMediaProfiles->getCamcorderProfileParamByName("vid.height", (int)mCameraId, q);
mAudioCodec = mMediaProfiles->getCamcorderProfileParamByName("aud.codec", (int)mCameraId, q);
mAudioBitRate = mMediaProfiles->getCamcorderProfileParamByName("aud.bps", (int)mCameraId, q);
mAudioSampleRate = mMediaProfiles->getCamcorderProfileParamByName("aud.hz", (int)mCameraId, q);
mAudioChannels = mMediaProfiles->getCamcorderProfileParamByName("aud.ch", (int)mCameraId, q);
if (mVideoFrameRate == -1) {
DOM_CAMERA_LOGE("Failed to get a valid frame rate!\n");
DOM_CAMERA_LOGE("Also got width=%d, height=%d\n", mVideoFrameWidth, mVideoFrameHeight);
return NS_ERROR_FAILURE;
}
PullParametersImpl();
// Configure camera video recording parameters.
const size_t SIZE = 256;
char buffer[SIZE];
/**
* Ignore the width and height settings from app, just use the one in profile.
* Eventually, will try to choose a profile which respects the settings from app.
* See bug 795330.
*/
mParams.setPreviewSize(mVideoFrameWidth, mVideoFrameHeight);
mParams.setPreviewFrameRate(mVideoFrameRate);
snprintf(buffer, SIZE, "%dx%d", mVideoFrameWidth, mVideoFrameHeight);
/**
* "record-size" is probably deprecated in later ICS;
* might need to set "video-size" instead of "record-size".
* See bug 795332.
*/
mParams.set("record-size", buffer);
/**
* If we want to enable picture-taking _while_ recording video, this sets the
* size of the captured picture. For now, just set it to the same dimensions
* as the video we're recording; ideally, we should probably make sure it
* matches one of the supported picture sizes.
*/
mParams.setPictureSize(mVideoFrameWidth, mVideoFrameHeight);
PushParametersImpl();
return NS_OK;
}
#ifndef CHECK_SETARG
#define CHECK_SETARG(x) \
do { \
if (x) { \
DOM_CAMERA_LOGE(#x " failed\n"); \
return NS_ERROR_INVALID_ARG; \
} \
} while(0)
#endif
nsresult
nsGonkCameraControl::SetupRecording(int aFd)
{
// choosing a size big enough to hold the params
const size_t SIZE = 256;
char buffer[SIZE];
mRecorder = new GonkRecorder();
CHECK_SETARG(mRecorder->init());
// set all the params
CHECK_SETARG(mRecorder->setCameraHandle((int32_t)mHwHandle));
CHECK_SETARG(mRecorder->setAudioSource(AUDIO_SOURCE_CAMCORDER));
CHECK_SETARG(mRecorder->setVideoSource(VIDEO_SOURCE_CAMERA));
CHECK_SETARG(mRecorder->setOutputFormat((output_format)mVideoFileFormat));
CHECK_SETARG(mRecorder->setVideoFrameRate(mVideoFrameRate));
CHECK_SETARG(mRecorder->setVideoSize(mVideoFrameWidth, mVideoFrameHeight));
snprintf(buffer, SIZE, "video-param-encoding-bitrate=%d", mVideoBitRate);
CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
CHECK_SETARG(mRecorder->setVideoEncoder((video_encoder)mVideoCodec));
snprintf(buffer, SIZE, "audio-param-encoding-bitrate=%d", mAudioBitRate);
CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
snprintf(buffer, SIZE, "audio-param-number-of-channels=%d", mAudioChannels);
CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
snprintf(buffer, SIZE, "audio-param-sampling-rate=%d", mAudioSampleRate);
CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
CHECK_SETARG(mRecorder->setAudioEncoder((audio_encoder)mAudioCodec));
// TODO: For now there is no limit on recording duration (See bug 795090)
CHECK_SETARG(mRecorder->setParameters(String8("max-duration=-1")));
// TODO: For now there is no limit on file size (See bug 795090)
CHECK_SETARG(mRecorder->setParameters(String8("max-filesize=-1")));
snprintf(buffer, SIZE, "video-param-rotation-angle-degrees=%d", mVideoRotation);
CHECK_SETARG(mRecorder->setParameters(String8(buffer)));
// recording API needs file descriptor of output file
CHECK_SETARG(mRecorder->setOutputFile(aFd, 0, 0));
CHECK_SETARG(mRecorder->prepare());
return NS_OK;
}
nsresult
nsGonkCameraControl::GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode)
{
nsCOMPtr<GetPreviewStreamResult> getPreviewStreamResult = nullptr;
// stop any currently running preview
StopPreviewInternal(true /* forced */);
// copy the recording preview options
mVideoRotation = aGetPreviewStreamVideoMode->mOptions.rotation;
mVideoWidth = aGetPreviewStreamVideoMode->mOptions.width;
mVideoHeight = aGetPreviewStreamVideoMode->mOptions.height;
DOM_CAMERA_LOGI("recording preview format: %d x %d (w x h) (rotated %d degrees)\n", mVideoWidth, mVideoHeight, mVideoRotation);
// setup the video mode
nsresult rv = SetupVideoMode();
NS_ENSURE_SUCCESS(rv, rv);
// create and return new preview stream object
getPreviewStreamResult = new GetPreviewStreamResult(this, mVideoWidth, mVideoHeight, mVideoFrameRate, aGetPreviewStreamVideoMode->mOnSuccessCb);
rv = NS_DispatchToMainThread(getPreviewStreamResult);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch GetPreviewStreamVideoMode() onSuccess callback to main thread!");
return rv;
}
return NS_OK;
}
// Gonk callback handlers.
namespace mozilla {

View File

@ -24,6 +24,7 @@
#include "DOMCameraControl.h"
#include "CameraControlImpl.h"
#include "CameraCommon.h"
#include "GonkRecorder.h"
namespace mozilla {
@ -47,6 +48,9 @@ public:
void SetParameter(uint32_t aKey, const nsTArray<dom::CameraRegion>& aRegions);
nsresult PushParameters();
nsresult SetupRecording(int aFd);
nsresult SetupVideoMode();
void AutoFocusComplete(bool aSuccess);
void TakePictureComplete(uint8_t* aData, uint32_t aLength);
@ -56,12 +60,14 @@ protected:
nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream);
nsresult StartPreviewImpl(StartPreviewTask* aStartPreview);
nsresult StopPreviewImpl(StopPreviewTask* aStopPreview);
nsresult StopPreviewInternal(bool aForced = false);
nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus);
nsresult TakePictureImpl(TakePictureTask* aTakePicture);
nsresult StartRecordingImpl(StartRecordingTask* aStartRecording);
nsresult StopRecordingImpl(StopRecordingTask* aStopRecording);
nsresult PushParametersImpl();
nsresult PullParametersImpl();
nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode);
void SetPreviewSize(uint32_t aWidth, uint32_t aHeight);
@ -84,6 +90,27 @@ protected:
uint32_t mFps;
uint32_t mDiscardedFrameCount;
android::MediaProfiles* mMediaProfiles;
android::GonkRecorder* mRecorder;
PRUint32 mVideoRotation;
PRUint32 mVideoWidth;
PRUint32 mVideoHeight;
nsString mVideoFile;
// camcorder profile settings for the desired quality level
int mDuration; // max recording duration (ignored)
int mVideoFileFormat; // output file format
int mVideoCodec; // video encoder
int mVideoBitRate; // video bit rate
int mVideoFrameRate; // video frame rate
int mVideoFrameWidth; // video frame width
int mVideoFrameHeight;// video frame height
int mAudioCodec; // audio encoder
int mAudioBitRate; // audio bit rate
int mAudioSampleRate; // audio sample rate
int mAudioChannels; // number of audio channels
private:
nsGonkCameraControl(const nsGonkCameraControl&) MOZ_DELETE;
nsGonkCameraControl& operator=(const nsGonkCameraControl&) MOZ_DELETE;

View File

@ -143,6 +143,34 @@ GonkCameraHardware::NotifyCallback(int32_t aMsgType, int32_t ext1, int32_t ext2,
}
}
void
GonkCameraHardware::DataCallbackTimestamp(nsecs_t aTimestamp, int32_t aMsgType, const sp<IMemory> &aDataPtr, void* aUser)
{
DOM_CAMERA_LOGI("%s",__func__);
GonkCameraHardware* hw = GetHardware((uint32_t)aUser);
if (!hw) {
DOM_CAMERA_LOGE("%s:aUser = %d resolved to no camera hw\n", __func__, (uint32_t)aUser);
return;
}
if (hw->mClosing) {
return;
}
sp<GonkCameraListener> listener;
{
//TODO
//Mutex::Autolock _l(hw->mLock);
listener = hw->mListener;
}
if (listener.get()) {
DOM_CAMERA_LOGI("Listener registered, posting recording frame!");
listener->postDataTimestamp(aTimestamp, aMsgType, aDataPtr);
} else {
DOM_CAMERA_LOGW("No listener was set. Drop a recording frame.");
hw->mHardware->releaseRecordingFrame(aDataPtr);
}
}
void
GonkCameraHardware::Init()
{
@ -162,7 +190,7 @@ GonkCameraHardware::Init()
if (sHwHandle == 0) {
sHwHandle = 1; // don't use 0
}
mHardware->setCallbacks(GonkCameraHardware::NotifyCallback, GonkCameraHardware::DataCallback, NULL, (void*)sHwHandle);
mHardware->setCallbacks(GonkCameraHardware::NotifyCallback, GonkCameraHardware::DataCallback, GonkCameraHardware::DataCallbackTimestamp, (void*)sHwHandle);
mInitialized = true;
}
@ -314,3 +342,81 @@ GonkCameraHardware::StopPreview(uint32_t aHwHandle)
hw->mHardware->stopPreview();
}
}
int
GonkCameraHardware::StartRecording(uint32_t aHwHandle)
{
DOM_CAMERA_LOGI("%s: aHwHandle = %d\n", __func__, aHwHandle);
int rv = OK;
GonkCameraHardware* hw = GetHardware(aHwHandle);
if (!hw) {
return DEAD_OBJECT;
}
if (hw->mHardware->recordingEnabled()) {
return OK;
}
if (!hw->mHardware->previewEnabled()) {
DOM_CAMERA_LOGW("Preview was not enabled, enabling now!\n");
rv = StartPreview(aHwHandle);
if (rv != OK) {
return rv;
}
}
// start recording mode
hw->mHardware->enableMsgType(CAMERA_MSG_VIDEO_FRAME);
DOM_CAMERA_LOGI("Calling hw->startRecording\n");
rv = hw->mHardware->startRecording();
if (rv != OK) {
DOM_CAMERA_LOGE("mHardware->startRecording() failed with status %d", rv);
}
return rv;
}
int
GonkCameraHardware::StopRecording(uint32_t aHwHandle)
{
DOM_CAMERA_LOGI("%s: aHwHandle = %d\n", __func__, aHwHandle);
GonkCameraHardware* hw = GetHardware(aHwHandle);
if (!hw) {
return DEAD_OBJECT;
}
hw->mHardware->disableMsgType(CAMERA_MSG_VIDEO_FRAME);
hw->mHardware->stopRecording();
return OK;
}
int
GonkCameraHardware::SetListener(uint32_t aHwHandle, const sp<GonkCameraListener>& aListener)
{
GonkCameraHardware* hw = GetHardware(aHwHandle);
if (!hw) {
return DEAD_OBJECT;
}
hw->mListener = aListener;
return OK;
}
void
GonkCameraHardware::ReleaseRecordingFrame(uint32_t aHwHandle, const sp<IMemory>& aFrame)
{
GonkCameraHardware* hw = GetHardware(aHwHandle);
if (hw) {
hw->mHardware->releaseRecordingFrame(aFrame);
}
}
int
GonkCameraHardware::StoreMetaDataInBuffers(uint32_t aHwHandle, bool aEnabled)
{
GonkCameraHardware* hw = GetHardware(aHwHandle);
if (!hw) {
return DEAD_OBJECT;
}
return hw->mHardware->storeMetaDataInBuffers(aEnabled);
}

View File

@ -20,6 +20,8 @@
#include "libcameraservice/CameraHardwareInterface.h"
#include "binder/IMemory.h"
#include "mozilla/ReentrantMonitor.h"
#include "GonkCameraListener.h"
#include <utils/threads.h>
#include "GonkCameraControl.h"
#include "CameraCommon.h"
@ -46,6 +48,7 @@ protected:
static void DataCallback(int32_t aMsgType, const sp<IMemory> &aDataPtr, camera_frame_metadata_t* aMetadata, void* aUser);
static void NotifyCallback(int32_t aMsgType, int32_t ext1, int32_t ext2, void* aUser);
static void DataCallbackTimestamp(nsecs_t aTimestamp, int32_t aMsgType, const sp<IMemory>& aDataPtr, void* aUser);
public:
virtual void OnNewFrame() MOZ_OVERRIDE;
@ -60,6 +63,11 @@ public:
static void StopPreview(uint32_t aHwHandle);
static int PushParameters(uint32_t aHwHandle, const CameraParameters& aParams);
static void PullParameters(uint32_t aHwHandle, CameraParameters& aParams);
static int StartRecording(uint32_t aHwHandle);
static int StopRecording(uint32_t aHwHandle);
static int SetListener(uint32_t aHwHandle, const sp<GonkCameraListener>& aListener);
static void ReleaseRecordingFrame(uint32_t aHwHandle, const sp<IMemory>& aFrame);
static int StoreMetaDataInBuffers(uint32_t aHwHandle, bool aEnabled);
protected:
static GonkCameraHardware* sHw;
@ -93,6 +101,7 @@ protected:
struct timespec mStart;
struct timespec mAutoFocusStart;
#endif
sp<GonkCameraListener> mListener;
bool mInitialized;
bool IsInitialized()

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GONK_CAMERA_LISTENER_H
#define GONK_CAMERA_LISTENER_H
#include <utils/Timers.h>
#include "libcameraservice/CameraHardwareInterface.h"
namespace android {
// ref-counted object for callbacks
class GonkCameraListener: virtual public RefBase
{
public:
virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) = 0;
virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr,
camera_frame_metadata_t *metadata) = 0;
virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) = 0;
};
}; // namespace android
#endif

View File

@ -0,0 +1,733 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <base/basictypes.h>
#include "nsDebug.h"
#define DOM_CAMERA_LOG_LEVEL 3
#include "CameraCommon.h"
#define LOGD DOM_CAMERA_LOGA
#define LOGV DOM_CAMERA_LOGI
#define LOGI DOM_CAMERA_LOGI
#define LOGW DOM_CAMERA_LOGW
#define LOGE DOM_CAMERA_LOGE
#include <OMX_Component.h>
#include "GonkCameraSource.h"
#include "GonkCameraListener.h"
#include "GonkCameraHwMgr.h"
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <utils/String8.h>
#include <cutils/properties.h>
using namespace mozilla;
namespace android {
static const int64_t CAMERA_SOURCE_TIMEOUT_NS = 3000000000LL;
struct GonkCameraSourceListener : public GonkCameraListener {
GonkCameraSourceListener(const sp<GonkCameraSource> &source);
virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2);
virtual void postData(int32_t msgType, const sp<IMemory> &dataPtr,
camera_frame_metadata_t *metadata);
virtual void postDataTimestamp(
nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
protected:
virtual ~GonkCameraSourceListener();
private:
wp<GonkCameraSource> mSource;
GonkCameraSourceListener(const GonkCameraSourceListener &);
GonkCameraSourceListener &operator=(const GonkCameraSourceListener &);
};
GonkCameraSourceListener::GonkCameraSourceListener(const sp<GonkCameraSource> &source)
: mSource(source) {
}
GonkCameraSourceListener::~GonkCameraSourceListener() {
}
void GonkCameraSourceListener::notify(int32_t msgType, int32_t ext1, int32_t ext2) {
LOGV("notify(%d, %d, %d)", msgType, ext1, ext2);
}
void GonkCameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr,
camera_frame_metadata_t *metadata) {
LOGV("postData(%d, ptr:%p, size:%d)",
msgType, dataPtr->pointer(), dataPtr->size());
sp<GonkCameraSource> source = mSource.promote();
if (source.get() != NULL) {
source->dataCallback(msgType, dataPtr);
}
}
void GonkCameraSourceListener::postDataTimestamp(
nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) {
sp<GonkCameraSource> source = mSource.promote();
if (source.get() != NULL) {
source->dataCallbackTimestamp(timestamp/1000, msgType, dataPtr);
}
}
static int32_t getColorFormat(const char* colorFormat) {
return OMX_COLOR_FormatYUV420SemiPlanar;
if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420P)) {
return OMX_COLOR_FormatYUV420Planar;
}
if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422SP)) {
return OMX_COLOR_FormatYUV422SemiPlanar;
}
if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420SP)) {
return OMX_COLOR_FormatYUV420SemiPlanar;
}
if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422I)) {
return OMX_COLOR_FormatYCbYCr;
}
if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_RGB565)) {
return OMX_COLOR_Format16bitRGB565;
}
if (!strcmp(colorFormat, "OMX_TI_COLOR_FormatYUV420PackedSemiPlanar")) {
return OMX_TI_COLOR_FormatYUV420PackedSemiPlanar;
}
LOGE("Uknown color format (%s), please add it to "
"GonkCameraSource::getColorFormat", colorFormat);
CHECK_EQ(0, "Unknown color format");
}
GonkCameraSource *GonkCameraSource::Create(
int32_t cameraHandle,
Size videoSize,
int32_t frameRate,
bool storeMetaDataInVideoBuffers) {
GonkCameraSource *source = new GonkCameraSource(cameraHandle,
videoSize, frameRate,
storeMetaDataInVideoBuffers);
return source;
}
GonkCameraSource::GonkCameraSource(
int32_t cameraHandle,
Size videoSize,
int32_t frameRate,
bool storeMetaDataInVideoBuffers)
: mCameraFlags(0),
mVideoFrameRate(-1),
mNumFramesReceived(0),
mLastFrameTimestampUs(0),
mStarted(false),
mNumFramesEncoded(0),
mTimeBetweenFrameCaptureUs(0),
mFirstFrameTimeUs(0),
mNumFramesDropped(0),
mNumGlitches(0),
mGlitchDurationThresholdUs(200000),
mCollectStats(false) {
mVideoSize.width = -1;
mVideoSize.height = -1;
mCameraHandle = cameraHandle;
mInitCheck = init(
videoSize, frameRate,
storeMetaDataInVideoBuffers);
if (mInitCheck != OK) releaseCamera();
}
status_t GonkCameraSource::initCheck() const {
return mInitCheck;
}
//TODO: Do we need to reimplement isCameraAvailable?
/*
* Check to see whether the requested video width and height is one
* of the supported sizes.
* @param width the video frame width in pixels
* @param height the video frame height in pixels
* @param suppportedSizes the vector of sizes that we check against
* @return true if the dimension (width and height) is supported.
*/
static bool isVideoSizeSupported(
int32_t width, int32_t height,
const Vector<Size>& supportedSizes) {
LOGV("isVideoSizeSupported");
for (size_t i = 0; i < supportedSizes.size(); ++i) {
if (width == supportedSizes[i].width &&
height == supportedSizes[i].height) {
return true;
}
}
return false;
}
/*
* If the preview and video output is separate, we only set the
* the video size, and applications should set the preview size
* to some proper value, and the recording framework will not
* change the preview size; otherwise, if the video and preview
* output is the same, we need to set the preview to be the same
* as the requested video size.
*
*/
/*
* Query the camera to retrieve the supported video frame sizes
* and also to see whether CameraParameters::setVideoSize()
* is supported or not.
* @param params CameraParameters to retrieve the information
* @@param isSetVideoSizeSupported retunrs whether method
* CameraParameters::setVideoSize() is supported or not.
* @param sizes returns the vector of Size objects for the
* supported video frame sizes advertised by the camera.
*/
static void getSupportedVideoSizes(
const CameraParameters& params,
bool *isSetVideoSizeSupported,
Vector<Size>& sizes) {
*isSetVideoSizeSupported = true;
params.getSupportedVideoSizes(sizes);
if (sizes.size() == 0) {
LOGD("Camera does not support setVideoSize()");
params.getSupportedPreviewSizes(sizes);
*isSetVideoSizeSupported = false;
}
}
/*
* Check whether the camera has the supported color format
* @param params CameraParameters to retrieve the information
* @return OK if no error.
*/
status_t GonkCameraSource::isCameraColorFormatSupported(
const CameraParameters& params) {
mColorFormat = getColorFormat(params.get(
CameraParameters::KEY_VIDEO_FRAME_FORMAT));
if (mColorFormat == -1) {
return BAD_VALUE;
}
return OK;
}
/*
* Configure the camera to use the requested video size
* (width and height) and/or frame rate. If both width and
* height are -1, configuration on the video size is skipped.
* if frameRate is -1, configuration on the frame rate
* is skipped. Skipping the configuration allows one to
* use the current camera setting without the need to
* actually know the specific values (see Create() method).
*
* @param params the CameraParameters to be configured
* @param width the target video frame width in pixels
* @param height the target video frame height in pixels
* @param frameRate the target frame rate in frames per second.
* @return OK if no error.
*/
status_t GonkCameraSource::configureCamera(
CameraParameters* params,
int32_t width, int32_t height,
int32_t frameRate) {
LOGV("configureCamera");
Vector<Size> sizes;
bool isSetVideoSizeSupportedByCamera = true;
getSupportedVideoSizes(*params, &isSetVideoSizeSupportedByCamera, sizes);
bool isCameraParamChanged = false;
if (width != -1 && height != -1) {
if (!isVideoSizeSupported(width, height, sizes)) {
LOGE("Video dimension (%dx%d) is unsupported", width, height);
return BAD_VALUE;
}
if (isSetVideoSizeSupportedByCamera) {
params->setVideoSize(width, height);
} else {
params->setPreviewSize(width, height);
}
isCameraParamChanged = true;
} else if ((width == -1 && height != -1) ||
(width != -1 && height == -1)) {
// If one and only one of the width and height is -1
// we reject such a request.
LOGE("Requested video size (%dx%d) is not supported", width, height);
return BAD_VALUE;
} else { // width == -1 && height == -1
// Do not configure the camera.
// Use the current width and height value setting from the camera.
}
if (frameRate != -1) {
CHECK(frameRate > 0 && frameRate <= 120);
const char* supportedFrameRates =
params->get(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES);
CHECK(supportedFrameRates != NULL);
LOGV("Supported frame rates: %s", supportedFrameRates);
char buf[4];
snprintf(buf, 4, "%d", frameRate);
if (strstr(supportedFrameRates, buf) == NULL) {
LOGE("Requested frame rate (%d) is not supported: %s",
frameRate, supportedFrameRates);
return BAD_VALUE;
}
// The frame rate is supported, set the camera to the requested value.
params->setPreviewFrameRate(frameRate);
isCameraParamChanged = true;
} else { // frameRate == -1
// Do not configure the camera.
// Use the current frame rate value setting from the camera
}
if (isCameraParamChanged) {
// Either frame rate or frame size needs to be changed.
if (OK != GonkCameraHardware::PushParameters(mCameraHandle,*params)) {
LOGE("Could not change settings."
" Someone else is using camera ?");
return -EBUSY;
}
}
return OK;
}
/*
* Check whether the requested video frame size
* has been successfully configured or not. If both width and height
* are -1, check on the current width and height value setting
* is performed.
*
* @param params CameraParameters to retrieve the information
* @param the target video frame width in pixels to check against
* @param the target video frame height in pixels to check against
* @return OK if no error
*/
status_t GonkCameraSource::checkVideoSize(
const CameraParameters& params,
int32_t width, int32_t height) {
LOGV("checkVideoSize");
// The actual video size is the same as the preview size
// if the camera hal does not support separate video and
// preview output. In this case, we retrieve the video
// size from preview.
int32_t frameWidthActual = -1;
int32_t frameHeightActual = -1;
Vector<Size> sizes;
params.getSupportedVideoSizes(sizes);
if (sizes.size() == 0) {
// video size is the same as preview size
params.getPreviewSize(&frameWidthActual, &frameHeightActual);
} else {
// video size may not be the same as preview
params.getVideoSize(&frameWidthActual, &frameHeightActual);
}
if (frameWidthActual < 0 || frameHeightActual < 0) {
LOGE("Failed to retrieve video frame size (%dx%d)",
frameWidthActual, frameHeightActual);
return UNKNOWN_ERROR;
}
// Check the actual video frame size against the target/requested
// video frame size.
if (width != -1 && height != -1) {
if (frameWidthActual != width || frameHeightActual != height) {
LOGE("Failed to set video frame size to %dx%d. "
"The actual video size is %dx%d ", width, height,
frameWidthActual, frameHeightActual);
return UNKNOWN_ERROR;
}
}
// Good now.
mVideoSize.width = frameWidthActual;
mVideoSize.height = frameHeightActual;
return OK;
}
/*
* Check the requested frame rate has been successfully configured or not.
* If the target frameRate is -1, check on the current frame rate value
* setting is performed.
*
* @param params CameraParameters to retrieve the information
* @param the target video frame rate to check against
* @return OK if no error.
*/
status_t GonkCameraSource::checkFrameRate(
const CameraParameters& params,
int32_t frameRate) {
LOGV("checkFrameRate");
int32_t frameRateActual = params.getPreviewFrameRate();
if (frameRateActual < 0) {
LOGE("Failed to retrieve preview frame rate (%d)", frameRateActual);
return UNKNOWN_ERROR;
}
// Check the actual video frame rate against the target/requested
// video frame rate.
if (frameRate != -1 && (frameRateActual - frameRate) != 0) {
LOGE("Failed to set preview frame rate to %d fps. The actual "
"frame rate is %d", frameRate, frameRateActual);
return UNKNOWN_ERROR;
}
// Good now.
mVideoFrameRate = frameRateActual;
return OK;
}
/*
* Initialize the CameraSource to so that it becomes
* ready for providing the video input streams as requested.
* @param camera the camera object used for the video source
* @param cameraId if camera == 0, use camera with this id
* as the video source
* @param videoSize the target video frame size. If both
* width and height in videoSize is -1, use the current
* width and heigth settings by the camera
* @param frameRate the target frame rate in frames per second.
* if it is -1, use the current camera frame rate setting.
* @param storeMetaDataInVideoBuffers request to store meta
* data or real YUV data in video buffers. Request to
* store meta data in video buffers may not be honored
* if the source does not support this feature.
*
* @return OK if no error.
*/
status_t GonkCameraSource::init(
Size videoSize,
int32_t frameRate,
bool storeMetaDataInVideoBuffers) {
LOGV("init");
status_t err = OK;
//TODO: need to do something here to check the sanity of camera
CameraParameters params;
GonkCameraHardware::PullParameters(mCameraHandle, params);
if ((err = isCameraColorFormatSupported(params)) != OK) {
return err;
}
// Set the camera to use the requested video frame size
// and/or frame rate.
if ((err = configureCamera(&params,
videoSize.width, videoSize.height,
frameRate))) {
return err;
}
// Check on video frame size and frame rate.
CameraParameters newCameraParams;
GonkCameraHardware::PullParameters(mCameraHandle, newCameraParams);
if ((err = checkVideoSize(newCameraParams,
videoSize.width, videoSize.height)) != OK) {
return err;
}
if ((err = checkFrameRate(newCameraParams, frameRate)) != OK) {
return err;
}
// By default, do not store metadata in video buffers
mIsMetaDataStoredInVideoBuffers = false;
GonkCameraHardware::StoreMetaDataInBuffers(mCameraHandle, false);
if (storeMetaDataInVideoBuffers) {
if (OK == GonkCameraHardware::StoreMetaDataInBuffers(mCameraHandle, true)) {
mIsMetaDataStoredInVideoBuffers = true;
}
}
const char *hfr_str = params.get("video-hfr");
int32_t hfr = -1;
if ( hfr_str != NULL ) {
hfr = atoi(hfr_str);
}
if(hfr < 0) {
LOGW("Invalid hfr value(%d) set from app. Disabling HFR.", hfr);
hfr = 0;
}
int64_t glitchDurationUs = (1000000LL / mVideoFrameRate);
if (glitchDurationUs > mGlitchDurationThresholdUs) {
mGlitchDurationThresholdUs = glitchDurationUs;
}
const char * k3dFrameArrangement = "3d-frame-format";
const char * arrangement = params.get(k3dFrameArrangement);
// XXX: just assume left/right for now since that's all the camera supports
bool want3D = (arrangement != NULL && !strcmp("left-right", arrangement));
// XXX: query camera for the stride and slice height
// when the capability becomes available.
mMeta = new MetaData;
mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
mMeta->setInt32(kKeyColorFormat, mColorFormat);
mMeta->setInt32(kKeyWidth, mVideoSize.width);
mMeta->setInt32(kKeyHeight, mVideoSize.height);
mMeta->setInt32(kKeyStride, mVideoSize.width);
mMeta->setInt32(kKeySliceHeight, mVideoSize.height);
mMeta->setInt32(kKeyFrameRate, mVideoFrameRate);
return OK;
}
GonkCameraSource::~GonkCameraSource() {
if (mStarted) {
stop();
} else if (mInitCheck == OK) {
// Camera is initialized but because start() is never called,
// the lock on Camera is never released(). This makes sure
// Camera's lock is released in this case.
// TODO: Don't think I need to do this
releaseCamera();
}
}
void GonkCameraSource::startCameraRecording() {
LOGV("startCameraRecording");
CHECK_EQ(OK, GonkCameraHardware::StartRecording(mCameraHandle));
}
status_t GonkCameraSource::start(MetaData *meta) {
LOGV("start");
CHECK(!mStarted);
if (mInitCheck != OK) {
LOGE("GonkCameraSource is not initialized yet");
return mInitCheck;
}
char value[PROPERTY_VALUE_MAX];
if (property_get("media.stagefright.record-stats", value, NULL)
&& (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
mCollectStats = true;
}
mStartTimeUs = 0;
int64_t startTimeUs;
if (meta && meta->findInt64(kKeyTime, &startTimeUs)) {
LOGV("Metadata enabled, startime: %lld us", startTimeUs);
mStartTimeUs = startTimeUs;
}
// Register a listener with GonkCameraHardware so that we can get callbacks
GonkCameraHardware::SetListener(mCameraHandle, new GonkCameraSourceListener(this));
startCameraRecording();
mStarted = true;
return OK;
}
void GonkCameraSource::stopCameraRecording() {
LOGV("stopCameraRecording");
GonkCameraHardware::StopRecording(mCameraHandle);
}
void GonkCameraSource::releaseCamera() {
LOGV("releaseCamera");
}
status_t GonkCameraSource::stop() {
LOGV("stop: E");
Mutex::Autolock autoLock(mLock);
mStarted = false;
mFrameAvailableCondition.signal();
releaseQueuedFrames();
while (!mFramesBeingEncoded.empty()) {
if (NO_ERROR !=
mFrameCompleteCondition.waitRelative(mLock,
mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) {
LOGW("Timed out waiting for outstanding frames being encoded: %d",
mFramesBeingEncoded.size());
}
}
LOGV("Calling stopCameraRecording");
stopCameraRecording();
releaseCamera();
if (mCollectStats) {
LOGI("Frames received/encoded/dropped: %d/%d/%d in %lld us",
mNumFramesReceived, mNumFramesEncoded, mNumFramesDropped,
mLastFrameTimestampUs - mFirstFrameTimeUs);
}
if (mNumGlitches > 0) {
LOGW("%d long delays between neighboring video frames", mNumGlitches);
}
CHECK_EQ(mNumFramesReceived, mNumFramesEncoded + mNumFramesDropped);
LOGV("stop: X");
return OK;
}
void GonkCameraSource::releaseRecordingFrame(const sp<IMemory>& frame) {
LOGV("releaseRecordingFrame");
GonkCameraHardware::ReleaseRecordingFrame(mCameraHandle, frame);
}
void GonkCameraSource::releaseQueuedFrames() {
List<sp<IMemory> >::iterator it;
while (!mFramesReceived.empty()) {
it = mFramesReceived.begin();
releaseRecordingFrame(*it);
mFramesReceived.erase(it);
++mNumFramesDropped;
}
}
sp<MetaData> GonkCameraSource::getFormat() {
return mMeta;
}
void GonkCameraSource::releaseOneRecordingFrame(const sp<IMemory>& frame) {
releaseRecordingFrame(frame);
}
void GonkCameraSource::signalBufferReturned(MediaBuffer *buffer) {
LOGV("signalBufferReturned: %p", buffer->data());
Mutex::Autolock autoLock(mLock);
for (List<sp<IMemory> >::iterator it = mFramesBeingEncoded.begin();
it != mFramesBeingEncoded.end(); ++it) {
if ((*it)->pointer() == buffer->data()) {
releaseOneRecordingFrame((*it));
mFramesBeingEncoded.erase(it);
++mNumFramesEncoded;
buffer->setObserver(0);
buffer->release();
mFrameCompleteCondition.signal();
return;
}
}
CHECK_EQ(0, "signalBufferReturned: bogus buffer");
}
status_t GonkCameraSource::read(
MediaBuffer **buffer, const ReadOptions *options) {
LOGV("read");
*buffer = NULL;
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
return ERROR_UNSUPPORTED;
}
sp<IMemory> frame;
int64_t frameTime;
{
Mutex::Autolock autoLock(mLock);
while (mStarted && mFramesReceived.empty()) {
if (NO_ERROR !=
mFrameAvailableCondition.waitRelative(mLock,
mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) {
//TODO: check sanity of camera?
LOGW("Timed out waiting for incoming camera video frames: %lld us",
mLastFrameTimestampUs);
}
}
if (!mStarted) {
return OK;
}
frame = *mFramesReceived.begin();
mFramesReceived.erase(mFramesReceived.begin());
frameTime = *mFrameTimes.begin();
mFrameTimes.erase(mFrameTimes.begin());
mFramesBeingEncoded.push_back(frame);
*buffer = new MediaBuffer(frame->pointer(), frame->size());
(*buffer)->setObserver(this);
(*buffer)->add_ref();
(*buffer)->meta_data()->setInt64(kKeyTime, frameTime);
}
return OK;
}
void GonkCameraSource::dataCallbackTimestamp(int64_t timestampUs,
int32_t msgType, const sp<IMemory> &data) {
LOGV("dataCallbackTimestamp: timestamp %lld us", timestampUs);
//LOGV("dataCallbackTimestamp: data %x size %d", data->pointer(), data->size());
Mutex::Autolock autoLock(mLock);
if (!mStarted || (mNumFramesReceived == 0 && timestampUs < mStartTimeUs)) {
LOGV("Drop frame at %lld/%lld us", timestampUs, mStartTimeUs);
releaseOneRecordingFrame(data);
return;
}
if (mNumFramesReceived > 0) {
CHECK(timestampUs > mLastFrameTimestampUs);
if (timestampUs - mLastFrameTimestampUs > mGlitchDurationThresholdUs) {
++mNumGlitches;
}
}
// May need to skip frame or modify timestamp. Currently implemented
// by the subclass GonkCameraSourceTimeLapse.
if (skipCurrentFrame(timestampUs)) {
releaseOneRecordingFrame(data);
return;
}
mLastFrameTimestampUs = timestampUs;
if (mNumFramesReceived == 0) {
mFirstFrameTimeUs = timestampUs;
// Initial delay
if (mStartTimeUs > 0) {
if (timestampUs < mStartTimeUs) {
// Frame was captured before recording was started
// Drop it without updating the statistical data.
releaseOneRecordingFrame(data);
return;
}
mStartTimeUs = timestampUs - mStartTimeUs;
}
}
++mNumFramesReceived;
CHECK(data != NULL && data->size() > 0);
mFramesReceived.push_back(data);
int64_t timeUs = mStartTimeUs + (timestampUs - mFirstFrameTimeUs);
mFrameTimes.push_back(timeUs);
LOGV("initial delay: %lld, current time stamp: %lld",
mStartTimeUs, timeUs);
mFrameAvailableCondition.signal();
}
bool GonkCameraSource::isMetaDataStoredInVideoBuffers() const {
LOGV("isMetaDataStoredInVideoBuffers");
return mIsMetaDataStoredInVideoBuffers;
}
} // namespace android

View File

@ -0,0 +1,161 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GONK_CAMERA_SOURCE_H_
#define GONK_CAMERA_SOURCE_H_
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaSource.h>
#include <camera/CameraParameters.h>
#include <utils/List.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
namespace android {
class IMemory;
class GonkCameraSourceListener;
class GonkCameraSource : public MediaSource, public MediaBufferObserver {
public:
static GonkCameraSource *Create(int32_t cameraHandle,
Size videoSize,
int32_t frameRate,
bool storeMetaDataInVideoBuffers = false);
virtual ~GonkCameraSource();
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
/**
* Check whether a GonkCameraSource object is properly initialized.
* Must call this method before stop().
* @return OK if initialization has successfully completed.
*/
virtual status_t initCheck() const;
/**
* Returns the MetaData associated with the GonkCameraSource,
* including:
* kKeyColorFormat: YUV color format of the video frames
* kKeyWidth, kKeyHeight: dimension (in pixels) of the video frames
* kKeySampleRate: frame rate in frames per second
* kKeyMIMEType: always fixed to be MEDIA_MIMETYPE_VIDEO_RAW
*/
virtual sp<MetaData> getFormat();
/**
* Tell whether this camera source stores meta data or real YUV
* frame data in video buffers.
*
* @return true if meta data is stored in the video
* buffers; false if real YUV data is stored in
* the video buffers.
*/
bool isMetaDataStoredInVideoBuffers() const;
virtual void signalBufferReturned(MediaBuffer* buffer);
protected:
enum CameraFlags {
FLAGS_SET_CAMERA = 1L << 0,
FLAGS_HOT_CAMERA = 1L << 1,
};
int32_t mCameraFlags;
Size mVideoSize;
int32_t mVideoFrameRate;
int32_t mColorFormat;
status_t mInitCheck;
sp<MetaData> mMeta;
int64_t mStartTimeUs;
int32_t mNumFramesReceived;
int64_t mLastFrameTimestampUs;
bool mStarted;
int32_t mNumFramesEncoded;
// Time between capture of two frames.
int64_t mTimeBetweenFrameCaptureUs;
GonkCameraSource(int32_t cameraHandle,
Size videoSize, int32_t frameRate,
bool storeMetaDataInVideoBuffers = false);
virtual void startCameraRecording();
virtual void stopCameraRecording();
virtual void releaseRecordingFrame(const sp<IMemory>& frame);
// Returns true if need to skip the current frame.
// Called from dataCallbackTimestamp.
virtual bool skipCurrentFrame(int64_t timestampUs) {return false;}
friend class GonkCameraSourceListener;
// Callback called when still camera raw data is available.
virtual void dataCallback(int32_t msgType, const sp<IMemory> &data) {}
virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
const sp<IMemory> &data);
private:
Mutex mLock;
Condition mFrameAvailableCondition;
Condition mFrameCompleteCondition;
List<sp<IMemory> > mFramesReceived;
List<sp<IMemory> > mFramesBeingEncoded;
List<int64_t> mFrameTimes;
int64_t mFirstFrameTimeUs;
int32_t mNumFramesDropped;
int32_t mNumGlitches;
int64_t mGlitchDurationThresholdUs;
bool mCollectStats;
bool mIsMetaDataStoredInVideoBuffers;
int32_t mCameraHandle;
void releaseQueuedFrames();
void releaseOneRecordingFrame(const sp<IMemory>& frame);
status_t init(Size videoSize, int32_t frameRate,
bool storeMetaDataInVideoBuffers);
status_t isCameraColorFormatSupported(const CameraParameters& params);
status_t configureCamera(CameraParameters* params,
int32_t width, int32_t height,
int32_t frameRate);
status_t checkVideoSize(const CameraParameters& params,
int32_t width, int32_t height);
status_t checkFrameRate(const CameraParameters& params,
int32_t frameRate);
void releaseCamera();
GonkCameraSource(const GonkCameraSource &);
GonkCameraSource &operator=(const GonkCameraSource &);
};
} // namespace android
#endif // GONK_CAMERA_SOURCE_H_

1629
dom/camera/GonkRecorder.cpp Normal file

File diff suppressed because it is too large Load Diff

174
dom/camera/GonkRecorder.h Normal file
View File

@ -0,0 +1,174 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GONK_RECORDER_H_
#define GONK_RECORDER_H_
#include <media/mediarecorder.h>
#include <camera/CameraParameters.h>
#include <utils/String8.h>
#include <system/audio.h>
namespace android {
class GonkCameraSource;
struct MediaSource;
struct MediaWriter;
class MetaData;
struct AudioSource;
class MediaProfiles;
struct GonkRecorder {
GonkRecorder();
virtual ~GonkRecorder();
virtual status_t init();
virtual status_t setAudioSource(audio_source_t as);
virtual status_t setVideoSource(video_source vs);
virtual status_t setOutputFormat(output_format of);
virtual status_t setAudioEncoder(audio_encoder ae);
virtual status_t setVideoEncoder(video_encoder ve);
virtual status_t setVideoSize(int width, int height);
virtual status_t setVideoFrameRate(int frames_per_second);
virtual status_t setOutputFile(const char *path);
virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
virtual status_t setParameters(const String8& params);
virtual status_t setCameraHandle(int32_t handle);
virtual status_t setListener(const sp<IMediaRecorderClient>& listener);
virtual status_t prepare();
virtual status_t start();
virtual status_t pause();
virtual status_t stop();
virtual status_t close();
virtual status_t reset();
virtual status_t getMaxAmplitude(int *max);
virtual status_t dump(int fd, const Vector<String16>& args) const;
// Querying a SurfaceMediaSourcer
private:
sp<IMediaRecorderClient> mListener;
sp<MediaWriter> mWriter;
int mOutputFd;
sp<AudioSource> mAudioSourceNode;
audio_source_t mAudioSource;
video_source mVideoSource;
output_format mOutputFormat;
audio_encoder mAudioEncoder;
video_encoder mVideoEncoder;
bool mUse64BitFileOffset;
int32_t mVideoWidth, mVideoHeight;
int32_t mFrameRate;
int32_t mVideoBitRate;
int32_t mAudioBitRate;
int32_t mAudioChannels;
int32_t mSampleRate;
int32_t mInterleaveDurationUs;
int32_t mIFramesIntervalSec;
int32_t mCameraId;
int32_t mVideoEncoderProfile;
int32_t mVideoEncoderLevel;
int32_t mMovieTimeScale;
int32_t mVideoTimeScale;
int32_t mAudioTimeScale;
int64_t mMaxFileSizeBytes;
int64_t mMaxFileDurationUs;
int64_t mTrackEveryTimeDurationUs;
int32_t mRotationDegrees; // Clockwise
int32_t mLatitudex10000;
int32_t mLongitudex10000;
int32_t mStartTimeOffsetMs;
String8 mParams;
bool mIsMetaDataStoredInVideoBuffers;
MediaProfiles *mEncoderProfiles;
bool mStarted;
// Needed when GLFrames are encoded.
// An <ISurfaceTexture> pointer
// will be sent to the client side using which the
// frame buffers will be queued and dequeued
bool mDisableAudio;
int32_t mCameraHandle;
status_t setupMPEG4Recording(
int outputFd,
int32_t videoWidth, int32_t videoHeight,
int32_t videoBitRate,
int32_t *totalBitRate,
sp<MediaWriter> *mediaWriter);
void setupMPEG4MetaData(int64_t startTimeUs, int32_t totalBitRate,
sp<MetaData> *meta);
status_t startMPEG4Recording();
status_t startAMRRecording();
status_t startRawAudioRecording();
status_t startMPEG2TSRecording();
sp<MediaSource> createAudioSource();
status_t checkVideoEncoderCapabilities();
status_t checkAudioEncoderCapabilities();
// Generic MediaSource set-up. Returns the appropriate
// source (CameraSource or SurfaceMediaSource)
// depending on the videosource type
status_t setupMediaSource(sp<MediaSource> *mediaSource);
status_t setupCameraSource(sp<GonkCameraSource> *cameraSource);
// setup the surfacemediasource for the encoder
status_t setupAudioEncoder(const sp<MediaWriter>& writer);
status_t setupVideoEncoder(
sp<MediaSource> cameraSource,
int32_t videoBitRate,
sp<MediaSource> *source);
// Encoding parameter handling utilities
status_t setParameter(const String8 &key, const String8 &value);
status_t setParamAudioEncodingBitRate(int32_t bitRate);
status_t setParamAudioNumberOfChannels(int32_t channles);
status_t setParamAudioSamplingRate(int32_t sampleRate);
status_t setParamAudioTimeScale(int32_t timeScale);
status_t setParamVideoEncodingBitRate(int32_t bitRate);
status_t setParamVideoIFramesInterval(int32_t seconds);
status_t setParamVideoEncoderProfile(int32_t profile);
status_t setParamVideoEncoderLevel(int32_t level);
status_t setParamVideoCameraId(int32_t cameraId);
status_t setParamVideoTimeScale(int32_t timeScale);
status_t setParamVideoRotation(int32_t degrees);
status_t setParamTrackTimeStatus(int64_t timeDurationUs);
status_t setParamInterleaveDuration(int32_t durationUs);
status_t setParam64BitFileOffset(bool use64BitFileOffset);
status_t setParamMaxFileDurationUs(int64_t timeUs);
status_t setParamMaxFileSizeBytes(int64_t bytes);
status_t setParamMovieTimeScale(int32_t timeScale);
status_t setParamGeoDataLongitude(int64_t longitudex10000);
status_t setParamGeoDataLatitude(int64_t latitudex10000);
void clipVideoBitRate();
void clipVideoFrameRate();
void clipVideoFrameWidth();
void clipVideoFrameHeight();
void clipAudioBitRate();
void clipAudioSampleRate();
void clipNumberOfAudioChannels();
void setDefaultProfileIfNecessary();
GonkRecorder(const GonkRecorder &);
GonkRecorder &operator=(const GonkRecorder &);
};
} // namespace android
#endif // GONK_RECORDER_H_

View File

@ -6,6 +6,7 @@
#define DOM_CAMERA_ICAMERACONTROL_H
#include "jsapi.h"
#include "nsIDOMDeviceStorage.h"
#include "nsIDOMCameraManager.h"
#include "DictionaryHelpers.h"
#include "CameraCommon.h"
@ -26,8 +27,9 @@ public:
virtual void StopPreview() = 0;
virtual nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
virtual nsresult TakePicture(CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
virtual nsresult StartRecording(CameraSize aSize, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
virtual nsresult StartRecording(nsIDOMDeviceStorage* aStorageArea, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
virtual nsresult StopRecording() = 0;
virtual nsresult GetPreviewStreamVideoMode(CameraRecordingOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
virtual nsresult Set(uint32_t aKey, const nsAString& aValue) = 0;
virtual nsresult Get(uint32_t aKey, nsAString& aValue) = 0;

View File

@ -32,6 +32,9 @@ CPPSRCS += \
GonkCameraControl.cpp \
GonkCameraHwMgr.cpp \
GonkNativeWindow.cpp \
GonkRecorder.cpp \
GonkCameraSource.cpp \
AudioParameter.cpp \
$(NULL)
else ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
CPPSRCS += \

28
dom/camera/README Normal file
View File

@ -0,0 +1,28 @@
This README file details from where some of the camcorder source files were derived from and how to apply the provided patch file to get the updated files for B2G.
---------------------------------
Following is the list of B2G files which were derived from an android ics_chocolate build. It also shows the corresponding locations where the original source files can be found:
GonkRecoder.cpp:
https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=media/libmediaplayerservice/StagefrightRecorder.cpp;hb=ef1672482a9c2b88d8017927df68144fee42626c
GonkRecorder.h:
https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=media/libmediaplayerservice/StagefrightRecorder.h;hb=e3682213bcd3fe43b059e00f0fe4dbebc3f3c35d
GonkCameraSource.cpp:
https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=media/libstagefright/CameraSource.cpp;hb=7fa677babfee9c241a131b22c9c1c5ab512ef2d2
GonkCameraSource.h:
https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=include/media/stagefright/CameraSource.h;hb=96af14d9b013496accf40a85a66fefcba3ac0111
AudioParameter.cpp:
https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=media/libmedia/AudioParameter.cpp;hb=4dc22e77cfd2a1c3671e5646ee87c5e4c15596a0
GonkCameraListener.h:
https://www.codeaurora.org/gitweb/quic/la/?p=platform/frameworks/base.git;a=blob;f=include/camera/Camera.h;hb=796f35e408d9dca386f90d8fbde80471ac011fa6
There were quite a few changes done to the above listed sources to support camcorder on B2G platform.
update.patch lists the changes on top of the original files.
update.sh shell script copies the files from an android tree and applies the patch to get updated files for B2G.

View File

@ -5,6 +5,7 @@
interface nsIDOMBlob;
interface nsIDOMDeviceStorage;
/* Used to set the dimensions of a captured picture,
a preview stream, a video capture stream, etc. */
@ -108,10 +109,8 @@ interface nsICameraCapabilities : nsISupports
readonly attribute jsval videoSizes;
};
/*
These properties only affect the captured image;
invalid property settings are ignored.
*/
/* These properties only affect the captured image;
invalid property settings are ignored. */
dictionary CameraPictureOptions
{
/* an object with a combination of 'height' and 'width' properties
@ -147,6 +146,14 @@ dictionary CameraPictureOptions
jsval position;
};
/* These properties affect video recording. */
dictionary CameraRecordingOptions
{
long width;
long height;
long rotation;
};
[scriptable, function, uuid(0444a687-4bc9-462c-8246-5423f0fe46a4)]
interface nsICameraPreviewStreamCallback : nsISupports
{
@ -165,10 +172,10 @@ interface nsICameraTakePictureCallback : nsISupports
void handleEvent(in nsIDOMBlob picture);
};
[scriptable, function, uuid(ac43f123-529c-48d3-84dd-ad206b7aca9b)]
[scriptable, function, uuid(89a762f8-581b-410a-ad86-e2bd2113ad82)]
interface nsICameraStartRecordingCallback : nsISupports
{
void handleEvent(in nsIDOMMediaStream stream);
void handleEvent();
};
[scriptable, function, uuid(fb80db71-e315-42f0-9ea9-dd3dd312ed70)]
@ -187,7 +194,7 @@ interface nsICameraErrorCallback : nsISupports
attributes here affect the preview, any pictures taken, and/or
any video recorded by the camera.
*/
[scriptable, uuid(b8949e5c-55b0-49dd-99a9-68d11342915a)]
[scriptable, uuid(469e0462-59e4-4ed5-afa9-aecd1256ee30)]
interface nsICameraControl : nsISupports
{
readonly attribute nsICameraCapabilities capabilities;
@ -290,15 +297,21 @@ interface nsICameraControl : nsISupports
[implicit_jscontext]
void takePicture(in jsval aOptions, in nsICameraTakePictureCallback onSuccess, [optional] in nsICameraErrorCallback onError);
/* start recording video; 'aOptions' define the frame size of to
capture, chosen from capabilities.videoSizes, e.g.:
/* get a media stream to be used as a camera viewfinder in video mode; 'aOptions'
define the frame size of the video capture, chosen from capabilities.videoSizes, e.g.:
{
width: 640,
height: 480
height: 480,
rotation: 90
}
*/
[implicit_jscontext]
void startRecording(in jsval aOptions, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError);
void getPreviewStreamVideoMode(in jsval aOptions, in nsICameraPreviewStreamCallback onSuccess, [optional] in nsICameraErrorCallback onError);
/* start recording video;
*/
[implicit_jscontext]
void startRecording(in nsIDOMDeviceStorage storageArea, in DOMString filename, in nsICameraStartRecordingCallback onSuccess, [optional] in nsICameraErrorCallback onError);
/* stop precording video. */
void stopRecording();

2296
dom/camera/update.patch Normal file

File diff suppressed because it is too large Load Diff

14
dom/camera/update.sh Normal file
View File

@ -0,0 +1,14 @@
# Usage: ./update.sh <android_ics_os_src_directory>
#
# Copies the needed files from the directory containing the original
# Android ICS OS source and applies the B2G specific changes for the
# camcorder functionality in B2G.
cp $1/frameworks/base/media/libmediaplayerservice/StagefrightRecorder.cpp ./GonkRecorder.cpp
cp $1/frameworks/base/media/libmediaplayerservice/StagefrightRecorder.h ./GonkRecorder.h
cp $1/frameworks/base/media/libstagefright/CameraSource.cpp ./GonkCameraSource.cpp
cp $1/frameworks/base/include/media/stagefright/CameraSource.h ./GonkCameraSource.h
cp $1/frameworks/base/media/libmedia/AudioParameter.cpp ./AudioParameter.cpp
cp $1/frameworks/base/include/camera/Camera.h ./GonkCameraListener.h
patch -p1 <update.patch
# If you import CAF sources, you also need to apply update2.patch
patch -p1 <update2.patch

163
dom/camera/update2.patch Normal file
View File

@ -0,0 +1,163 @@
diff --git a/dom/camera/GonkCameraSource.cpp b/dom/camera/GonkCameraSource.cpp
--- a/dom/camera/GonkCameraSource.cpp
+++ b/dom/camera/GonkCameraSource.cpp
@@ -492,21 +492,17 @@ status_t GonkCameraSource::init(
mMeta = new MetaData;
mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
mMeta->setInt32(kKeyColorFormat, mColorFormat);
mMeta->setInt32(kKeyWidth, mVideoSize.width);
mMeta->setInt32(kKeyHeight, mVideoSize.height);
mMeta->setInt32(kKeyStride, mVideoSize.width);
mMeta->setInt32(kKeySliceHeight, mVideoSize.height);
mMeta->setInt32(kKeyFrameRate, mVideoFrameRate);
- mMeta->setInt32(kKeyHFR, hfr);
- if (want3D) {
- mMeta->setInt32(kKey3D, !0);
- }
return OK;
}
GonkCameraSource::~GonkCameraSource() {
if (mStarted) {
stop();
} else if (mInitCheck == OK) {
// Camera is initialized but because start() is never called,
diff --git a/dom/camera/GonkRecorder.cpp b/dom/camera/GonkRecorder.cpp
--- a/dom/camera/GonkRecorder.cpp
+++ b/dom/camera/GonkRecorder.cpp
@@ -716,56 +716,16 @@ status_t GonkRecorder::start() {
mStarted = true;
}
return status;
}
sp<MediaSource> GonkRecorder::createAudioSource() {
- bool tunneledSource = false;
- const char *tunnelMime;
- {
- AudioParameter param;
- String8 key("tunneled-input-formats");
- param.add( key, String8("get") );
- String8 valueStr = AudioSystem::getParameters( 0, param.toString());
- AudioParameter result(valueStr);
- int value;
- if ( mAudioEncoder == AUDIO_ENCODER_AMR_NB &&
- result.getInt(String8("AMR"),value) == NO_ERROR ) {
- tunneledSource = true;
- tunnelMime = MEDIA_MIMETYPE_AUDIO_AMR_NB;
- }
- else if ( mAudioEncoder == AUDIO_ENCODER_QCELP &&
- result.getInt(String8("QCELP"),value) == NO_ERROR ) {
- tunneledSource = true;
- tunnelMime = MEDIA_MIMETYPE_AUDIO_QCELP;
- }
- else if ( mAudioEncoder == AUDIO_ENCODER_EVRC &&
- result.getInt(String8("EVRC"),value) == NO_ERROR ) {
- tunneledSource = true;
- tunnelMime = MEDIA_MIMETYPE_AUDIO_EVRC;
- }
- }
-
- if ( tunneledSource ) {
- sp<AudioSource> audioSource = NULL;
- sp<MetaData> meta = new MetaData;
- meta->setInt32(kKeyChannelCount, mAudioChannels);
- meta->setInt32(kKeySampleRate, mSampleRate);
- meta->setInt32(kKeyBitRate, mAudioBitRate);
- if (mAudioTimeScale > 0) {
- meta->setInt32(kKeyTimeScale, mAudioTimeScale);
- }
- meta->setCString( kKeyMIMEType, tunnelMime );
- audioSource = new AudioSource( mAudioSource, meta);
- return audioSource->initCheck( ) == OK ? audioSource : NULL;
- }
-
sp<AudioSource> audioSource =
new AudioSource(
mAudioSource,
mSampleRate,
mAudioChannels);
status_t err = audioSource->initCheck();
@@ -1226,56 +1186,33 @@ status_t GonkRecorder::setupVideoEncoder
default:
CHECK(!"Should not be here, unsupported video encoding.");
break;
}
sp<MetaData> meta = cameraSource->getFormat();
- int32_t width, height, stride, sliceHeight, colorFormat, hfr, is3D;
+ int32_t width, height, stride, sliceHeight, colorFormat;
CHECK(meta->findInt32(kKeyWidth, &width));
CHECK(meta->findInt32(kKeyHeight, &height));
CHECK(meta->findInt32(kKeyStride, &stride));
CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight));
CHECK(meta->findInt32(kKeyColorFormat, &colorFormat));
- CHECK(meta->findInt32(kKeyHFR, &hfr));
-
- if(hfr) {
- mMaxFileDurationUs = mMaxFileDurationUs * (hfr/mFrameRate);
- }
-
enc_meta->setInt32(kKeyWidth, width);
enc_meta->setInt32(kKeyHeight, height);
enc_meta->setInt32(kKeyIFramesInterval, mIFramesIntervalSec);
enc_meta->setInt32(kKeyStride, stride);
enc_meta->setInt32(kKeySliceHeight, sliceHeight);
enc_meta->setInt32(kKeyColorFormat, colorFormat);
- enc_meta->setInt32(kKeyHFR, hfr);
if (mVideoTimeScale > 0) {
enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale);
}
- char mDeviceName[100];
- property_get("ro.board.platform",mDeviceName,"0");
- if(!strncmp(mDeviceName, "msm7627a", 8)) {
- if(hfr && (width * height > 432*240)) {
- LOGE("HFR mode is supported only upto WQVGA resolution");
- return INVALID_OPERATION;
- }
- }
- else {
- if(hfr && ((mVideoEncoder != VIDEO_ENCODER_H264) || (width * height > 800*480))) {
- LOGE("HFR mode is supported only upto WVGA and H264 codec.");
- return INVALID_OPERATION;
- }
- }
-
-
/*
* can set profile from the app as a parameter.
* For the mean time, set from shell
*/
char value[PROPERTY_VALUE_MAX];
bool customProfile = false;
@@ -1322,19 +1259,16 @@ status_t GonkRecorder::setupVideoEncoder
}
if (mVideoEncoderProfile != -1) {
enc_meta->setInt32(kKeyVideoProfile, mVideoEncoderProfile);
}
if (mVideoEncoderLevel != -1) {
enc_meta->setInt32(kKeyVideoLevel, mVideoEncoderLevel);
}
- if (meta->findInt32(kKey3D, &is3D)) {
- enc_meta->setInt32(kKey3D, is3D);
- }
uint32_t encoder_flags = 0;
if (mIsMetaDataStoredInVideoBuffers) {
LOGW("Camera source supports metadata mode, create OMXCodec for metadata");
encoder_flags |= OMXCodec::kHardwareCodecsOnly;
encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers;
encoder_flags |= OMXCodec::kOnlySubmitOneInputBufferAtOneTime;
}

View File

@ -55,7 +55,8 @@ DeviceStorageRequestChild::Recv__delete__(const DeviceStorageResponseValue& aVal
BlobChild* actor = static_cast<BlobChild*>(r.blobChild());
nsCOMPtr<nsIDOMBlob> blob = actor->GetBlob();
jsval result = InterfaceToJsval(mRequest->GetOwner(), blob, &NS_GET_IID(nsIDOMBlob));
nsCOMPtr<nsIDOMFile> file = do_QueryInterface(blob);
jsval result = InterfaceToJsval(mRequest->GetOwner(), file, &NS_GET_IID(nsIDOMFile));
mRequest->FireSuccess(result);
break;
}

View File

@ -1913,6 +1913,17 @@ nsDOMDeviceStorage::Stat(nsIDOMDOMRequest** aRetval)
return NS_OK;
}
NS_IMETHODIMP
nsDOMDeviceStorage::GetRootDirectory(nsIFile** aRootDirectory)
{
if (!mRootDirectory) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIFile> file;
return mRootDirectory->Clone(aRootDirectory);
}
NS_IMETHODIMP
nsDOMDeviceStorage::Enumerate(const JS::Value & aName,
const JS::Value & aOptions,

View File

@ -255,6 +255,94 @@ dictionary MozStkSetUpEventList
jsval eventList; // unsigned short []
};
dictionary MozStkLocationInfo
{
/**
* Mobile Country Code (MCC) of the current serving operator.
*/
unsigned short mcc;
/**
* Mobile Network Code (MNC) of the current serving operator.
*/
unsigned short mnc;
/**
* Mobile Location Area Code (LAC) for the current serving operator.
*/
unsigned short gsmLocationAreaCode;
/**
* Mobile Cell ID for the current serving operator.
*/
unsigned long gsmCellId;
};
dictionary MozStkDuration
{
/**
* Time unit used, should be one of STK_TIME_UNIT_*.
*/
unsigned short timeUnit;
/**
* The length of time required, expressed in timeUnit.
*/
octet timeInterval;
};
dictionary MozStkPlayTone
{
/**
* Text String.
*/
DOMString text;
/**
* One of STK_TONE_TYPE_*.
*/
unsigned short tone;
/**
* The length of time for which the ME shall generate the tone.
*
* @see MozStkDuration
*/
jsval duration;
/**
* Need to vibrate or not.
* true: vibrate alert, if available, with the tone.
* false: use of vibrate alert is up to the ME.
*/
boolean isVibrate;
};
dictionary MozStkLocationEvent
{
/**
* The type of this event.
* It shall be nsIDOMMozIccManager.STK_EVENT_TYPE_LOCATION_STATUS;
*/
unsigned short eventType;
/**
* Indicate current service state of the MS with one of the values listed
* below:
* - nsIDOMMozIccManager.STK_SERVICE_STATE_NORMAL
* - nsIDOMMozIccManager.STK_SERVICE_STATE_LIMITED
* - nsIDOMMozIccManager.STK_SERVICE_STATE_UNAVAILABLE
*/
unsigned short locationStatus;
/**
* See MozStkLocationInfo.
* This value shall only be provided if the locationStatus indicates
* 'STK_SERVICE_STATE_NORMAL'.
*/
jsval locationInfo;
};
dictionary MozStkCommand
{
/**
@ -306,6 +394,22 @@ dictionary MozStkCommand
* When typeOfCommand is
* - STK_CMD_SET_UP_EVENT_LIST
* options is MozStkSetUpEventList.
*
* When typeOfCommand is
* - STK_CMD_PLAY_TONE
* options is MozStkPlayTone.
*
* When typeOfCommand is
* - STK_CMD_POLL_INTERVAL
* options is MozStkDuration.
*
* When typeOfCommand is
* - STK_CMD_POLL_OFF
* options is null.
*
* When typeOfCommand is
* - STK_CMD_REFRESH
* options is null.
*/
jsval options;
};

View File

@ -7,7 +7,7 @@
interface nsIDOMEventListener;
[scriptable, builtinclass, uuid(2eace3f9-6aa4-491b-820e-7d69ce7b3f02)]
[scriptable, builtinclass, uuid(9d898c66-3485-4cd5-ab8d-92ef2988887b)]
interface nsIDOMMozIccManager : nsIDOMEventTarget
{
/**
@ -30,6 +30,8 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget
* @see TS 11.14, clause 13.4
*/
const unsigned short STK_CMD_REFRESH = 0x01;
const unsigned short STK_CMD_POLL_INTERVAL = 0x03;
const unsigned short STK_CMD_POLL_OFF = 0x04;
const unsigned short STK_CMD_SET_UP_EVENT_LIST = 0x05;
const unsigned short STK_CMD_SET_UP_CALL = 0x10;
const unsigned short STK_CMD_SEND_SS = 0x11;
@ -37,6 +39,7 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget
const unsigned short STK_CMD_SEND_SMS = 0x13;
const unsigned short STK_CMD_SEND_DTMF = 0x14;
const unsigned short STK_CMD_LAUNCH_BROWSER = 0x15;
const unsigned short STK_CMD_PLAY_TONE = 0x20;
const unsigned short STK_CMD_DISPLAY_TEXT = 0x21;
const unsigned short STK_CMD_GET_INKEY = 0x22;
const unsigned short STK_CMD_GET_INPUT = 0x23;
@ -162,6 +165,35 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget
const unsigned short STK_EVENT_TYPE_BROWSING_STATUS = 0x0f;
const unsigned short STK_EVENT_TYPE_FRAMES_INFORMATION_CHANGED = 0x10;
/**
* The service state of STK Location Status.
*/
const unsigned short STK_SERVICE_STATE_NORMAL = 0x00;
const unsigned short STK_SERVICE_STATE_LIMITED = 0x01;
const unsigned short STK_SERVICE_STATE_UNAVAILABLE = 0x02;
/**
* Tone type.
*/
const unsigned short STK_TONE_TYPE_DIAL_TONE = 0x01;
const unsigned short STK_TONE_TYPE_CALLED_SUBSCRIBER_BUSY = 0x02;
const unsigned short STK_TONE_TYPE_CONGESTION = 0x03;
const unsigned short STK_TONE_TYPE_RADIO_PATH_ACK = 0x04;
const unsigned short STK_TONE_TYPE_RADIO_PATH_NOT_AVAILABLE = 0x05;
const unsigned short STK_TONE_TYPE_ERROR = 0x06;
const unsigned short STK_TONE_TYPE_CALL_WAITING_TONE = 0x07;
const unsigned short STK_TONE_TYPE_RINGING_TONE = 0x08;
const unsigned short STK_TONE_TYPE_GENERAL_BEEP = 0x10;
const unsigned short STK_TONE_TYPE_POSITIVE_ACK_TONE = 0x11;
const unsigned short STK_TONE_TYPE_NEGATIVE_ACK_TONE = 0x12;
/**
* Time unit
*/
const unsigned short STK_TIME_UNIT_MINUTE = 0x00;
const unsigned short STK_TIME_UNIT_SECOND = 0x01;
const unsigned short STK_TIME_UNIT_TENTH_SECOND = 0x02;
/**
* Send the response back to ICC after an attempt to execute STK Proactive
* Command.
@ -185,6 +217,16 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget
void sendStkMenuSelection(in unsigned short itemIdentifier,
in boolean helpRequested);
/**
* Send "Event Download" Envelope command to ICC.
* ICC will not respond with any data for this command.
*
* @param event
* one of events below:
* - MozStkLocationEvent
*/
void sendStkEventDownload(in jsval event);
/**
* The 'stkcommand' event is notified whenever STK Proactive Command is
* issued from ICC.

View File

@ -137,6 +137,17 @@ IccManager::SendStkMenuSelection(uint16_t aItemIdentifier, bool aHelpRequested)
return NS_OK;
}
NS_IMETHODIMP
IccManager::SendStkEventDownload(const JS::Value& aEvent)
{
if (!mProvider) {
return NS_ERROR_FAILURE;
}
mProvider->SendStkEventDownload(GetOwner(), aEvent);
return NS_OK;
}
nsresult
IccManager::InternalDispatchEvent(const nsAString& aType)
{

View File

@ -9,13 +9,14 @@ interface nsIDOMDOMRequest;
interface nsIDOMDeviceStorageCursor;
interface nsIDOMDeviceStorageChangeEvent;
interface nsIDOMEventListener;
interface nsIFile;
dictionary DeviceStorageEnumerationParameters
{
jsval since;
};
[scriptable, uuid(7efbe025-3a8a-4151-9257-3e8c941dc099), builtinclass]
[scriptable, uuid(7f69936f-2948-4733-ba41-c7e1d657a88b), builtinclass]
interface nsIDOMDeviceStorage : nsIDOMEventTarget
{
[implicit_jscontext] attribute jsval onchange;
@ -38,4 +39,6 @@ interface nsIDOMDeviceStorage : nsIDOMEventTarget
nsIDOMDeviceStorageCursor enumerateEditable([optional] in jsval aName, /* DeviceStorageEnumerationParameters */ [optional] in jsval options);
nsIDOMDOMRequest stat();
[noscript] readonly attribute nsIFile rootDirectory;
};

View File

@ -266,6 +266,12 @@ parent:
*/
ContentReceivedTouch(bool aPreventDefault);
/**
* Updates any zoom constraints on the parent and anything tied to it. This
* is useful for control logic that resides outside of the remote browser.
*/
UpdateZoomConstraints(bool aAllowZoom, float aMinZoom, float aMaxZoom);
__delete__();
child:
@ -282,9 +288,9 @@ child:
LoadURL(nsCString uri);
UpdateDimensions(nsRect rect, nsIntSize size);
UpdateDimensions(nsRect rect, nsIntSize size) compress;
UpdateFrame(FrameMetrics frame);
UpdateFrame(FrameMetrics frame) compress;
/**
* Requests handling of a double tap. |point| is in CSS pixels, relative to

View File

@ -18,6 +18,7 @@
#include "mozilla/dom/PContentChild.h"
#include "mozilla/dom/PContentDialogChild.h"
#include "mozilla/ipc/DocumentRendererChild.h"
#include "mozilla/layers/AsyncPanZoomController.h"
#include "mozilla/layers/CompositorChild.h"
#include "mozilla/layers/PLayersChild.h"
#include "mozilla/layout/RenderFrameChild.h"
@ -29,10 +30,12 @@
#include "nsContentUtils.h"
#include "nsEmbedCID.h"
#include "nsEventListenerManager.h"
#include "nsGenericElement.h"
#include "nsIAppsService.h"
#include "nsIBaseWindow.h"
#include "nsIComponentManager.h"
#include "nsIDOMClassInfo.h"
#include "nsIDOMElement.h"
#include "nsIDOMEvent.h"
#include "nsIDOMWindow.h"
#include "nsIDOMWindowUtils.h"
@ -50,6 +53,8 @@
#include "nsIServiceManager.h"
#include "nsISupportsImpl.h"
#include "nsIURI.h"
#include "nsIURIFixup.h"
#include "nsCDefaultURIFixup.h"
#include "nsIView.h"
#include "nsIWebBrowser.h"
#include "nsIWebBrowserFocus.h"
@ -85,6 +90,12 @@ using namespace mozilla::widget;
NS_IMPL_ISUPPORTS1(ContentListener, nsIDOMEventListener)
static const nsIntSize kDefaultViewportSize(980, 480);
static const char CANCEL_DEFAULT_PAN_ZOOM[] = "cancel-default-pan-zoom";
static const char BROWSER_ZOOM_TO_RECT[] = "browser-zoom-to-rect";
static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
NS_IMETHODIMP
ContentListener::HandleEvent(nsIDOMEvent* aEvent)
{
@ -151,28 +162,45 @@ TabChild::TabChild(uint32_t aChromeFlags, bool aIsBrowserElement,
, mTabChildGlobal(nullptr)
, mChromeFlags(aChromeFlags)
, mOuterRect(0, 0, 0, 0)
, mInnerSize(0, 0)
, mOldViewportWidth(0.0f)
, mLastBackgroundColor(NS_RGB(255, 255, 255))
, mAppId(aAppId)
, mDidFakeShow(false)
, mIsBrowserElement(aIsBrowserElement)
, mNotified(false)
, mContentDocumentIsDisplayed(false)
, mTriedBrowserInit(false)
{
printf("creating %d!\n", NS_IsMainThread());
}
nsresult
NS_IMETHODIMP
TabChild::HandleEvent(nsIDOMEvent* aEvent)
{
nsAutoString eventType;
aEvent->GetType(eventType);
if (eventType.EqualsLiteral("DOMMetaAdded")) {
// This meta data may or may not have been a meta viewport tag. If it was,
// we should handle it immediately.
HandlePossibleMetaViewportChange();
}
return NS_OK;
}
NS_IMETHODIMP
TabChild::Observe(nsISupports *aSubject,
const char *aTopic,
const PRUnichar *aData)
{
if (!strcmp(aTopic, "cancel-default-pan-zoom")) {
if (!strcmp(aTopic, CANCEL_DEFAULT_PAN_ZOOM)) {
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aSubject));
nsCOMPtr<nsITabChild> tabChild(GetTabChildFrom(docShell));
if (tabChild == this) {
mRemoteFrame->CancelDefaultPanZoom();
}
} else if (!strcmp(aTopic, "browser-zoom-to-rect")) {
} else if (!strcmp(aTopic, BROWSER_ZOOM_TO_RECT)) {
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aSubject));
nsCOMPtr<nsITabChild> tabChild(GetTabChildFrom(docShell));
if (tabChild == this) {
@ -182,11 +210,264 @@ TabChild::Observe(nsISupports *aSubject,
&rect.x, &rect.y, &rect.width, &rect.height);
SendZoomToRect(rect);
}
} else if (!strcmp(aTopic, BEFORE_FIRST_PAINT)) {
if (IsAsyncPanZoomEnabled()) {
nsCOMPtr<nsIDocument> subject(do_QueryInterface(aSubject));
nsCOMPtr<nsIDOMDocument> domDoc;
mWebNav->GetDocument(getter_AddRefs(domDoc));
nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
if (SameCOMIdentity(subject, doc)) {
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
mContentDocumentIsDisplayed = true;
// Reset CSS viewport and zoom to default on new page, then calculate them
// properly using the actual metadata from the page.
SetCSSViewport(kDefaultViewportSize.width, kDefaultViewportSize.height);
// Calculate a really simple resolution that we probably won't be
// keeping, as well as putting the scroll offset back to the top-left of
// the page.
float resolution = float(mInnerSize.width) / float(kDefaultViewportSize.width);
mLastMetrics.mZoom.width = mLastMetrics.mZoom.height =
mLastMetrics.mResolution.width = mLastMetrics.mResolution.height =
resolution;
mLastMetrics.mScrollOffset = gfx::Point(0, 0);
utils->SetResolution(resolution, resolution);
HandlePossibleMetaViewportChange();
}
}
}
return NS_OK;
}
NS_IMETHODIMP
TabChild::OnStateChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
uint32_t aStateFlags,
nsresult aStatus)
{
NS_NOTREACHED("not implemented in TabChild");
return NS_OK;
}
NS_IMETHODIMP
TabChild::OnProgressChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
int32_t aCurSelfProgress,
int32_t aMaxSelfProgress,
int32_t aCurTotalProgress,
int32_t aMaxTotalProgress)
{
NS_NOTREACHED("not implemented in TabChild");
return NS_OK;
}
NS_IMETHODIMP
TabChild::OnLocationChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
nsIURI *aLocation,
uint32_t aFlags)
{
if (!IsAsyncPanZoomEnabled()) {
return NS_OK;
}
nsCOMPtr<nsIDOMWindow> window;
aWebProgress->GetDOMWindow(getter_AddRefs(window));
if (!window) {
return NS_OK;
}
nsCOMPtr<nsIDOMDocument> progressDoc;
window->GetDocument(getter_AddRefs(progressDoc));
if (!progressDoc) {
return NS_OK;
}
nsCOMPtr<nsIDOMDocument> domDoc;
mWebNav->GetDocument(getter_AddRefs(domDoc));
if (!domDoc || !SameCOMIdentity(domDoc, progressDoc)) {
return NS_OK;
}
nsCOMPtr<nsIURIFixup> urifixup(do_GetService(NS_URIFIXUP_CONTRACTID));
if (!urifixup) {
return NS_OK;
}
nsCOMPtr<nsIURI> exposableURI;
urifixup->CreateExposableURI(aLocation, getter_AddRefs(exposableURI));
if (!exposableURI) {
return NS_OK;
}
if (!(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT)) {
mContentDocumentIsDisplayed = false;
} else if (mLastURI != nullptr) {
bool exposableEqualsLast, exposableEqualsNew;
exposableURI->Equals(mLastURI.get(), &exposableEqualsLast);
exposableURI->Equals(aLocation, &exposableEqualsNew);
if (exposableEqualsLast && !exposableEqualsNew) {
mContentDocumentIsDisplayed = false;
}
}
return NS_OK;
}
NS_IMETHODIMP
TabChild::OnStatusChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
nsresult aStatus,
const PRUnichar* aMessage)
{
NS_NOTREACHED("not implemented in TabChild");
return NS_OK;
}
NS_IMETHODIMP
TabChild::OnSecurityChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
uint32_t aState)
{
NS_NOTREACHED("not implemented in TabChild");
return NS_OK;
}
void
TabChild::SetCSSViewport(float aWidth, float aHeight)
{
mOldViewportWidth = aWidth;
if (mContentDocumentIsDisplayed) {
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
utils->SetCSSViewport(aWidth, aHeight);
}
}
void
TabChild::HandlePossibleMetaViewportChange()
{
if (!IsAsyncPanZoomEnabled()) {
return;
}
nsCOMPtr<nsIDOMDocument> domDoc;
mWebNav->GetDocument(getter_AddRefs(domDoc));
nsCOMPtr<nsIDocument> document(do_QueryInterface(domDoc));
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
ViewportInfo viewportMetaData =
nsContentUtils::GetViewportInfo(document, mInnerSize.width, mInnerSize.height);
SendUpdateZoomConstraints(viewportMetaData.allowZoom,
viewportMetaData.minZoom,
viewportMetaData.maxZoom);
float screenW = mInnerSize.width;
float screenH = mInnerSize.height;
float viewportW = viewportMetaData.width;
float viewportH = viewportMetaData.height;
// We're not being displayed in any way; don't bother doing anything because
// that will just confuse future adjustments.
if (!screenW || !screenH) {
return;
}
// Make sure the viewport height is not shorter than the window when the page
// is zoomed out to show its full width. Note that before we set the viewport
// width, the "full width" of the page isn't properly defined, so that's why
// we have to call SetCSSViewport twice - once to set the width, and the
// second time to figure out the height based on the layout at that width.
float oldBrowserWidth = mOldViewportWidth;
mLastMetrics.mViewport.width = viewportMetaData.width;
mLastMetrics.mViewport.height = viewportMetaData.height;
if (!oldBrowserWidth) {
oldBrowserWidth = kDefaultViewportSize.width;
}
SetCSSViewport(viewportW, viewportH);
// If this page has not been painted yet, then this must be getting run
// because a meta-viewport element was added (via the DOMMetaAdded handler).
// in this case, we should not do anything that forces a reflow (see bug
// 759678) such as requesting the page size or sending a viewport update. this
// code will get run again in the before-first-paint handler and that point we
// will run though all of it. the reason we even bother executing up to this
// point on the DOMMetaAdded handler is so that scripts that use
// window.innerWidth before they are painted have a correct value (bug
// 771575).
if (!mContentDocumentIsDisplayed) {
return;
}
float minScale = 1.0f;
nsCOMPtr<nsIDOMElement> htmlDOMElement = do_QueryInterface(document->GetHtmlElement());
nsCOMPtr<nsIDOMElement> bodyDOMElement = do_QueryInterface(document->GetBodyElement());
PRInt32 htmlWidth = 0, htmlHeight = 0;
if (htmlDOMElement) {
htmlDOMElement->GetScrollWidth(&htmlWidth);
htmlDOMElement->GetScrollHeight(&htmlHeight);
}
PRInt32 bodyWidth = 0, bodyHeight = 0;
if (bodyDOMElement) {
bodyDOMElement->GetScrollWidth(&bodyWidth);
bodyDOMElement->GetScrollHeight(&bodyHeight);
}
float pageWidth = NS_MAX(htmlWidth, bodyWidth);
float pageHeight = NS_MAX(htmlHeight, bodyHeight);
minScale = mInnerSize.width / pageWidth;
minScale = clamped((double)minScale, viewportMetaData.minZoom, viewportMetaData.maxZoom);
viewportH = NS_MAX(viewportH, screenH / minScale);
SetCSSViewport(viewportW, viewportH);
// This change to the zoom accounts for all types of changes I can conceive:
// 1. screen size changes, CSS viewport does not (pages with no meta viewport
// or a fixed size viewport)
// 2. screen size changes, CSS viewport also does (pages with a device-width
// viewport)
// 3. screen size remains constant, but CSS viewport changes (meta viewport
// tag is added or removed)
// 4. neither screen size nor CSS viewport changes
//
// In all of these cases, we maintain how much actual content is visible
// within the screen width. Note that "actual content" may be different with
// respect to CSS pixels because of the CSS viewport size changing.
int32_t oldScreenWidth = mLastMetrics.mCompositionBounds.width;
if (!oldScreenWidth) {
oldScreenWidth = mInnerSize.width;
}
float zoomScale = (screenW * oldBrowserWidth) / (oldScreenWidth * viewportW);
float zoom = clamped(double(mLastMetrics.mZoom.width * zoomScale),
viewportMetaData.minZoom, viewportMetaData.maxZoom);
utils->SetResolution(zoom, zoom);
FrameMetrics metrics(mLastMetrics);
metrics.mViewport = gfx::Rect(0.0f, 0.0f, viewportW, viewportH);
metrics.mScrollableRect = gfx::Rect(0.0f, 0.0f, pageWidth, pageHeight);
metrics.mCompositionBounds = nsIntRect(0, 0, mInnerSize.width, mInnerSize.height);
metrics.mZoom.width = metrics.mZoom.height =
metrics.mResolution.width = metrics.mResolution.height = zoom;
metrics.mDisplayPort = AsyncPanZoomController::CalculatePendingDisplayPort(
// The page must have been refreshed in some way such as a new document or
// new CSS viewport, so we know that there's no velocity, acceleration, and
// we have no idea how long painting will take.
metrics, gfx::Point(0.0f, 0.0f), gfx::Point(0.0f, 0.0f), 0.0);
// Force a repaint with these metrics. This, among other things, sets the
// displayport, so we start with async painting.
RecvUpdateFrame(metrics);
}
nsresult
TabChild::Init()
{
@ -238,6 +519,12 @@ TabChild::Init()
"DNS prefetching enable step.");
}
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(mWebNav);
MOZ_ASSERT(docShell);
nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
NS_ENSURE_TRUE(webProgress, NS_ERROR_FAILURE);
webProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_LOCATION);
return NS_OK;
}
@ -266,8 +553,11 @@ NS_INTERFACE_MAP_BEGIN(TabChild)
NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChromeFocus)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
NS_INTERFACE_MAP_ENTRY(nsITabChild)
NS_INTERFACE_MAP_ENTRY(nsIDialogCreator)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsSupportsWeakReference)
NS_INTERFACE_MAP_END
@ -799,6 +1089,7 @@ TabChild::RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size)
mOuterRect.width = rect.width;
mOuterRect.height = rect.height;
mInnerSize = size;
mWidget->Resize(0, 0, size.width, size.height,
true);
@ -806,6 +1097,8 @@ TabChild::RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size)
baseWin->SetPositionAndSize(0, 0, size.width, size.height,
true);
HandlePossibleMetaViewportChange();
return true;
}
@ -842,33 +1135,63 @@ TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
return true;
}
// The BrowserElementScrolling helper must know about these updated metrics
// for other functions it performs, such as double tap handling.
nsCString data;
data += nsPrintfCString("{ \"x\" : %d", NS_lround(aFrameMetrics.mViewportScrollOffset.x));
data += nsPrintfCString(", \"y\" : %d", NS_lround(aFrameMetrics.mViewportScrollOffset.y));
data += nsPrintfCString("{ \"x\" : %d", NS_lround(aFrameMetrics.mScrollOffset.x));
data += nsPrintfCString(", \"y\" : %d", NS_lround(aFrameMetrics.mScrollOffset.y));
// We don't treat the x and y scales any differently for this
// semi-platform-specific code.
data += nsPrintfCString(", \"zoom\" : %f", aFrameMetrics.mResolution.width);
data += nsPrintfCString(", \"zoom\" : %f", aFrameMetrics.mZoom.width);
data += nsPrintfCString(", \"displayPort\" : ");
data += nsPrintfCString("{ \"left\" : %d", aFrameMetrics.mDisplayPort.X());
data += nsPrintfCString(", \"top\" : %d", aFrameMetrics.mDisplayPort.Y());
data += nsPrintfCString(", \"width\" : %d", aFrameMetrics.mDisplayPort.Width());
data += nsPrintfCString(", \"height\" : %d", aFrameMetrics.mDisplayPort.Height());
data += nsPrintfCString("{ \"x\" : %f", aFrameMetrics.mDisplayPort.x);
data += nsPrintfCString(", \"y\" : %f", aFrameMetrics.mDisplayPort.y);
data += nsPrintfCString(", \"width\" : %f", aFrameMetrics.mDisplayPort.width);
data += nsPrintfCString(", \"height\" : %f", aFrameMetrics.mDisplayPort.height);
data += nsPrintfCString(", \"resolution\" : %f", aFrameMetrics.mResolution.width);
data += nsPrintfCString(" }");
data += nsPrintfCString(", \"screenSize\" : ");
data += nsPrintfCString("{ \"width\" : %d", aFrameMetrics.mViewport.width);
data += nsPrintfCString(", \"height\" : %d", aFrameMetrics.mViewport.height);
data += nsPrintfCString(", \"compositionBounds\" : ");
data += nsPrintfCString("{ \"x\" : %d", aFrameMetrics.mCompositionBounds.x);
data += nsPrintfCString(", \"y\" : %d", aFrameMetrics.mCompositionBounds.y);
data += nsPrintfCString(", \"width\" : %d", aFrameMetrics.mCompositionBounds.width);
data += nsPrintfCString(", \"height\" : %d", aFrameMetrics.mCompositionBounds.height);
data += nsPrintfCString(" }");
data += nsPrintfCString(", \"cssPageRect\" : ");
data += nsPrintfCString("{ \"x\" : %f", aFrameMetrics.mCSSContentRect.x);
data += nsPrintfCString(", \"y\" : %f", aFrameMetrics.mCSSContentRect.y);
data += nsPrintfCString(", \"width\" : %f", aFrameMetrics.mCSSContentRect.width);
data += nsPrintfCString(", \"height\" : %f", aFrameMetrics.mCSSContentRect.height);
data += nsPrintfCString("{ \"x\" : %f", aFrameMetrics.mScrollableRect.x);
data += nsPrintfCString(", \"y\" : %f", aFrameMetrics.mScrollableRect.y);
data += nsPrintfCString(", \"width\" : %f", aFrameMetrics.mScrollableRect.width);
data += nsPrintfCString(", \"height\" : %f", aFrameMetrics.mScrollableRect.height);
data += nsPrintfCString(" }");
data += nsPrintfCString(" }");
DispatchMessageManagerMessage(NS_LITERAL_STRING("Viewport:Change"), data);
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
nsCOMPtr<nsIDOMWindow> window = do_GetInterface(mWebNav);
utils->SetScrollPositionClampingScrollPortSize(
aFrameMetrics.mCompositionBounds.width / aFrameMetrics.mZoom.width,
aFrameMetrics.mCompositionBounds.height / aFrameMetrics.mZoom.width);
window->ScrollTo(aFrameMetrics.mScrollOffset.x,
aFrameMetrics.mScrollOffset.y);
utils->SetResolution(aFrameMetrics.mResolution.width,
aFrameMetrics.mResolution.width);
nsCOMPtr<nsIDOMDocument> domDoc;
nsCOMPtr<nsIDOMElement> docElement;
mWebNav->GetDocument(getter_AddRefs(domDoc));
if (domDoc) {
domDoc->GetDocumentElement(getter_AddRefs(docElement));
if (docElement) {
utils->SetDisplayPortForElement(
aFrameMetrics.mDisplayPort.x, aFrameMetrics.mDisplayPort.y,
aFrameMetrics.mDisplayPort.width, aFrameMetrics.mDisplayPort.height,
docElement);
}
}
mLastMetrics = aFrameMetrics;
return true;
}
@ -926,8 +1249,7 @@ TabChild::RecvMouseEvent(const nsString& aType,
const int32_t& aModifiers,
const bool& aIgnoreRootScrollFrame)
{
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mWebNav);
nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
NS_ENSURE_TRUE(utils, true);
utils->SendMouseEvent(aType, aX, aY, aButton, aClickCount, aModifiers,
aIgnoreRootScrollFrame, 0, 0);
@ -1028,8 +1350,7 @@ TabChild::RecvKeyEvent(const nsString& aType,
const int32_t& aModifiers,
const bool& aPreventDefault)
{
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mWebNav);
nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
NS_ENSURE_TRUE(utils, true);
bool ignored = false;
utils->SendKeyEvent(aType, aKeyCode, aCharCode,
@ -1279,6 +1600,13 @@ TabChild::RecvDestroy()
);
}
nsCOMPtr<nsIObserverService> observerService =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
observerService->RemoveObserver(this, CANCEL_DEFAULT_PAN_ZOOM);
observerService->RemoveObserver(this, BROWSER_ZOOM_TO_RECT);
observerService->RemoveObserver(this, BEFORE_FIRST_PAINT);
// XXX what other code in ~TabChild() should we be running here?
DestroyWindow();
@ -1325,6 +1653,8 @@ TabChild::InitTabChildGlobal(FrameScriptLoading aScriptLoading)
nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(chromeHandler);
NS_ENSURE_TRUE(root, false);
root->SetParentTarget(scope);
chromeHandler->AddEventListener(NS_LITERAL_STRING("DOMMetaAdded"), this, false);
}
if (aScriptLoading != DONT_LOAD_SCRIPTS && !mTriedBrowserInit) {
@ -1389,10 +1719,13 @@ TabChild::InitRenderingState()
if (observerService) {
observerService->AddObserver(this,
"cancel-default-pan-zoom",
CANCEL_DEFAULT_PAN_ZOOM,
false);
observerService->AddObserver(this,
"browser-zoom-to-rect",
BROWSER_ZOOM_TO_RECT,
false);
observerService->AddObserver(this,
BEFORE_FIRST_PAINT,
false);
}

View File

@ -36,9 +36,11 @@
#include "nsNetUtil.h"
#include "nsFrameMessageManager.h"
#include "nsIScriptContext.h"
#include "nsIWebProgressListener.h"
#include "nsDOMEventTargetHelper.h"
#include "nsIDialogCreator.h"
#include "nsIDialogParamBlock.h"
#include "nsIDOMWindowUtils.h"
#include "nsIPresShell.h"
#include "nsIPrincipal.h"
#include "nsIScriptObjectPrincipal.h"
@ -141,6 +143,8 @@ class TabChild : public PBrowserChild,
public nsIWebBrowserChromeFocus,
public nsIInterfaceRequestor,
public nsIWindowProvider,
public nsIDOMEventListener,
public nsIWebProgressListener,
public nsSupportsWeakReference,
public nsIDialogCreator,
public nsITabChild,
@ -175,6 +179,8 @@ public:
NS_DECL_NSIWEBBROWSERCHROMEFOCUS
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIWINDOWPROVIDER
NS_DECL_NSIDOMEVENTLISTENER
NS_DECL_NSIWEBPROGRESSLISTENER
NS_DECL_NSIDIALOGCREATOR
NS_DECL_NSITABCHILD
NS_DECL_NSIOBSERVER
@ -328,6 +334,16 @@ private:
// Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow().
void DoFakeShow();
// Wrapper for nsIDOMWindowUtils.setCSSViewport(). This updates some state
// variables local to this class before setting it.
void SetCSSViewport(float aX, float aY);
// Recalculates the display state, including the CSS viewport. This should
// be called whenever we believe the meta viewport data on a document may
// have changed. If it didn't change, this function doesn't do anything.
// However, it should not be called all the time as it is fairly expensive.
void HandlePossibleMetaViewportChange();
// Wraps up a JSON object as a structured clone and sends it to the browser
// chrome script.
//
@ -347,18 +363,30 @@ private:
bool* aWindowIsNew,
nsIDOMWindow** aReturn);
nsIDOMWindowUtils* GetDOMWindowUtils()
{
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mWebNav);
nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
return utils;
}
nsCOMPtr<nsIWebNavigation> mWebNav;
nsCOMPtr<nsIWidget> mWidget;
nsCOMPtr<nsIURI> mLastURI;
FrameMetrics mLastMetrics;
RenderFrameChild* mRemoteFrame;
nsRefPtr<TabChildGlobal> mTabChildGlobal;
uint32_t mChromeFlags;
nsIntRect mOuterRect;
nsIntSize mInnerSize;
float mOldViewportWidth;
nscolor mLastBackgroundColor;
ScrollingBehavior mScrolling;
uint32_t mAppId;
bool mDidFakeShow;
bool mIsBrowserElement;
bool mNotified;
bool mContentDocumentIsDisplayed;
bool mTriedBrowserInit;
DISALLOW_EVIL_CONSTRUCTORS(TabChild);

View File

@ -83,6 +83,7 @@ TabParent::TabParent(mozIApplication* aApp, bool aIsBrowserElement)
, mIMECompositionStart(0)
, mIMESeqno(0)
, mEventCaptureDepth(0)
, mDimensions(0, 0)
, mDPI(0)
, mIsBrowserElement(aIsBrowserElement)
, mShown(false)
@ -229,6 +230,7 @@ TabParent::Show(const nsIntSize& size)
{
// sigh
mShown = true;
mDimensions = size;
unused << SendShow(size);
}
@ -239,6 +241,7 @@ TabParent::UpdateDimensions(const nsRect& rect, const nsIntSize& size)
if (RenderFrameParent* rfp = GetRenderFrame()) {
rfp->NotifyDimensionsChanged(size.width, size.height);
}
mDimensions = size;
}
void
@ -1172,6 +1175,21 @@ TabParent::RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
return true;
}
bool
TabParent::RecvPRenderFrameConstructor(PRenderFrameParent* actor,
ScrollingBehavior* scrolling,
LayersBackend* backend,
int32_t* maxTextureSize,
uint64_t* layersId)
{
RenderFrameParent* rfp = GetRenderFrame();
if (mDimensions != nsIntSize() && rfp) {
rfp->NotifyDimensionsChanged(mDimensions.width, mDimensions.height);
}
return true;
}
bool
TabParent::RecvZoomToRect(const gfxRect& aRect)
{
@ -1181,6 +1199,17 @@ TabParent::RecvZoomToRect(const gfxRect& aRect)
return true;
}
bool
TabParent::RecvUpdateZoomConstraints(const bool& aAllowZoom,
const float& aMinZoom,
const float& aMaxZoom)
{
if (RenderFrameParent* rfp = GetRenderFrame()) {
rfp->UpdateZoomConstraints(aAllowZoom, aMinZoom, aMaxZoom);
}
return true;
}
bool
TabParent::RecvContentReceivedTouch(const bool& aPreventDefault)
{

View File

@ -94,6 +94,11 @@ public:
virtual bool RecvMoveFocus(const bool& aForward);
virtual bool RecvEvent(const RemoteDOMEvent& aEvent);
virtual bool RecvPRenderFrameConstructor(PRenderFrameParent* actor,
ScrollingBehavior* scrolling,
LayersBackend* backend,
int32_t* maxTextureSize,
uint64_t* layersId);
virtual bool RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
const nsString& aURL,
const nsString& aName,
@ -131,6 +136,9 @@ public:
virtual bool RecvGetDPI(float* aValue);
virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue);
virtual bool RecvZoomToRect(const gfxRect& aRect);
virtual bool RecvUpdateZoomConstraints(const bool& aAllowZoom,
const float& aMinZoom,
const float& aMaxZoom);
virtual bool RecvContentReceivedTouch(const bool& aPreventDefault);
virtual PContentDialogParent* AllocPContentDialog(const uint32_t& aType,
const nsCString& aName,
@ -269,6 +277,7 @@ protected:
// The number of event series we're currently capturing.
int32_t mEventCaptureDepth;
nsIntSize mDimensions;
float mDPI;
bool mIsBrowserElement;
bool mShown;

View File

@ -14,7 +14,7 @@ interface nsIDOMWindow;
* XPCOM component (in the content process) that provides the mobile
* network information.
*/
[scriptable, uuid(63787ba1-5091-450b-8810-d321a8b4f77a)]
[scriptable, uuid(bc1a82aa-2a1f-4a27-a5e7-614d06b72e0a)]
interface nsIMobileConnectionProvider : nsISupports
{
readonly attribute DOMString cardState;
@ -40,4 +40,6 @@ interface nsIMobileConnectionProvider : nsISupports
void sendStkMenuSelection(in nsIDOMWindow window,
in unsigned short itemIdentifier,
in boolean helpRequested);
void sendStkEventDownload(in nsIDOMWindow window,
in jsval event);
};

View File

@ -453,6 +453,15 @@ RILContentHelper.prototype = {
helpRequested: helpRequested});
},
sendStkEventDownload: function sendStkEventDownload(window,
event) {
if (window == null) {
throw Components.Exception("Can't get window object",
Cr.NS_ERROR_UNEXPECTED);
}
cpmm.sendAsyncMessage("RIL:SendStkEventDownload", {event: event});
},
_telephonyCallbacks: null,
_voicemailCallbacks: null,
_enumerateTelephonyCallbacks: null,

View File

@ -73,6 +73,7 @@ const RIL_IPC_MOBILECONNECTION_MSG_NAMES = [
"RIL:CancelUSSD",
"RIL:SendStkResponse",
"RIL:SendStkMenuSelection",
"RIL:SendStkEventDownload",
];
XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
@ -384,6 +385,9 @@ RadioInterfaceLayer.prototype = {
case "RIL:SendStkMenuSelection":
this.sendStkMenuSelection(msg.json);
break;
case "RIL:SendStkEventDownload":
this.sendStkEventDownload(msg.json);
break;
}
},
@ -1530,6 +1534,11 @@ RadioInterfaceLayer.prototype = {
this.worker.postMessage(message);
},
sendStkEventDownload: function sendStkEventDownload(message) {
message.rilMessageType = "sendStkEventDownload";
this.worker.postMessage(message);
},
get microphoneMuted() {
return gAudioManager.microphoneMuted;
},

View File

@ -537,11 +537,15 @@ const COMPREHENSIONTLV_TAG_ALPHA_ID = 0x05;
const COMPREHENSIONTLV_TAG_ADDRESS = 0x06;
const COMPREHENSIONTLV_TAG_SMS_TPDU = 0x0b;
const COMPREHENSIONTLV_TAG_TEXT_STRING = 0x0d;
const COMPREHENSIONTLV_TAG_TONE = 0x0e;
const COMPREHENSIONTLV_TAG_ITEM = 0x0f;
const COMPREHENSIONTLV_TAG_ITEM_ID = 0x10;
const COMPREHENSIONTLV_TAG_RESPONSE_LENGTH = 0x11;
const COMPREHENSIONTLV_TAG_FILE_LIST = 0x12;
const COMPREHENSIONTLV_TAG_LOCATION_INFO = 0x13;
const COMPREHENSIONTLV_TAG_HELP_REQUEST = 0x15;
const COMPREHENSIONTLV_TAG_DEFAULT_TEXT = 0x17;
const COMPREHENSIONTLV_TAG_LOCATION_STATUS = 0x1b;
const COMPREHENSIONTLV_TAG_EVENT_LIST = 0x19;
const COMPREHENSIONTLV_TAG_ICON_ID = 0x1e;
const COMPREHENSIONTLV_TAG_ICON_ID_LIST = 0x1f;
@ -558,6 +562,8 @@ const STK_DEVICE_ID_NETWORK = 0x83;
// STK Proactive commands.
const STK_CMD_REFRESH = 0x01;
const STK_CMD_POLL_INTERVAL = 0x03;
const STK_CMD_POLL_OFF = 0x04;
const STK_CMD_SET_UP_EVENT_LIST = 0x05;
const STK_CMD_SET_UP_CALL = 0x10;
const STK_CMD_SEND_SS = 0x11;
@ -565,6 +571,7 @@ const STK_CMD_SEND_USSD = 0x12;
const STK_CMD_SEND_SMS = 0x13;
const STK_CMD_SEND_DTMF = 0x14;
const STK_CMD_LAUNCH_BROWSER = 0x15;
const STK_CMD_PLAY_TONE = 0x20;
const STK_CMD_DISPLAY_TEXT = 0x21;
const STK_CMD_GET_INKEY = 0x22;
const STK_CMD_GET_INPUT = 0x23;
@ -725,6 +732,36 @@ const STK_EVENT_TYPE_LOCAL_CONNECTION = 0x0d;
const STK_EVENT_TYPE_NETWORK_SEARCH_MODE_CHANGED = 0x0e;
const STK_EVENT_TYPE_BROWSING_STATUS = 0x0f;
// STK Service state of Location Status.
const STK_SERVICE_STATE_NORMAL = 0x00;
const STK_SERVICE_STATE_LIMITED = 0x01;
const STK_SERVICE_STATE_UNAVAILABLE = 0x02;
// Refresh mode.
const STK_REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE = 0x00;
const STK_REFRESH_FILE_CHANGE = 0x01;
const STK_REFRESH_NAA_INIT_AND_FILE_CHANGE = 0x02;
const STK_REFRESH_NAA_INIT = 0x03;
const STK_REFRESH_UICC_RESET = 0x04;
// Tone type.
const STK_TONE_TYPE_DIAL_TONE = 0x01;
const STK_TONE_TYPE_CALLED_SUBSCRIBER_BUSY = 0x02;
const STK_TONE_TYPE_CONGESTION = 0x03;
const STK_TONE_TYPE_RADIO_PATH_ACK = 0x04;
const STK_TONE_TYPE_RADIO_PATH_NOT_AVAILABLE = 0x05;
const STK_TONE_TYPE_ERROR = 0x06;
const STK_TONE_TYPE_CALL_WAITING_TONE = 0x07;
const STK_TONE_TYPE_RINGING_TONE = 0x08;
const STK_TONE_TYPE_GENERAL_BEEP = 0x10;
const STK_TONE_TYPE_POSITIVE_ACK_TONE = 0x11;
const STK_TONE_TYPE_NEGATIVE_ACK_TONE = 0x12;
// Time unit.
const STK_TIME_UNIT_MINUTE = 0x00;
const STK_TIME_UNIT_SECOND = 0x01;
const STK_TIME_UNIT_TENTH_SECOND = 0x02;
/**
* (U)SIM Services.
*

View File

@ -51,6 +51,16 @@ const PARCEL_SIZE_SIZE = UINT32_SIZE;
const PDU_HEX_OCTET_SIZE = 4;
const TLV_COMMAND_DETAILS_SIZE = 5;
const TLV_DEVICE_ID_SIZE = 4;
const TLV_RESULT_SIZE = 3;
const TLV_ITEM_ID_SIZE = 3;
const TLV_HELP_REQUESTED_SIZE = 2;
const TLV_EVENT_LIST_SIZE = 3;
const TLV_LOCATION_STATUS_SIZE = 3;
const TLV_LOCATION_INFO_GSM_SIZE = 9;
const TLV_LOCATION_INFO_UMTS_SIZE = 11;
const DEFAULT_EMERGENCY_NUMBERS = ["112", "911"];
let RILQUIRKS_CALLSTATE_EXTRA_UINT32 = libcutils.property_get("ro.moz.ril.callstate_extra_int");
@ -2257,10 +2267,10 @@ let RIL = {
}
// 1 octets = 2 chars.
let size = (5 + /* Size of Command Details TLV */
4 + /* Size of Device Identifier TLV */
3 + /* Size of Result */
(response.itemIdentifier ? 3 : 0) +
let size = (TLV_COMMAND_DETAILS_SIZE +
TLV_DEVICE_ID_SIZE +
TLV_RESULT_SIZE +
(response.itemIdentifier ? TLV_ITEM_ID_SIZE : 0) +
(textLen ? textLen + 3 : 0)) * 2;
Buf.writeUint32(size);
@ -2365,6 +2375,29 @@ let RIL = {
this.sendICCEnvelopeCommand(command);
},
/**
* Send STK Envelope(Event Download) command.
* @param event
*/
sendStkEventDownload: function sendStkEventDownload(command) {
command.tag = BER_EVENT_DOWNLOAD_TAG;
command.eventList = command.event.eventType;
switch (command.eventList) {
case STK_EVENT_TYPE_LOCATION_STATUS:
command.deviceId = {
sourceId :STK_DEVICE_ID_ME,
destinationId: STK_DEVICE_ID_SIM
};
command.locationStatus = command.event.locationStatus;
// Location info should only be provided when locationStatus is normal.
if (command.locationStatus == STK_SERVICE_STATE_NORMAL) {
command.locationInfo = command.event.locationInfo;
}
break;
}
this.sendICCEnvelopeCommand(command);
},
/**
* Send REQUEST_STK_SEND_ENVELOPE_COMMAND to ICC.
*
@ -2372,12 +2405,25 @@ let RIL = {
* @patam deviceId
* @param [optioanl] itemIdentifier
* @param [optional] helpRequested
* @param [optional] eventList
* @param [optional] locationStatus
* @param [optional] locationInfo
*/
sendICCEnvelopeCommand: function sendICCEnvelopeCommand(options) {
if (DEBUG) {
debug("Stk Envelope " + JSON.stringify(options));
}
let token = Buf.newParcel(REQUEST_STK_SEND_ENVELOPE_COMMAND);
let berLen = 4 + /* Size of Device Identifier TLV */
(options.itemIdentifier ? 3 : 0) +
(options.helpRequested ? 2 : 0);
let berLen = TLV_DEVICE_ID_SIZE + /* Size of Device Identifier TLV */
(options.itemIdentifier ? TLV_ITEM_ID_SIZE : 0) +
(options.helpRequested ? TLV_HELP_REQUESTED_SIZE : 0) +
(options.eventList ? TLV_EVENT_LIST_SIZE : 0) +
(options.locationStatus ? TLV_LOCATION_STATUS_SIZE : 0) +
(options.locationInfo ?
(options.locationInfo.gsmCellId > 0xffff ?
TLV_LOCATION_INFO_UMTS_SIZE :
TLV_LOCATION_INFO_GSM_SIZE) :
0);
let size = (2 + berLen) * 2;
Buf.writeUint32(size);
@ -2409,6 +2455,28 @@ let RIL = {
// Help Request doesn't have value
}
// Event List
if (options.eventList) {
GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_EVENT_LIST |
COMPREHENSIONTLV_FLAG_CR);
GsmPDUHelper.writeHexOctet(1);
GsmPDUHelper.writeHexOctet(options.eventList);
}
// Location Status
if (options.locationStatus) {
let len = options.locationStatus.length;
GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_LOCATION_STATUS |
COMPREHENSIONTLV_FLAG_CR);
GsmPDUHelper.writeHexOctet(1);
GsmPDUHelper.writeHexOctet(options.locationStatus);
}
// Location Info
if (options.locationInfo) {
ComprehensionTlvHelper.writeLocationInfoTlv(options.locationInfo);
}
Buf.writeUint32(0);
Buf.sendParcel();
},
@ -6006,6 +6074,15 @@ let StkCommandParamsFactory = {
createParam: function createParam(cmdDetails, ctlvs) {
let param;
switch (cmdDetails.typeOfCommand) {
case STK_CMD_REFRESH:
param = this.processRefresh(cmdDetails, ctlvs);
break;
case STK_CMD_POLL_INTERVAL:
param = this.processPollInterval(cmdDetails, ctlvs);
break;
case STK_CMD_POLL_OFF:
param = this.processPollOff(cmdDetails, ctlvs);
break;
case STK_CMD_SET_UP_EVENT_LIST:
param = this.processSetUpEventList(cmdDetails, ctlvs);
break;
@ -6034,9 +6111,12 @@ let StkCommandParamsFactory = {
case STK_CMD_SET_UP_CALL:
param = this.processSetupCall(cmdDetails, ctlvs);
break;
case STK_LAUNCH_BROWSER:
case STK_CMD_LAUNCH_BROWSER:
param = this.processLaunchBrowser(cmdDetails, ctlvs);
break;
case STK_CMD_PLAY_TONE:
param = this.processPlayTone(cmdDetails, ctlvs);
break;
default:
debug("unknown proactive command");
break;
@ -6044,6 +6124,66 @@ let StkCommandParamsFactory = {
return param;
},
/**
* Construct a param for Refresh.
*
* @param cmdDetails
* The value object of CommandDetails TLV.
* @param ctlvs
* The all TLVs in this proactive command.
*/
processRefresh: function processRefresh(cmdDetails, ctlvs) {
let refreshType = cmdDetails.commandQualifier;
switch (refreshType) {
case STK_REFRESH_FILE_CHANGE:
case STK_REFRESH_NAA_INIT_AND_FILE_CHANGE:
let ctlv = StkProactiveCmdHelper.searchForTag(
COMPREHENSIONTLV_FILE_LIST, ctlvs);
if (ctlv) {
let list = ctlv.value.fileList;
if (DEBUG) {
debug("Refresh, list = " + list);
}
RIL.fetchICCRecords();
}
break;
}
return {};
},
/**
* Construct a param for Poll Interval.
*
* @param cmdDetails
* The value object of CommandDetails TLV.
* @param ctlvs
* The all TLVs in this proactive command.
*/
processPollInterval: function processPollInterval(cmdDetails, ctlvs) {
let ctlv = StkProactiveCmdHelper.searchForTag(
COMPREHENSIONTLV_TAG_DURATION, ctlvs);
if (!ctlv) {
RIL.sendStkTerminalResponse({
command: cmdDetails,
resultCode: STK_RESULT_REQUIRED_VALUES_MISSING});
throw new Error("Stk Poll Interval: Required value missing : Duration");
}
return ctlv.value;
},
/**
* Construct a param for Poll Off.
*
* @param cmdDetails
* The value object of CommandDetails TLV.
* @param ctlvs
* The all TLVs in this proactive command.
*/
processPollOff: function processPollOff(cmdDetails, ctlvs) {
return {};
},
/**
* Construct a param for Set Up Event list.
*
@ -6309,6 +6449,32 @@ let StkCommandParamsFactory = {
browser.mode = cmdDetails.commandQualifier & 0x03;
return browser;
},
processPlayTone: function processPlayTone(cmdDetails, ctlvs) {
let playTone = {};
let ctlv = StkProactiveCmdHelper.searchForTag(
COMPREHENSIONTLV_TAG_ALPHA_ID, ctlvs);
if (ctlv) {
playTone.text = ctlv.value.identifier;
}
ctlv = StkProactiveCmdHelper.searchForTag(COMPREHENSIONTLV_TAG_TONE, ctlvs);
if (ctlv) {
playTone.tone = ctlv.value.tone;
}
ctlv = StkProactiveCmdHelper.searchForTag(
COMPREHENSIONTLV_TAG_DURATION, ctlvs);
if (ctlv) {
playTone.duration = ctlv.value;
}
// vibrate is only defined in TS 102.223
playTone.isVibrate = (cmdDetails.commandQualifier & 0x01) != 0x00;
return playTone;
}
};
@ -6321,16 +6487,22 @@ let StkProactiveCmdHelper = {
return this.retrieveDeviceId(length);
case COMPREHENSIONTLV_TAG_ALPHA_ID:
return this.retrieveAlphaId(length);
case COMPREHENSIONTLV_TAG_DURATION:
return this.retrieveDuration(length);
case COMPREHENSIONTLV_TAG_ADDRESS:
return this.retrieveAddress(length);
case COMPREHENSIONTLV_TAG_TEXT_STRING:
return this.retrieveTextString(length);
case COMPREHENSIONTLV_TAG_TONE:
return this.retrieveTone(length);
case COMPREHENSIONTLV_TAG_ITEM:
return this.retrieveItem(length);
case COMPREHENSIONTLV_TAG_ITEM_ID:
return this.retrieveItemId(length);
case COMPREHENSIONTLV_TAG_RESPONSE_LENGTH:
return this.retrieveResponseLength(length);
case COMPREHENSIONTLV_TAG_FILE_LIST:
return this.retrieveFileList(length);
case COMPREHENSIONTLV_TAG_DEFAULT_TEXT:
return this.retrieveDefaultText(length);
case COMPREHENSIONTLV_TAG_EVENT_LIST:
@ -6398,6 +6570,23 @@ let StkProactiveCmdHelper = {
return alphaId;
},
/**
* Duration.
*
* | Byte | Description | Length |
* | 1 | Response Length Tag | 1 |
* | 2 | Lenth = 02 | 1 |
* | 3 | Time unit | 1 |
* | 4 | Time interval | 1 |
*/
retrieveDuration: function retrieveDuration(length) {
let duration = {
timeUnit: GsmPDUHelper.readHexOctet(),
timeInterval: GsmPDUHelper.readHexOctet(),
};
return duration;
},
/**
* Address.
*
@ -6450,6 +6639,21 @@ let StkProactiveCmdHelper = {
return text;
},
/**
* Tone.
*
* | Byte | Description | Length |
* | 1 | Tone Tag | 1 |
* | 2 | Lenth = 01 | 1 |
* | 3 | Tone | 1 |
*/
retrieveTone: function retrieveTone(length) {
let tone = {
tone: GsmPDUHelper.readHexOctet(),
};
return tone;
},
/**
* Item.
*
@ -6500,6 +6704,30 @@ let StkProactiveCmdHelper = {
return rspLength;
},
/**
* File List.
*
* | Byte | Description | Length |
* | 1 | File List Tag | 1 |
* | 2 ~ (Y-1)+2 | Length (X) | Y |
* | (Y-1)+3 | Number of files | 1 |
* | (Y-1)+4 ~ | Files | X |
* | (Y-1)+X+2 | | |
*/
retrieveFileList: function retrieveFileList(length) {
let num = GsmPDUHelper.readHexOctet();
let fileList = "";
length--; // -1 for the num octet.
for (let i = 0; i < 2 * length; i++) {
// Didn't use readHexOctet here,
// otherwise 0x00 will be "0", not "00"
fileList += String.fromCharCode(Buf.readUint16());
}
return {
fileList: fileList
};
},
/**
* Default Text.
*
@ -6669,7 +6897,68 @@ let ComprehensionTlvHelper = {
index += tlv.hlen;
}
return chunks;
},
/**
* Write Location Info Comprehension TLV.
*
* @param loc location Information.
*/
writeLocationInfoTlv: function writeLocationInfoTlv(loc) {
GsmPDUHelper.writeHexOctet(COMPREHENSIONTLV_TAG_LOCATION_INFO |
COMPREHENSIONTLV_FLAG_CR);
GsmPDUHelper.writeHexOctet(loc.gsmCellId > 0xffff ? 9 : 7);
// From TS 11.14, clause 12.19
// "The mobile country code (MCC), the mobile network code (MNC),
// the location area code (LAC) and the cell ID are
// coded as in TS 04.08."
// And from TS 04.08 and TS 24.008,
// the format is as follows:
//
// MCC = MCC_digit_1 + MCC_digit_2 + MCC_digit_3
//
// 8 7 6 5 4 3 2 1
// +-------------+-------------+
// | MCC digit 2 | MCC digit 1 | octet 2
// | MNC digit 3 | MCC digit 3 | octet 3
// | MNC digit 2 | MNC digit 1 | octet 4
// +-------------+-------------+
//
// Also in TS 24.008
// "However a network operator may decide to
// use only two digits in the MNC in the LAI over the
// radio interface. In this case, bits 5 to 8 of octet 3
// shall be coded as '1111'".
// MCC & MNC, 3 octets
let mcc = loc.mcc.toString();
let mnc = loc.mnc.toString();
if (mnc.length == 1) {
mnc = "F0" + mnc;
} else if (mnc.length == 2) {
mnc = "F" + mnc;
} else {
mnc = mnc[2] + mnc[0] + mnc[1];
}
GsmPDUHelper.writeSwappedNibbleBCD(mcc + mnc);
// LAC, 2 octets
GsmPDUHelper.writeHexOctet((loc.gsmLocationAreaCode >> 8) & 0xff);
GsmPDUHelper.writeHexOctet(loc.gsmLocationAreaCode & 0xff);
// Cell Id
if (loc.gsmCellId > 0xffff) {
// UMTS/WCDMA, gsmCellId is 28 bits.
GsmPDUHelper.writeHexOctet((loc.gsmCellId >> 24) & 0xff);
GsmPDUHelper.writeHexOctet((loc.gsmCellId >> 16) & 0xff);
GsmPDUHelper.writeHexOctet((loc.gsmCellId >> 8) & 0xff);
GsmPDUHelper.writeHexOctet(loc.gsmCellId & 0xff);
} else {
// GSM, gsmCellId is 16 bits.
GsmPDUHelper.writeHexOctet((loc.gsmCellId >> 8) & 0xff);
GsmPDUHelper.writeHexOctet(loc.gsmCellId & 0xff);
}
},
};
let BerTlvHelper = {

View File

@ -142,3 +142,202 @@ add_test(function test_write_dialling_number() {
run_next_test();
});
/**
* Verify ComprehensionTlvHelper.writeLocationInfoTlv
*/
add_test(function test_write_location_info_tlv() {
let worker = newUint8Worker();
let pduHelper = worker.GsmPDUHelper;
let tlvHelper = worker.ComprehensionTlvHelper;
// Test with 2-digit mnc, and gsmCellId obtained from UMTS network.
let loc = {
mcc: 466,
mnc: 92,
gsmLocationAreaCode : 10291,
gsmCellId: 19072823
};
tlvHelper.writeLocationInfoTlv(loc);
let tag = pduHelper.readHexOctet();
do_check_eq(tag, COMPREHENSIONTLV_TAG_LOCATION_INFO |
COMPREHENSIONTLV_FLAG_CR);
let length = pduHelper.readHexOctet();
do_check_eq(length, 9);
let mcc_mnc = pduHelper.readSwappedNibbleBcdString(3);
do_check_eq(mcc_mnc, "46692");
let lac = (pduHelper.readHexOctet() << 8) | pduHelper.readHexOctet();
do_check_eq(lac, 10291);
let cellId = (pduHelper.readHexOctet() << 24) |
(pduHelper.readHexOctet() << 16) |
(pduHelper.readHexOctet() << 8) |
(pduHelper.readHexOctet());
do_check_eq(cellId, 19072823);
// Test with 1-digit mnc, and gsmCellId obtained from GSM network.
loc = {
mcc: 466,
mnc: 2,
gsmLocationAreaCode : 10291,
gsmCellId: 65534
};
tlvHelper.writeLocationInfoTlv(loc);
tag = pduHelper.readHexOctet();
do_check_eq(tag, COMPREHENSIONTLV_TAG_LOCATION_INFO |
COMPREHENSIONTLV_FLAG_CR);
length = pduHelper.readHexOctet();
do_check_eq(length, 7);
mcc_mnc = pduHelper.readSwappedNibbleBcdString(3);
do_check_eq(mcc_mnc, "46602");
lac = (pduHelper.readHexOctet() << 8) | pduHelper.readHexOctet();
do_check_eq(lac, 10291);
cellId = (pduHelper.readHexOctet() << 8) |
(pduHelper.readHexOctet());
do_check_eq(cellId, 65534);
// Test with 3-digit mnc, and gsmCellId obtained from GSM network.
loc = {
mcc: 466,
mnc: 222,
gsmLocationAreaCode : 10291,
gsmCellId: 65534
};
tlvHelper.writeLocationInfoTlv(loc);
tag = pduHelper.readHexOctet();
do_check_eq(tag, COMPREHENSIONTLV_TAG_LOCATION_INFO |
COMPREHENSIONTLV_FLAG_CR);
length = pduHelper.readHexOctet();
do_check_eq(length, 7);
mcc_mnc = pduHelper.readSwappedNibbleBcdString(3);
do_check_eq(mcc_mnc, "466222");
lac = (pduHelper.readHexOctet() << 8) | pduHelper.readHexOctet();
do_check_eq(lac, 10291);
cellId = (pduHelper.readHexOctet() << 8) |
(pduHelper.readHexOctet());
do_check_eq(cellId, 65534);
run_next_test();
});
/**
* Verify Proactive Command : Refresh
*/
add_test(function test_stk_proactive_command_refresh() {
let worker = newUint8Worker();
let pduHelper = worker.GsmPDUHelper;
let berHelper = worker.BerTlvHelper;
let stkHelper = worker.StkProactiveCmdHelper;
let refresh_1 = [
0xD0,
0x10,
0x81, 0x03, 0x01, 0x01, 0x01,
0x82, 0x02, 0x81, 0x82,
0x92, 0x05, 0x01, 0x3F, 0x00, 0x2F, 0xE2];
for (let i = 0; i < refresh_1.length; i++) {
pduHelper.writeHexOctet(refresh_1[i]);
}
let berTlv = berHelper.decode(refresh_1.length);
let ctlvs = berTlv.value;
let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
do_check_eq(tlv.value.commandNumber, 0x01);
do_check_eq(tlv.value.typeOfCommand, 0x01);
do_check_eq(tlv.value.commandQualifier, 0x01);
tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_FILE_LIST, ctlvs);
do_check_eq(tlv.value.fileList, "3F002FE2");
run_next_test();
});
/**
* Verify Proactive Command : Play Tone
*/
add_test(function test_stk_proactive_command_play_tone() {
let worker = newUint8Worker();
let pduHelper = worker.GsmPDUHelper;
let berHelper = worker.BerTlvHelper;
let stkHelper = worker.StkProactiveCmdHelper;
let tone_1 = [
0xD0,
0x1B,
0x81, 0x03, 0x01, 0x20, 0x00,
0x82, 0x02, 0x81, 0x03,
0x85, 0x09, 0x44, 0x69, 0x61, 0x6C, 0x20, 0x54, 0x6F, 0x6E, 0x65,
0x8E, 0x01, 0x01,
0x84, 0x02, 0x01, 0x05];
for (let i = 0; i < tone_1.length; i++) {
pduHelper.writeHexOctet(tone_1[i]);
}
let berTlv = berHelper.decode(tone_1.length);
let ctlvs = berTlv.value;
let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
do_check_eq(tlv.value.commandNumber, 0x01);
do_check_eq(tlv.value.typeOfCommand, 0x20);
do_check_eq(tlv.value.commandQualifier, 0x00);
tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_ALPHA_ID, ctlvs);
do_check_eq(tlv.value.identifier, "Dial Tone");
tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_TONE, ctlvs);
do_check_eq(tlv.value.tone, STK_TONE_TYPE_DIAL_TONE);
tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_DURATION, ctlvs);
do_check_eq(tlv.value.timeUnit, STK_TIME_UNIT_SECOND);
do_check_eq(tlv.value.timeInterval, 5);
run_next_test();
});
/**
* Verify Proactive Command : Poll Interval
*/
add_test(function test_stk_proactive_command_poll_interval() {
let worker = newUint8Worker();
let pduHelper = worker.GsmPDUHelper;
let berHelper = worker.BerTlvHelper;
let stkHelper = worker.StkProactiveCmdHelper;
let poll_1 = [
0xD0,
0x0D,
0x81, 0x03, 0x01, 0x03, 0x00,
0x82, 0x02, 0x81, 0x82,
0x84, 0x02, 0x01, 0x14];
for (let i = 0; i < poll_1.length; i++) {
pduHelper.writeHexOctet(poll_1[i]);
}
let berTlv = berHelper.decode(poll_1.length);
let ctlvs = berTlv.value;
let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
do_check_eq(tlv.value.commandNumber, 0x01);
do_check_eq(tlv.value.typeOfCommand, 0x03);
do_check_eq(tlv.value.commandQualifier, 0x00);
tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_DURATION, ctlvs);
do_check_eq(tlv.value.timeUnit, STK_TIME_UNIT_SECOND);
do_check_eq(tlv.value.timeInterval, 0x14);
run_next_test();
});

View File

@ -403,6 +403,21 @@ struct BaseRect {
width = right - x;
height = bottom - y;
}
// Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
// the largest integer-coordinate rectangle contained by the unrounded result.
void ScaleInverseRoundIn(double aScale) { ScaleInverseRoundIn(aScale, aScale); }
// Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
// that the result is the largest integer-coordinate rectangle contained by the
// unrounded result.
void ScaleInverseRoundIn(double aXScale, double aYScale)
{
T right = static_cast<T>(floor(double(XMost()) / aXScale));
T bottom = static_cast<T>(floor(double(YMost()) / aYScale));
x = static_cast<T>(ceil(double(x) / aXScale));
y = static_cast<T>(ceil(double(y) / aYScale));
width = gfx_max<T>(0, right - x);
height = gfx_max<T>(0, bottom - y);
}
/**
* Clamp aPoint to this rectangle. It is allowed to end up on any

View File

@ -30,12 +30,15 @@ public:
// will begin at.
FrameMetrics()
: mViewport(0, 0, 0, 0)
: mCompositionBounds(0, 0, 0, 0)
, mContentRect(0, 0, 0, 0)
, mViewportScrollOffset(0, 0)
, mDisplayPort(0, 0, 0, 0)
, mViewport(0, 0, 0, 0)
, mScrollOffset(0, 0)
, mScrollId(NULL_SCROLL_ID)
, mCSSContentRect(0, 0, 0, 0)
, mScrollableRect(0, 0, 0, 0)
, mResolution(1, 1)
, mDevPixelsPerCSSPixel(1)
, mMayHaveTouchListeners(false)
{}
@ -44,7 +47,7 @@ public:
bool operator==(const FrameMetrics& aOther) const
{
return (mViewport.IsEqualEdges(aOther.mViewport) &&
mViewportScrollOffset == aOther.mViewportScrollOffset &&
mScrollOffset == aOther.mScrollOffset &&
mDisplayPort.IsEqualEdges(aOther.mDisplayPort) &&
mScrollId == aOther.mScrollId);
}
@ -68,21 +71,152 @@ public:
return mScrollId != NULL_SCROLL_ID;
}
// These are all in layer coordinate space.
nsIntRect mViewport;
gfxSize LayersPixelsPerCSSPixel() const
{
return mResolution * mDevPixelsPerCSSPixel;
}
gfx::Point GetScrollOffsetInLayerPixels() const
{
return gfx::Point(mScrollOffset.x * LayersPixelsPerCSSPixel().width,
mScrollOffset.y * LayersPixelsPerCSSPixel().height);
}
// ---------------------------------------------------------------------------
// The following metrics are all in widget space/device pixels.
//
// This is the area within the widget that we're compositing to, which means
// that it is the visible region of this frame. It is not relative to
// anything.
// So { 0, 0, [compositeArea.width], [compositeArea.height] }.
//
// This is useful because, on mobile, the viewport and composition dimensions
// are not always the same. In this case, we calculate the displayport using
// an area bigger than the region we're compositing to. If we used the
// viewport dimensions to calculate the displayport, we'd run into situations
// where we're prerendering the wrong regions and the content may be clipped,
// or too much of it prerendered. If the displayport is the same as the
// viewport, there is no need for this and we can just use the viewport
// instead.
//
// This is only valid on the root layer. Nested iframes do not need this
// metric as they do not have a displayport set. See bug 775452.
nsIntRect mCompositionBounds;
// |mScrollableRect|, stored in device pixels. DECPRECATED, DO NOT USE.
//
// This is valid on any layer where |mScrollableRect| is, though it may be
// more lazily maintained than |mScrollableRect|. That is, when
// |mScrollableRect| is updated, this may lag. For this reason, it's better to
// use |mScrollableRect| for any control logic.
//
// FIXME/bug 785929: Is this really necessary? Can it not be calculated from
// |mScrollableRect| whenever it's needed?
nsIntRect mContentRect;
gfx::Point mViewportScrollOffset;
nsIntRect mDisplayPort;
// ---------------------------------------------------------------------------
// The following metrics are all in CSS pixels. They are not in any uniform
// space, so each is explained separately.
//
// The area of a frame's contents that has been painted, relative to the
// viewport. It is in the same coordinate space as |mViewport|. For example,
// if it is at 0,0, then it's at the same place at the viewport, which is at
// the top-left in the layer, and at the same place as the scroll offset of
// the document.
//
// Note that this is structured in such a way that it doesn't depend on the
// method layout uses to scroll content.
//
// May be larger or smaller than |mScrollableRect|.
//
// To pre-render a margin of 100 CSS pixels around the window,
// { x = -100, y = - 100,
// width = window.innerWidth + 100, height = window.innerHeight + 100 }
//
// This is only valid on the root layer. Nested iframes do not have a
// displayport set on them. See bug 775452.
gfx::Rect mDisplayPort;
// The CSS viewport, which is the dimensions we're using to constrain the
// <html> element of this frame, relative to the top-left of the layer. Note
// that its offset is structured in such a way that it doesn't depend on the
// method layout uses to scroll content.
//
// This is mainly useful on the root layer, however nested iframes can have
// their own viewport, which will just be the size of the window of the
// iframe. For layers that don't correspond to a document, this metric is
// meaningless and invalid.
gfx::Rect mViewport;
// The position of the top-left of the CSS viewport, relative to the document
// (or the document relative to the viewport, if that helps understand it).
//
// Thus it is relative to the document. It is in the same coordinate space as
// |mScrollableRect|, but a different coordinate space than |mViewport| and
// |mDisplayPort|.
//
// It is required that the rect:
// { x = mScrollOffset.x, y = mScrollOffset.y,
// width = mCompositionBounds.x / mResolution.width,
// height = mCompositionBounds.y / mResolution.height }
// Be within |mScrollableRect|.
//
// This is valid for any layer, but is always relative to this frame and
// not any parents, regardless of parent transforms.
gfx::Point mScrollOffset;
// A unique ID assigned to each scrollable frame (unless this is
// ROOT_SCROLL_ID, in which case it is not unique).
ViewID mScrollId;
// Consumers often want to know the origin/size before scaling to pixels
// so we record this as well.
gfx::Rect mCSSContentRect;
// The scrollable bounds of a frame. This is determined by reflow.
// For the top-level |window|,
// { x = window.scrollX, y = window.scrollY, // could be 0, 0
// width = window.innerWidth, height = window.innerHeight }
//
// This is relative to the document. It is in the same coordinate space as
// |mScrollOffset|, but a different coordinate space than |mViewport| and
// |mDisplayPort|. Note also that this coordinate system is understood by
// window.scrollTo().
//
// This is valid on any layer unless it has no content.
gfx::Rect mScrollableRect;
// This represents the resolution at which the associated layer
// will been rendered.
// ---------------------------------------------------------------------------
// The following metrics are dimensionless.
//
// The resolution, along both axes, that the current frame has been painted
// at.
//
// Every time this frame is composited and the compositor samples its
// transform, this metric is used to create a transform which is
// post-multiplied into the parent's transform. Since this only happens when
// we walk the layer tree, the resulting transform isn't stored here. Thus the
// resolution of parent layers is opaque to this metric.
gfxSize mResolution;
// The amount we are currently zooming the frame. This is distinct from
// |mResolution| as we can paint a frame at a different resolution than we
// zoom it at. This is useful in situations where we want to zoom a frame
// without forcing a repaint. At steady state, this and |mResolution| will be
// equal.
//
// Every time this frame is composited and the compositor samples its
// transform, this metric is used to create a transform which is
// post-multiplied into the parent's transform. Since this only happens when
// we walk the layer tree, the resulting transform isn't stored here. Thus the
// zoom of parent layers is opaque to this metric.
gfxSize mZoom;
// The conversion factor between CSS pixels and device pixels for this frame.
// This can vary based on a variety of things, such as reflowing-zoom. The
// conversion factor for device pixels to layers pixels is just the
// resolution.
float mDevPixelsPerCSSPixel;
// Whether or not this frame may have touch listeners.
bool mMayHaveTouchListeners;
};

View File

@ -143,6 +143,17 @@ AppendToString(nsACString& s, const nsIntRect& r,
return s += sfx;
}
nsACString&
AppendToString(nsACString& s, const Rect& r,
const char* pfx="", const char* sfx="")
{
s += pfx;
s.AppendPrintf(
"(x=%f, y=%f, w=%f, h=%f)",
r.x, r.y, r.width, r.height);
return s += sfx;
}
nsACString&
AppendToString(nsACString& s, const nsIntRegion& r,
const char* pfx="", const char* sfx="")
@ -173,7 +184,7 @@ AppendToString(nsACString& s, const FrameMetrics& m,
{
s += pfx;
AppendToString(s, m.mViewport, "{ viewport=");
AppendToString(s, m.mViewportScrollOffset, " viewportScroll=");
AppendToString(s, m.mScrollOffset, " viewportScroll=");
AppendToString(s, m.mDisplayPort, " displayport=");
AppendToString(s, m.mScrollId, " scrollId=", " }");
return s += sfx;

View File

@ -253,7 +253,7 @@ BasicTiledThebesLayer::PaintThebes(gfxContext* aContext,
Layer* primaryScrollable = BasicManager()->GetPrimaryScrollableLayer();
if (primaryScrollable) {
const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics();
scrollOffset = metrics.mViewportScrollOffset;
scrollOffset = metrics.mScrollOffset;
}
int32_t scrollDiffX = scrollOffset.x - mLastScrollOffset.x;
int32_t scrollDiffY = scrollOffset.y - mLastScrollOffset.y;

View File

@ -44,7 +44,7 @@ static const int32_t FLING_REPAINT_INTERVAL = 75;
* Minimum amount of speed along an axis before we begin painting far ahead by
* adjusting the displayport.
*/
static const float MIN_SKATE_SPEED = 0.5f;
static const float MIN_SKATE_SPEED = 0.7f;
/**
* Angle from axis within which we stay axis-locked.
@ -79,17 +79,26 @@ static const double MIN_ZOOM = 0.125;
*/
static const int TOUCH_LISTENER_TIMEOUT = 300;
/**
* Number of samples to store of how long it took to paint after the previous
* requests.
*/
static const int NUM_PAINT_DURATION_SAMPLES = 3;
AsyncPanZoomController::AsyncPanZoomController(GeckoContentController* aGeckoContentController,
GestureBehavior aGestures)
: mGeckoContentController(aGeckoContentController),
mTouchListenerTimeoutTask(nullptr),
mX(this),
mY(this),
mAllowZoom(true),
mMinZoom(MIN_ZOOM),
mMaxZoom(MAX_ZOOM),
mMonitor("AsyncPanZoomController"),
mLastSampleTime(TimeStamp::Now()),
mState(NOTHING),
mDPI(72),
mContentPainterStatus(CONTENT_IDLE),
mWaitingForContentToPaint(false),
mDisableNextTouchBatch(false),
mHandlingTouchQueue(false)
{
@ -137,11 +146,11 @@ AsyncPanZoomController::ReceiveInputEvent(const nsInputEvent& aEvent,
gfx::Point currentScrollOffset, lastScrollOffset;
{
MonitorAutoLock monitor(mMonitor);
currentZoom = mFrameMetrics.mResolution.width;
currentScrollOffset = gfx::Point(mFrameMetrics.mViewportScrollOffset.x,
mFrameMetrics.mViewportScrollOffset.y);
lastScrollOffset = gfx::Point(mLastContentPaintMetrics.mViewportScrollOffset.x,
mLastContentPaintMetrics.mViewportScrollOffset.y);
currentZoom = mFrameMetrics.mZoom.width;
currentScrollOffset = gfx::Point(mFrameMetrics.mScrollOffset.x,
mFrameMetrics.mScrollOffset.y);
lastScrollOffset = gfx::Point(mLastContentPaintMetrics.mScrollOffset.x,
mLastContentPaintMetrics.mScrollOffset.y);
}
nsEventStatus status;
@ -284,8 +293,13 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent
case ANIMATING_ZOOM:
// We just interrupted a double-tap animation, so force a redraw in case
// this touchstart is just a tap that doesn't end up triggering a redraw.
{
MonitorAutoLock monitor(mMonitor);
// Bring the resolution back in sync with the zoom.
SetZoomAndResolution(mFrameMetrics.mZoom.width);
RequestContentRepaint();
ScheduleComposite();
}
// Fall through.
case FLING:
CancelAnimation();
@ -404,6 +418,10 @@ nsEventStatus AsyncPanZoomController::OnTouchCancel(const MultiTouchInput& aEven
}
nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
if (!mAllowZoom) {
return nsEventStatus_eConsumeNoDefault;
}
SetState(PINCHING);
mLastZoomFocus = aEvent.mFocusPoint;
@ -411,6 +429,10 @@ nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEve
}
nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
if (mState != PINCHING) {
return nsEventStatus_eConsumeNoDefault;
}
float prevSpan = aEvent.mPreviousSpan;
if (fabsf(prevSpan) <= EPSILON || fabsf(aEvent.mCurrentSpan) <= EPSILON) {
// We're still handling it; we've just decided to throw this event away.
@ -422,7 +444,7 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
{
MonitorAutoLock monitor(mMonitor);
float scale = mFrameMetrics.mResolution.width;
float scale = mFrameMetrics.mZoom.width;
nsIntPoint focusPoint = aEvent.mFocusPoint;
float xFocusChange = (mLastZoomFocus.x - focusPoint.x) / scale, yFocusChange = (mLastZoomFocus.y - focusPoint.y) / scale;
@ -442,14 +464,14 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
float neededDisplacementX = 0, neededDisplacementY = 0;
// Only do the scaling if we won't go over 8x zoom in or out.
bool doScale = (scale < MAX_ZOOM && spanRatio > 1.0f) || (scale > MIN_ZOOM && spanRatio < 1.0f);
bool doScale = (scale < mMaxZoom && spanRatio > 1.0f) || (scale > mMinZoom && spanRatio < 1.0f);
// If this zoom will take it over 8x zoom in either direction, but it's not
// already there, then normalize it.
if (scale * spanRatio > MAX_ZOOM) {
spanRatio = scale / MAX_ZOOM;
} else if (scale * spanRatio < MIN_ZOOM) {
spanRatio = scale / MIN_ZOOM;
if (scale * spanRatio > mMaxZoom) {
spanRatio = scale / mMaxZoom;
} else if (scale * spanRatio < mMinZoom) {
spanRatio = scale / mMinZoom;
}
if (doScale) {
@ -529,7 +551,7 @@ nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEven
gfx::Point point = WidgetSpaceToCompensatedViewportSpace(
gfx::Point(aEvent.mPoint.x, aEvent.mPoint.y),
mFrameMetrics.mResolution.width);
mFrameMetrics.mZoom.width);
mGeckoContentController->HandleSingleTap(nsIntPoint(NS_lround(point.x), NS_lround(point.y)));
return nsEventStatus_eConsumeNoDefault;
}
@ -545,10 +567,13 @@ nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent)
if (mGeckoContentController) {
MonitorAutoLock monitor(mMonitor);
if (mAllowZoom) {
gfx::Point point = WidgetSpaceToCompensatedViewportSpace(
gfx::Point(aEvent.mPoint.x, aEvent.mPoint.y),
mFrameMetrics.mResolution.width);
mFrameMetrics.mZoom.width);
mGeckoContentController->HandleDoubleTap(nsIntPoint(NS_lround(point.x), NS_lround(point.y)));
}
return nsEventStatus_eConsumeNoDefault;
}
return nsEventStatus_eIgnore;
@ -568,6 +593,10 @@ const gfx::Point AsyncPanZoomController::GetVelocityVector() {
return gfx::Point(mX.GetVelocity(), mY.GetVelocity());
}
const gfx::Point AsyncPanZoomController::GetAccelerationVector() {
return gfx::Point(mX.GetAccelerationFactor(), mY.GetAccelerationFactor());
}
void AsyncPanZoomController::StartPanning(const MultiTouchInput& aEvent) {
float dx = mX.PanDistance(),
dy = mY.PanDistance();
@ -614,7 +643,7 @@ void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
// We want to inversely scale it because when you're zoomed further in, a
// larger swipe should move you a shorter distance.
float inverseScale = 1 / mFrameMetrics.mResolution.width;
float inverseScale = 1 / mFrameMetrics.mZoom.width;
int32_t xDisplacement = mX.GetDisplacementForDuration(inverseScale, timeDelta);
int32_t yDisplacement = mY.GetDisplacementForDuration(inverseScale, timeDelta);
@ -642,6 +671,9 @@ bool AsyncPanZoomController::DoFling(const TimeDuration& aDelta) {
shouldContinueFlingY = mY.FlingApplyFrictionOrCancel(aDelta);
// If we shouldn't continue the fling, let's just stop and repaint.
if (!shouldContinueFlingX && !shouldContinueFlingY) {
// Bring the resolution back in sync with the zoom, in case we scaled down
// the zoom while accelerating.
SetZoomAndResolution(mFrameMetrics.mZoom.width);
RequestContentRepaint();
mState = NOTHING;
return false;
@ -649,7 +681,7 @@ bool AsyncPanZoomController::DoFling(const TimeDuration& aDelta) {
// We want to inversely scale it because when you're zoomed further in, a
// larger swipe should move you a shorter distance.
float inverseScale = 1 / mFrameMetrics.mResolution.width;
float inverseScale = 1 / mFrameMetrics.mZoom.width;
ScrollBy(gfx::Point(
mX.GetDisplacementForDuration(inverseScale, aDelta),
@ -669,132 +701,167 @@ void AsyncPanZoomController::SetCompositorParent(CompositorParent* aCompositorPa
}
void AsyncPanZoomController::ScrollBy(const gfx::Point& aOffset) {
gfx::Point newOffset(mFrameMetrics.mViewportScrollOffset.x + aOffset.x,
mFrameMetrics.mViewportScrollOffset.y + aOffset.y);
gfx::Point newOffset(mFrameMetrics.mScrollOffset.x + aOffset.x,
mFrameMetrics.mScrollOffset.y + aOffset.y);
FrameMetrics metrics(mFrameMetrics);
metrics.mViewportScrollOffset = newOffset;
metrics.mScrollOffset = newOffset;
mFrameMetrics = metrics;
}
void AsyncPanZoomController::SetPageRect(const gfx::Rect& aCSSPageRect) {
FrameMetrics metrics = mFrameMetrics;
gfx::Rect pageSize = aCSSPageRect;
float scale = mFrameMetrics.mResolution.width;
float scale = mFrameMetrics.mZoom.width;
// The page rect is the css page rect scaled by the current zoom.
pageSize.ScaleRoundOut(1 / scale);
pageSize.ScaleInverseRoundOut(scale);
// Round the page rect so we don't get any truncation, then get the nsIntRect
// from this.
metrics.mContentRect = nsIntRect(pageSize.x, pageSize.y, pageSize.width, pageSize.height);
metrics.mCSSContentRect = aCSSPageRect;
metrics.mScrollableRect = aCSSPageRect;
mFrameMetrics = metrics;
}
void AsyncPanZoomController::ScaleWithFocus(float aScale, const nsIntPoint& aFocus) {
FrameMetrics metrics(mFrameMetrics);
float scaleFactor = aScale / mFrameMetrics.mZoom.width;
// Don't set the scale to the inputted value, but rather multiply it in.
float scaleFactor = aScale / metrics.mResolution.width,
oldScale = metrics.mResolution.width;
metrics.mResolution.width = metrics.mResolution.height = aScale;
SetZoomAndResolution(aScale);
// Force a recalculation of the page rect based on the new zoom and the
// current CSS page rect (which is unchanged since it's not affected by zoom).
SetPageRect(mFrameMetrics.mCSSContentRect);
SetPageRect(mFrameMetrics.mScrollableRect);
gfx::Point scrollOffset = metrics.mViewportScrollOffset;
scrollOffset.x += float(aFocus.x) * (scaleFactor - 1.0f) / oldScale;
scrollOffset.y += float(aFocus.y) * (scaleFactor - 1.0f) / oldScale;
metrics.mViewportScrollOffset = scrollOffset;
mFrameMetrics = metrics;
// If the new scale is very small, we risk multiplying in huge rounding
// errors, so don't bother adjusting the scroll offset.
if (aScale >= 0.01f) {
mFrameMetrics.mScrollOffset.x += float(aFocus.x) * (scaleFactor - 1.0f) / aScale;
mFrameMetrics.mScrollOffset.y += float(aFocus.y) * (scaleFactor - 1.0f) / aScale;
}
}
bool AsyncPanZoomController::EnlargeDisplayPortAlongAxis(float aViewport,
bool AsyncPanZoomController::EnlargeDisplayPortAlongAxis(float aSkateSizeMultiplier,
double aEstimatedPaintDuration,
float aCompositionBounds,
float aVelocity,
float aAcceleration,
float* aDisplayPortOffset,
float* aDisplayPortLength)
{
const float MIN_SKATE_SIZE_MULTIPLIER = 2.0f;
const float MAX_SKATE_SIZE_MULTIPLIER = 4.0f;
if (fabsf(aVelocity) > MIN_SKATE_SPEED) {
*aDisplayPortLength = aViewport * clamped(fabsf(aVelocity),
MIN_SKATE_SIZE_MULTIPLIER, MAX_SKATE_SIZE_MULTIPLIER);
*aDisplayPortOffset = aVelocity > 0 ? 0 : aViewport - *aDisplayPortLength;
// Enlarge the area we paint.
*aDisplayPortLength = aCompositionBounds * aSkateSizeMultiplier;
// Position the area we paint such that all of the excess that extends past
// the screen is on the side towards the velocity.
*aDisplayPortOffset = aVelocity > 0 ? 0 : aCompositionBounds - *aDisplayPortLength;
// Only compensate for acceleration when we actually have any. Otherwise
// we'll overcompensate when a user is just panning around without flinging.
if (aAcceleration > 1.01f) {
// Compensate for acceleration and how long we expect a paint to take. We
// try to predict where the viewport will be when painting has finished.
*aDisplayPortOffset +=
fabsf(aAcceleration) * aVelocity * aCompositionBounds * aEstimatedPaintDuration;
// If our velocity is in the negative direction of the axis, we have to
// compensate for the fact that our scroll offset is the top-left position
// of the viewport. In this case, let's make it relative to the
// bottom-right. That way, we'll always be growing the displayport upwards
// and to the left when skating negatively.
*aDisplayPortOffset -= aVelocity < 0 ? aCompositionBounds : 0;
}
return true;
}
return false;
}
const nsIntRect AsyncPanZoomController::CalculatePendingDisplayPort() {
float scale = mFrameMetrics.mResolution.width;
nsIntRect viewport = mFrameMetrics.mViewport;
viewport.ScaleRoundIn(1 / scale);
const gfx::Rect AsyncPanZoomController::CalculatePendingDisplayPort(
const FrameMetrics& aFrameMetrics,
const gfx::Point& aVelocity,
const gfx::Point& aAcceleration,
double aEstimatedPaintDuration)
{
// The multiplier we apply to a dimension's length if it is skating. That is,
// if it's going above MIN_SKATE_SPEED. We prefer to increase the size of the
// Y axis because it is more natural in the case that a user is reading a page
// that scrolls up/down. Note that one, both or neither of these may be used
// at any instant.
const float X_SKATE_SIZE_MULTIPLIER = 3.0f;
const float Y_SKATE_SIZE_MULTIPLIER = 3.5f;
gfx::Point scrollOffset = mFrameMetrics.mViewportScrollOffset;
gfx::Point velocity = GetVelocityVector();
// The multiplier we apply to a dimension's length if it is stationary. We
// prefer to increase the size of the Y axis because it is more natural in the
// case that a user is reading a page that scrolls up/down. Note that one,
// both or neither of these may be used at any instant.
const float X_STATIONARY_SIZE_MULTIPLIER = 1.5f;
const float Y_STATIONARY_SIZE_MULTIPLIER = 2.5f;
// If we don't get an estimated paint duration, we probably don't have any
// data. In this case, we're dealing with either a stationary frame or a first
// paint. In either of these cases, we can just assume it'll take 1 second to
// paint. Getting this correct is not important anyways since it's only really
// useful when accelerating, which can't be happening at this point.
double estimatedPaintDuration =
aEstimatedPaintDuration > EPSILON ? aEstimatedPaintDuration : 1.0;
float scale = aFrameMetrics.mZoom.width;
nsIntRect compositionBounds = aFrameMetrics.mCompositionBounds;
compositionBounds.ScaleInverseRoundIn(scale);
const gfx::Rect& scrollableRect = aFrameMetrics.mScrollableRect;
gfx::Point scrollOffset = aFrameMetrics.mScrollOffset;
// The displayport is relative to the current scroll offset. Here's a little
// diagram to make it easier to see:
//
// - - - -
// | |
// *************
// * | | *
// - -*- @------ -*- -
// | * |=====| * |
// * |=====| *
// | * |=====| * |
// - -*- ------- -*- -
// * | | *
// *************
// | |
// - - - -
//
// The full --- area with === inside it is the actual viewport rect, the *** area
// is the displayport, and the - - - area is an imaginary additional page on all 4
// borders of the actual page. Notice that the displayport intersects half-way with
// each of the imaginary extra pages. The @ symbol at the top left of the
// viewport marks the current scroll offset. From the @ symbol to the far left
// and far top, it is clear that this distance is 1/4 of the displayport's
// height/width dimension.
const float STATIONARY_SIZE_MULTIPLIER = 2.0f;
gfx::Rect displayPort(0, 0,
viewport.width * STATIONARY_SIZE_MULTIPLIER,
viewport.height * STATIONARY_SIZE_MULTIPLIER);
compositionBounds.width * X_STATIONARY_SIZE_MULTIPLIER,
compositionBounds.height * Y_STATIONARY_SIZE_MULTIPLIER);
// If there's motion along an axis of movement, and it's above a threshold,
// then we want to paint a larger area in the direction of that motion so that
// it's less likely to checkerboard.
bool enlargedX = EnlargeDisplayPortAlongAxis(
viewport.width, velocity.x, &displayPort.x, &displayPort.width);
X_SKATE_SIZE_MULTIPLIER, estimatedPaintDuration,
compositionBounds.width, aVelocity.x, aAcceleration.x,
&displayPort.x, &displayPort.width);
bool enlargedY = EnlargeDisplayPortAlongAxis(
viewport.height, velocity.y, &displayPort.y, &displayPort.height);
Y_SKATE_SIZE_MULTIPLIER, estimatedPaintDuration,
compositionBounds.height, aVelocity.y, aAcceleration.y,
&displayPort.y, &displayPort.height);
if (!enlargedX && !enlargedY) {
displayPort.x = -displayPort.width / 4;
displayPort.y = -displayPort.height / 4;
// Position the x and y such that the screen falls in the middle of the displayport.
displayPort.x = -(displayPort.width - compositionBounds.width) / 2;
displayPort.y = -(displayPort.height - compositionBounds.height) / 2;
} else if (!enlargedX) {
displayPort.width = viewport.width;
displayPort.width = compositionBounds.width;
} else if (!enlargedY) {
displayPort.height = viewport.height;
displayPort.height = compositionBounds.height;
}
// If we go over the bounds when trying to predict where we will be when this
// paint finishes, move it back into the range of the CSS content rect.
// FIXME/bug 780395: Generalize this. This code is pretty hacky as it will
// probably not work at all for RTL content. This is not intended to be
// incredibly accurate; it'll just prevent the entire displayport from being
// outside the content rect (which causes bad things to happen).
if (enlargedX || enlargedY) {
if (scrollOffset.x + compositionBounds.width > scrollableRect.width) {
scrollOffset.x -= compositionBounds.width + scrollOffset.x - scrollableRect.width;
} else if (scrollOffset.x < scrollableRect.x) {
scrollOffset.x = scrollableRect.x;
}
if (scrollOffset.y + compositionBounds.height > scrollableRect.height) {
scrollOffset.y -= compositionBounds.height + scrollOffset.y - scrollableRect.height;
} else if (scrollOffset.y < scrollableRect.y) {
scrollOffset.y = scrollableRect.y;
}
}
gfx::Rect shiftedDisplayPort = displayPort;
shiftedDisplayPort.MoveBy(scrollOffset.x, scrollOffset.y);
displayPort = shiftedDisplayPort.Intersect(mFrameMetrics.mCSSContentRect);
displayPort = shiftedDisplayPort.Intersect(aFrameMetrics.mScrollableRect);
displayPort.MoveBy(-scrollOffset.x, -scrollOffset.y);
// Round the displayport so we don't get any truncation, then get the nsIntRect
// from this.
displayPort.Round();
return nsIntRect(displayPort.x, displayPort.y, displayPort.width, displayPort.height);
return displayPort;
}
void AsyncPanZoomController::SetDPI(int aDPI) {
@ -812,24 +879,31 @@ void AsyncPanZoomController::ScheduleComposite() {
}
void AsyncPanZoomController::RequestContentRepaint() {
mFrameMetrics.mDisplayPort = CalculatePendingDisplayPort();
mPreviousPaintStartTime = TimeStamp::Now();
gfx::Point oldScrollOffset = mLastPaintRequestMetrics.mViewportScrollOffset,
newScrollOffset = mFrameMetrics.mViewportScrollOffset;
double estimatedPaintSum = 0.0;
for (uint32_t i = 0; i < mPreviousPaintDurations.Length(); i++) {
estimatedPaintSum += mPreviousPaintDurations[i].ToSeconds();
}
double estimatedPaintDuration = 0.0;
if (estimatedPaintSum > EPSILON) {
estimatedPaintDuration = estimatedPaintSum / mPreviousPaintDurations.Length();
}
mFrameMetrics.mDisplayPort =
CalculatePendingDisplayPort(mFrameMetrics,
GetVelocityVector(),
GetAccelerationVector(),
estimatedPaintDuration);
gfx::Point oldScrollOffset = mLastPaintRequestMetrics.mScrollOffset,
newScrollOffset = mFrameMetrics.mScrollOffset;
// If we're trying to paint what we already think is painted, discard this
// request since it's a pointless paint.
nsRect oldDisplayPort = nsRect(
mLastPaintRequestMetrics.mDisplayPort.x,
mLastPaintRequestMetrics.mDisplayPort.y,
mLastPaintRequestMetrics.mDisplayPort.width,
mLastPaintRequestMetrics.mDisplayPort.height);
gfx::Rect newDisplayPort = gfx::Rect(
mFrameMetrics.mDisplayPort.x,
mFrameMetrics.mDisplayPort.y,
mFrameMetrics.mDisplayPort.width,
mFrameMetrics.mDisplayPort.height);
gfx::Rect oldDisplayPort = mLastPaintRequestMetrics.mDisplayPort;
gfx::Rect newDisplayPort = mFrameMetrics.mDisplayPort;
oldDisplayPort.MoveBy(oldScrollOffset.x, oldScrollOffset.y);
newDisplayPort.MoveBy(newScrollOffset.x, newScrollOffset.y);
@ -842,13 +916,27 @@ void AsyncPanZoomController::RequestContentRepaint() {
return;
}
if (mContentPainterStatus == CONTENT_IDLE) {
mContentPainterStatus = CONTENT_PAINTING;
mLastPaintRequestMetrics = mFrameMetrics;
// Cache the resolution since we're temporarily changing it to accomodate
// mixed resolution/zoom (normally we make them the same thing).
float actualResolution = mFrameMetrics.mResolution.width;
// Calculate the factor of acceleration based on the faster of the two axes.
float accelerationFactor =
clamped(NS_MAX(mX.GetAccelerationFactor(), mY.GetAccelerationFactor()),
float(MIN_ZOOM) / 2.0f, float(MAX_ZOOM));
// Scale down the resolution a bit based on acceleration.
mFrameMetrics.mResolution.width = mFrameMetrics.mResolution.height =
actualResolution / accelerationFactor;
// This message is compressed, so fire whether or not we already have a paint
// queued up. We need to know whether or not a paint was requested anyways,
// ofr the purposes of content calling window.scrollTo().
mGeckoContentController->RequestContentRepaint(mFrameMetrics);
} else {
mContentPainterStatus = CONTENT_PAINTING_AND_PAINT_PENDING;
}
mLastPaintRequestMetrics = mFrameMetrics;
mWaitingForContentToPaint = true;
// Set the resolution back to what it was for the purpose of logic control.
mFrameMetrics.mResolution.width = mFrameMetrics.mResolution.height =
actualResolution;
}
bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSampleTime,
@ -888,20 +976,22 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa
}
double sampledPosition = gComputedTimingFunction->GetValue(animPosition);
mFrameMetrics.mResolution.width = mFrameMetrics.mResolution.height =
mEndZoomToMetrics.mResolution.width * sampledPosition +
mStartZoomToMetrics.mResolution.width * (1 - sampledPosition);
mFrameMetrics.mZoom.width = mFrameMetrics.mZoom.height =
mEndZoomToMetrics.mZoom.width * sampledPosition +
mStartZoomToMetrics.mZoom.width * (1 - sampledPosition);
mFrameMetrics.mViewportScrollOffset = gfx::Point(
mEndZoomToMetrics.mViewportScrollOffset.x * sampledPosition +
mStartZoomToMetrics.mViewportScrollOffset.x * (1 - sampledPosition),
mEndZoomToMetrics.mViewportScrollOffset.y * sampledPosition +
mStartZoomToMetrics.mViewportScrollOffset.y * (1 - sampledPosition)
mFrameMetrics.mScrollOffset = gfx::Point(
mEndZoomToMetrics.mScrollOffset.x * sampledPosition +
mStartZoomToMetrics.mScrollOffset.x * (1 - sampledPosition),
mEndZoomToMetrics.mScrollOffset.y * sampledPosition +
mStartZoomToMetrics.mScrollOffset.y * (1 - sampledPosition)
);
requestAnimationFrame = true;
if (aSampleTime - mAnimationStartTime >= ZOOM_TO_DURATION) {
// Bring the resolution in sync with the zoom.
SetZoomAndResolution(mFrameMetrics.mZoom.width);
mState = NOTHING;
RequestContentRepaint();
}
@ -916,14 +1006,14 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa
// transformed due to touches like panning or pinching. Eventually, the root
// layer transform will become this during runtime, but we must wait for Gecko
// to repaint.
localScaleX = mFrameMetrics.mResolution.width;
localScaleY = mFrameMetrics.mResolution.height;
localScaleX = mFrameMetrics.mZoom.width;
localScaleY = mFrameMetrics.mZoom.height;
if (frame.IsScrollable()) {
metricsScrollOffset = frame.mViewportScrollOffset;
metricsScrollOffset = frame.GetScrollOffsetInLayerPixels();
}
scrollOffset = mFrameMetrics.mViewportScrollOffset;
scrollOffset = mFrameMetrics.mScrollOffset;
}
nsIntPoint scrollCompensation(
@ -953,13 +1043,15 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr
mLastContentPaintMetrics = aViewportFrame;
if (mContentPainterStatus != CONTENT_IDLE) {
if (mContentPainterStatus == CONTENT_PAINTING_AND_PAINT_PENDING) {
mContentPainterStatus = CONTENT_IDLE;
RequestContentRepaint();
} else {
mContentPainterStatus = CONTENT_IDLE;
if (mWaitingForContentToPaint) {
// Remove the oldest sample we have if adding a new sample takes us over our
// desired number of samples.
if (mPreviousPaintDurations.Length() >= NUM_PAINT_DURATION_SAMPLES) {
mPreviousPaintDurations.RemoveElementAt(0);
}
mPreviousPaintDurations.AppendElement(
TimeStamp::Now() - mPreviousPaintStartTime);
} else {
// No paint was requested, but we got one anyways. One possible cause of this
// is that content could have fired a scrollTo(). In this case, we should take
@ -972,10 +1064,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr
case FLING:
case TOUCHING:
case WAITING_LISTENERS:
// FIXME/bug 784908: Scroll offset is stored in layer pixels in the rest
// of the layers code, but we want it in CSS pixels.
mFrameMetrics.mViewportScrollOffset =
aViewportFrame.mViewportScrollOffset / aViewportFrame.mResolution.width;
mFrameMetrics.mScrollOffset = aViewportFrame.mScrollOffset;
break;
// Don't clobber if we're in other states.
default:
@ -983,25 +1072,28 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr
}
}
mWaitingForContentToPaint = false;
if (aIsFirstPaint || mFrameMetrics.IsDefault()) {
mContentPainterStatus = CONTENT_IDLE;
mPreviousPaintDurations.Clear();
mX.CancelTouch();
mY.CancelTouch();
mFrameMetrics = aViewportFrame;
mFrameMetrics.mResolution.width = 1 / mFrameMetrics.mResolution.width;
mFrameMetrics.mResolution.height = 1 / mFrameMetrics.mResolution.height;
SetPageRect(mFrameMetrics.mCSSContentRect);
// Bug 776413/fixme: Request a repaint as soon as a page is loaded so that
// we get a larger displayport. This is very bad because we're wasting a
// paint and not initializating the displayport correctly.
RequestContentRepaint();
// The composition bounds are not stored within the layers code, so we have
// to reset them back to what they were every time we overwrite them.
nsIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
mFrameMetrics = aViewportFrame;
mFrameMetrics.mCompositionBounds = compositionBounds;
// On first paint, we want to bring zoom back in sync with resolution.
mFrameMetrics.mZoom = mFrameMetrics.mResolution;
SetPageRect(mFrameMetrics.mScrollableRect);
mState = NOTHING;
} else if (!mFrameMetrics.mCSSContentRect.IsEqualEdges(aViewportFrame.mCSSContentRect)) {
mFrameMetrics.mCSSContentRect = aViewportFrame.mCSSContentRect;
SetPageRect(mFrameMetrics.mCSSContentRect);
} else if (!mFrameMetrics.mScrollableRect.IsEqualEdges(aViewportFrame.mScrollableRect)) {
mFrameMetrics.mScrollableRect = aViewportFrame.mScrollableRect;
SetPageRect(mFrameMetrics.mScrollableRect);
}
}
@ -1010,11 +1102,27 @@ const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() {
return mFrameMetrics;
}
void AsyncPanZoomController::UpdateViewportSize(int aWidth, int aHeight) {
void AsyncPanZoomController::UpdateCompositionBounds(const nsIntRect& aCompositionBounds) {
MonitorAutoLock mon(mMonitor);
FrameMetrics metrics = GetFrameMetrics();
metrics.mViewport = nsIntRect(0, 0, aWidth, aHeight);
mFrameMetrics = metrics;
nsIntRect oldCompositionBounds = mFrameMetrics.mCompositionBounds;
mFrameMetrics.mCompositionBounds = aCompositionBounds;
// If the window had 0 dimensions before, or does now, we don't want to
// repaint or update the zoom since we'll run into rendering issues and/or
// divide-by-zero. This manifests itself as the screen flashing. If the page
// has gone out of view, the buffer will be cleared elsewhere anyways.
if (aCompositionBounds.width && aCompositionBounds.height &&
oldCompositionBounds.width && oldCompositionBounds.height) {
// Alter the zoom such that we can see the same width of the page as we used
// to be able to.
SetZoomAndResolution(mFrameMetrics.mResolution.width *
aCompositionBounds.width /
oldCompositionBounds.width);
// Repaint on a rotation so that our new resolution gets properly updated.
RequestContentRepaint();
}
}
void AsyncPanZoomController::CancelDefaultPanZoom() {
@ -1032,27 +1140,29 @@ void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) {
{
MonitorAutoLock mon(mMonitor);
nsIntRect viewport = mFrameMetrics.mViewport;
gfx::Rect cssPageRect = mFrameMetrics.mCSSContentRect;
gfx::Point scrollOffset = mFrameMetrics.mViewportScrollOffset;
nsIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
gfx::Rect cssPageRect = mFrameMetrics.mScrollableRect;
gfx::Point scrollOffset = mFrameMetrics.mScrollOffset;
// If the rect is empty, treat it as a request to zoom out to the full page
// size.
if (zoomToRect.IsEmpty()) {
nsIntRect cssViewport = viewport;
cssViewport.ScaleRoundIn(1 / mFrameMetrics.mResolution.width);
cssViewport.MoveBy(nsIntPoint(NS_lround(scrollOffset.x), NS_lround(scrollOffset.y)));
// composition bounds in CSS coordinates
nsIntRect cssCompositionBounds = compositionBounds;
cssCompositionBounds.ScaleInverseRoundIn(mFrameMetrics.mZoom.width);
cssCompositionBounds.MoveBy(scrollOffset.x, scrollOffset.y);
float y = mFrameMetrics.mViewportScrollOffset.y;
float newHeight = cssViewport.height * cssPageRect.width / cssViewport.width;
float dh = cssViewport.height - newHeight;
float y = mFrameMetrics.mScrollOffset.y;
float newHeight =
cssCompositionBounds.height * cssPageRect.width / cssCompositionBounds.width;
float dh = cssCompositionBounds.height - newHeight;
zoomToRect = gfx::Rect(0.0f,
y + dh/2,
cssPageRect.width,
y + dh/2 + newHeight);
} else {
float targetRatio = float(viewport.width) / float(viewport.height);
float targetRatio = float(compositionBounds.width) / float(compositionBounds.height);
float rectRatio = zoomToRect.width / zoomToRect.height;
if (fabsf(targetRatio - rectRatio) < EPSILON) {
@ -1072,25 +1182,27 @@ void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) {
zoomToRect = zoomToRect.Intersect(cssPageRect);
}
mEndZoomToMetrics.mResolution.width = mEndZoomToMetrics.mResolution.height =
NS_MIN(viewport.width / zoomToRect.width, viewport.height / zoomToRect.height);
mEndZoomToMetrics.mZoom.width = mEndZoomToMetrics.mZoom.height =
NS_MIN(compositionBounds.width / zoomToRect.width, compositionBounds.height / zoomToRect.height);
mEndZoomToMetrics.mResolution.width = mEndZoomToMetrics.mResolution.height =
clamped(mEndZoomToMetrics.mResolution.width, MIN_ZOOM, MAX_ZOOM);
mEndZoomToMetrics.mZoom.width = mEndZoomToMetrics.mZoom.height =
clamped(float(mEndZoomToMetrics.mZoom.width),
mMinZoom,
mMaxZoom);
// Recalculate the zoom to rect using the new dimensions.
zoomToRect.width = viewport.width / mEndZoomToMetrics.mResolution.width;
zoomToRect.height = viewport.height / mEndZoomToMetrics.mResolution.height;
zoomToRect.width = compositionBounds.width / mEndZoomToMetrics.mZoom.width;
zoomToRect.height = compositionBounds.height / mEndZoomToMetrics.mZoom.height;
// Clamp the zoom to rect to the CSS rect to make sure it fits.
zoomToRect = zoomToRect.Intersect(cssPageRect);
// Do one final recalculation to get the resolution.
mEndZoomToMetrics.mResolution.width = mEndZoomToMetrics.mResolution.height =
NS_MAX(viewport.width / zoomToRect.width, viewport.height / zoomToRect.height);
mEndZoomToMetrics.mZoom.width = mEndZoomToMetrics.mZoom.height =
NS_MAX(compositionBounds.width / zoomToRect.width, compositionBounds.height / zoomToRect.height);
mStartZoomToMetrics = mFrameMetrics;
mEndZoomToMetrics.mViewportScrollOffset =
mEndZoomToMetrics.mScrollOffset =
gfx::Point(zoomToRect.x, zoomToRect.y);
mAnimationStartTime = TimeStamp::Now();
@ -1144,5 +1256,19 @@ void AsyncPanZoomController::TimeoutTouchListeners() {
ContentReceivedTouch(false);
}
void AsyncPanZoomController::SetZoomAndResolution(float aScale) {
mMonitor.AssertCurrentThreadOwns();
mFrameMetrics.mResolution.width = mFrameMetrics.mResolution.height =
mFrameMetrics.mZoom.width = mFrameMetrics.mZoom.height = aScale;
}
void AsyncPanZoomController::UpdateZoomConstraints(bool aAllowZoom,
float aMinZoom,
float aMaxZoom) {
mAllowZoom = aAllowZoom;
mMinZoom = aMinZoom;
mMaxZoom = aMaxZoom;
}
}
}

View File

@ -14,6 +14,7 @@
#include "mozilla/TimeStamp.h"
#include "InputData.h"
#include "Axis.h"
#include "nsContentUtils.h"
#include "base/message_loop.h"
@ -97,17 +98,13 @@ public:
nsInputEvent* aOutEvent);
/**
* Updates the viewport size, i.e. the dimensions of the frame (not
* necessarily the screen) content will actually be rendered onto in device
* pixels for example, a subframe will not take the entire screen, but we
* still want to know how big it is in device pixels. Ideally we want to be
* using CSS pixels everywhere inside here, but in this case we need to know
* how large of a displayport to set so we use these dimensions plus some
* extra.
*
* XXX: Use nsIntRect instead.
* Updates the composition bounds, i.e. the dimensions of the final size of
* the frame this is tied to during composition onto, in device pixels. In
* general, this will just be:
* { x = 0, y = 0, width = surface.width, height = surface.height }, however
* there is no hard requirement for this.
*/
void UpdateViewportSize(int aWidth, int aHeight);
void UpdateCompositionBounds(const nsIntRect& aCompositionBounds);
/**
* We have found a scrollable subframe, so disable our machinery until we hit
@ -134,6 +131,13 @@ public:
*/
void ContentReceivedTouch(bool aPreventDefault);
/**
* Updates any zoom constraints contained in the <meta name="viewport"> tag.
* We try to obey everything it asks us elsewhere, but here we only handle
* minimum-scale, maximum-scale, and user-scalable.
*/
void UpdateZoomConstraints(bool aAllowZoom, float aMinScale, float aMaxScale);
// --------------------------------------------------------------------------
// These methods must only be called on the compositor thread.
//
@ -193,6 +197,18 @@ public:
*/
int GetDPI();
/**
* Recalculates the displayport. Ideally, this should paint an area bigger
* than the composite-to dimensions so that when you scroll down, you don't
* checkerboard immediately. This includes a bunch of logic, including
* algorithms to bias painting in the direction of the velocity.
*/
static const gfx::Rect CalculatePendingDisplayPort(
const FrameMetrics& aFrameMetrics,
const gfx::Point& aVelocity,
const gfx::Point& aAcceleration,
double aEstimatedPaintDuration);
protected:
/**
* Internal handler for ReceiveInputEvent(). Does all the actual work.
@ -319,6 +335,11 @@ protected:
*/
const gfx::Point GetVelocityVector();
/**
* Gets a vector of the acceleration factors of each axis.
*/
const gfx::Point GetAccelerationVector();
/**
* Gets a reference to the first SingleTouchData from a MultiTouchInput. This
* gets only the first one and assumes the rest are either missing or not
@ -343,15 +364,6 @@ protected:
*/
void TrackTouch(const MultiTouchInput& aEvent);
/**
* Recalculates the displayport. Ideally, this should paint an area bigger
* than the actual screen. The viewport refers to the size of the screen,
* while the displayport is the area actually painted by Gecko. We paint
* a larger area than the screen so that when you scroll down, you don't
* checkerboard immediately.
*/
const nsIntRect CalculatePendingDisplayPort();
/**
* Attempts to enlarge the displayport along a single axis. Returns whether or
* not the displayport was enlarged. This will fail in circumstances where the
@ -360,8 +372,13 @@ protected:
* |aDisplayPortLength|. If enlarged, these will be updated with the new
* metrics.
*/
bool EnlargeDisplayPortAlongAxis(float aViewport, float aVelocity,
float* aDisplayPortOffset, float* aDisplayPortLength);
static bool EnlargeDisplayPortAlongAxis(float aSkateSizeMultiplier,
double aEstimatedPaintDuration,
float aCompositionBounds,
float aVelocity,
float aAcceleration,
float* aDisplayPortOffset,
float* aDisplayPortLength);
/**
* Utility function to send updated FrameMetrics to Gecko so that it can paint
@ -396,6 +413,14 @@ protected:
*/
void TimeoutTouchListeners();
/**
* Utility function that sets the zoom and resolution simultaneously. This is
* useful when we want to repaint at the current zoom level.
*
* *** The monitor must be held while calling this.
*/
void SetZoomAndResolution(float aScale);
private:
enum PanZoomState {
NOTHING, /* no touch-start events received */
@ -409,25 +434,6 @@ private:
prevented the default actions yet. we still need to abort animations. */
};
enum ContentPainterStatus {
// A paint may be happening, but it is not due to any action taken by this
// thread. For example, content could be invalidating itself, but
// AsyncPanZoomController has nothing to do with that.
CONTENT_IDLE,
// Set every time we dispatch a request for a repaint. When a
// ShadowLayersUpdate arrives and the metrics of this frame have changed, we
// toggle this off and assume that the paint has completed.
CONTENT_PAINTING,
// Set when we have a new displayport in the pipeline that we want to paint.
// When a ShadowLayersUpdate comes in, we dispatch a new repaint using
// mFrameMetrics.mDisplayPort (the most recent request) if this is toggled.
// This is distinct from CONTENT_PAINTING in that it signals that a repaint
// is happening, whereas this signals that we want to repaint as soon as the
// previous paint finishes. When the request is eventually made, it will use
// the most up-to-date metrics.
CONTENT_PAINTING_AND_PAINT_PENDING
};
/**
* Helper to set the current state. Holds the monitor before actually setting
* it. If the monitor is already held by the current thread, it is safe to
@ -467,10 +473,19 @@ private:
AxisX mX;
AxisY mY;
// Protects |mFrameMetrics|, |mLastContentPaintMetrics| and |mState|. Before
// manipulating |mFrameMetrics| or |mLastContentPaintMetrics|, the monitor
// should be held. When setting |mState|, either the SetState() function can
// be used, or the monitor can be held and then |mState| updated.
// Most up-to-date constraints on zooming. These should always be reasonable
// values; for example, allowing a min zoom of 0.0 can cause very bad things
// to happen.
bool mAllowZoom;
float mMinZoom;
float mMaxZoom;
// Protects |mFrameMetrics|, |mLastContentPaintMetrics|, |mState| and
// |mMetaViewportInfo|. Before manipulating |mFrameMetrics| or
// |mLastContentPaintMetrics|, the monitor should be held. When setting
// |mState|, either the SetState() function can be used, or the monitor can be
// held and then |mState| updated. |mMetaViewportInfo| should be updated
// using UpdateMetaViewport().
Monitor mMonitor;
// The last time the compositor has sampled the content transform for this
@ -491,13 +506,20 @@ private:
// |mMonitor|; that is, it should be held whenever this is updated.
PanZoomState mState;
// How long it took in the past to paint after a series of previous requests.
nsTArray<TimeDuration> mPreviousPaintDurations;
// When the last paint request started. Used to determine the duration of
// previous paints.
TimeStamp mPreviousPaintStartTime;
int mDPI;
// Stores the current paint status of the frame that we're managing. Repaints
// may be triggered by other things (like content doing things), in which case
// this status will not be updated. It is only changed when this class
// requests a repaint.
ContentPainterStatus mContentPainterStatus;
bool mWaitingForContentToPaint;
// Flag used to determine whether or not we should disable handling of the
// next batch of touch events. This is used for sync scrolling of subframes.

View File

@ -18,7 +18,7 @@ static const float EPSILON = 0.0001f;
* or we get a touch point very far away from the previous position for some
* reason.
*/
static const float MAX_EVENT_ACCELERATION = 0.5f;
static const float MAX_EVENT_ACCELERATION = 999.0f;
/**
* Amount of friction applied during flings.
@ -95,15 +95,19 @@ void Axis::StartTouch(int32_t aPos) {
}
float Axis::GetDisplacementForDuration(float aScale, const TimeDuration& aDelta) {
float velocityFactor = powf(ACCELERATION_MULTIPLIER,
NS_MAX(0, (mAcceleration - 4) * 3));
float displacement = mVelocity * aScale * aDelta.ToMilliseconds() * velocityFactor;
if (fabsf(mVelocity) < VELOCITY_THRESHOLD) {
mAcceleration = 0;
}
float accelerationFactor = GetAccelerationFactor();
float displacement = mVelocity * aScale * aDelta.ToMilliseconds() * accelerationFactor;
// If this displacement will cause an overscroll, throttle it. Can potentially
// bring it to 0 even if the velocity is high.
if (DisplacementWillOverscroll(displacement) != OVERSCROLL_NONE) {
// No need to have a velocity along this axis anymore; it won't take us
// anywhere, so we're just spinning needlessly.
mVelocity = 0.0f;
mAcceleration = 0;
displacement -= DisplacementWillOverscrollAmount(displacement);
}
return displacement;
@ -140,12 +144,12 @@ bool Axis::FlingApplyFrictionOrCancel(const TimeDuration& aDelta) {
}
Axis::Overscroll Axis::GetOverscroll() {
// If the current pan takes the viewport to the left of or above the current
// If the current pan takes the window to the left of or above the current
// page rect.
bool minus = GetOrigin() < GetPageStart();
// If the current pan takes the viewport to the right of or below the current
// If the current pan takes the window to the right of or below the current
// page rect.
bool plus = GetViewportEnd() > GetPageEnd();
bool plus = GetCompositionEnd() > GetPageEnd();
if (minus && plus) {
return OVERSCROLL_BOTH;
}
@ -161,19 +165,20 @@ Axis::Overscroll Axis::GetOverscroll() {
float Axis::GetExcess() {
switch (GetOverscroll()) {
case OVERSCROLL_MINUS: return GetOrigin() - GetPageStart();
case OVERSCROLL_PLUS: return GetViewportEnd() - GetPageEnd();
case OVERSCROLL_BOTH: return (GetViewportEnd() - GetPageEnd()) + (GetPageStart() - GetOrigin());
case OVERSCROLL_PLUS: return GetCompositionEnd() - GetPageEnd();
case OVERSCROLL_BOTH: return (GetCompositionEnd() - GetPageEnd()) +
(GetPageStart() - GetOrigin());
default: return 0;
}
}
Axis::Overscroll Axis::DisplacementWillOverscroll(int32_t aDisplacement) {
// If the current pan plus a displacement takes the viewport to the left of or
// If the current pan plus a displacement takes the window to the left of or
// above the current page rect.
bool minus = GetOrigin() + aDisplacement < GetPageStart();
// If the current pan plus a displacement takes the viewport to the right of or
// If the current pan plus a displacement takes the window to the right of or
// below the current page rect.
bool plus = GetViewportEnd() + aDisplacement > GetPageEnd();
bool plus = GetCompositionEnd() + aDisplacement > GetPageEnd();
if (minus && plus) {
return OVERSCROLL_BOTH;
}
@ -189,7 +194,7 @@ Axis::Overscroll Axis::DisplacementWillOverscroll(int32_t aDisplacement) {
float Axis::DisplacementWillOverscrollAmount(int32_t aDisplacement) {
switch (DisplacementWillOverscroll(aDisplacement)) {
case OVERSCROLL_MINUS: return (GetOrigin() + aDisplacement) - GetPageStart();
case OVERSCROLL_PLUS: return (GetViewportEnd() + aDisplacement) - GetPageEnd();
case OVERSCROLL_PLUS: return (GetCompositionEnd() + aDisplacement) - GetPageEnd();
// Don't handle overscrolled in both directions; a displacement can't cause
// this, it must have already been zoomed out too far.
default: return 0;
@ -201,7 +206,7 @@ Axis::Overscroll Axis::ScaleWillOverscroll(float aScale, int32_t aFocus) {
bool both = ScaleWillOverscrollBothSides(aScale);
bool minus = originAfterScale < GetPageStart() * aScale;
bool plus = (originAfterScale + GetViewportLength()) > GetPageEnd() * aScale;
bool plus = (originAfterScale + GetCompositionLength()) > GetPageEnd() * aScale;
if ((minus && plus) || both) {
return OVERSCROLL_BOTH;
@ -219,7 +224,8 @@ float Axis::ScaleWillOverscrollAmount(float aScale, int32_t aFocus) {
float originAfterScale = (GetOrigin() + aFocus) * aScale - aFocus;
switch (ScaleWillOverscroll(aScale, aFocus)) {
case OVERSCROLL_MINUS: return originAfterScale - GetPageStart() * aScale;
case OVERSCROLL_PLUS: return (originAfterScale + GetViewportLength()) - GetPageEnd() * aScale;
case OVERSCROLL_PLUS: return (originAfterScale + GetCompositionLength()) -
NS_lround(GetPageEnd() * aScale);
// Don't handle OVERSCROLL_BOTH. Client code is expected to deal with it.
default: return 0;
}
@ -229,8 +235,12 @@ float Axis::GetVelocity() {
return mVelocity;
}
float Axis::GetViewportEnd() {
return GetOrigin() + GetViewportLength();
float Axis::GetAccelerationFactor() {
return powf(ACCELERATION_MULTIPLIER, NS_MAX(0, (mAcceleration - 4) * 3));
}
float Axis::GetCompositionEnd() {
return GetOrigin() + GetCompositionLength();
}
float Axis::GetPageEnd() {
@ -238,40 +248,44 @@ float Axis::GetPageEnd() {
}
float Axis::GetOrigin() {
gfx::Point origin = mAsyncPanZoomController->GetFrameMetrics().mViewportScrollOffset;
gfx::Point origin = mAsyncPanZoomController->GetFrameMetrics().mScrollOffset;
return GetPointOffset(origin);
}
float Axis::GetViewportLength() {
nsIntRect viewport = mAsyncPanZoomController->GetFrameMetrics().mViewport;
gfx::Rect scaledViewport = gfx::Rect(viewport.x, viewport.y, viewport.width, viewport.height);
scaledViewport.ScaleRoundIn(1 / mAsyncPanZoomController->GetFrameMetrics().mResolution.width);
return GetRectLength(scaledViewport);
float Axis::GetCompositionLength() {
nsIntRect compositionBounds =
mAsyncPanZoomController->GetFrameMetrics().mCompositionBounds;
gfx::Rect scaledCompositionBounds =
gfx::Rect(compositionBounds.x, compositionBounds.y,
compositionBounds.width, compositionBounds.height);
scaledCompositionBounds.ScaleInverseRoundIn(
mAsyncPanZoomController->GetFrameMetrics().mZoom.width);
return GetRectLength(scaledCompositionBounds);
}
float Axis::GetPageStart() {
gfx::Rect pageRect = mAsyncPanZoomController->GetFrameMetrics().mCSSContentRect;
gfx::Rect pageRect = mAsyncPanZoomController->GetFrameMetrics().mScrollableRect;
return GetRectOffset(pageRect);
}
float Axis::GetPageLength() {
gfx::Rect pageRect = mAsyncPanZoomController->GetFrameMetrics().mCSSContentRect;
gfx::Rect pageRect = mAsyncPanZoomController->GetFrameMetrics().mScrollableRect;
return GetRectLength(pageRect);
}
bool Axis::ScaleWillOverscrollBothSides(float aScale) {
const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics();
gfx::Rect cssContentRect = metrics.mCSSContentRect;
gfx::Rect cssContentRect = metrics.mScrollableRect;
float currentScale = metrics.mResolution.width;
gfx::Rect viewport = gfx::Rect(metrics.mViewport.x,
metrics.mViewport.y,
metrics.mViewport.width,
metrics.mViewport.height);
viewport.ScaleRoundIn(1 / (currentScale * aScale));
float currentScale = metrics.mZoom.width;
nsIntRect compositionBounds = metrics.mCompositionBounds;
gfx::Rect scaledCompositionBounds =
gfx::Rect(compositionBounds.x, compositionBounds.y,
compositionBounds.width, compositionBounds.height);
scaledCompositionBounds.ScaleInverseRoundIn(currentScale * aScale);
return GetRectLength(cssContentRect) < GetRectLength(viewport);
return GetRectLength(cssContentRect) < GetRectLength(scaledCompositionBounds);
}
AxisX::AxisX(AsyncPanZoomController* aAsyncPanZoomController)

View File

@ -116,6 +116,12 @@ public:
*/
float GetExcess();
/**
* Gets the factor of acceleration applied to the velocity, based on the
* amount of flings that have been done successively.
*/
float GetAccelerationFactor();
/**
* Gets the raw velocity of this axis at this moment.
*/
@ -165,10 +171,10 @@ public:
bool ScaleWillOverscrollBothSides(float aScale);
float GetOrigin();
float GetViewportLength();
float GetCompositionLength();
float GetPageStart();
float GetPageLength();
float GetViewportEnd();
float GetCompositionEnd();
float GetPageEnd();
virtual float GetPointOffset(const gfx::Point& aPoint) = 0;

View File

@ -467,8 +467,8 @@ private:
}
const FrameMetrics& fm = c->GetFrameMetrics();
gfx3DMatrix m(aContainer->GetTransform());
m.Translate(gfxPoint3D(-fm.mViewportScrollOffset.x,
-fm.mViewportScrollOffset.y, 0));
m.Translate(gfxPoint3D(-fm.GetScrollOffsetInLayerPixels().x,
-fm.GetScrollOffsetInLayerPixels().y, 0));
// The transform already takes the resolution scale into account. Since we
// will apply the resolution scale again when computing the effective
@ -787,30 +787,43 @@ CompositorParent::TransformShadowTree(TimeStamp aCurrentFrame)
float rootScaleX = rootTransform.GetXScale(),
rootScaleY = rootTransform.GetYScale();
// The ratio of layers pixels to device pixels. The Java
// compositor wants to see values in units of device pixels, so we
// map our FrameMetrics values to that space. This is not exposed
// as a FrameMetrics helper because it's a deprecated conversion.
float devPixelRatioX = 1 / rootScaleX, devPixelRatioY = 1 / rootScaleY;
gfx::Point scrollOffsetLayersPixels(metrics.GetScrollOffsetInLayerPixels());
nsIntPoint scrollOffsetDevPixels(
NS_lround(scrollOffsetLayersPixels.x * devPixelRatioX),
NS_lround(scrollOffsetLayersPixels.y * devPixelRatioY));
if (mIsFirstPaint) {
mContentRect = metrics.mContentRect;
const gfx::Point& scrollOffset = metrics.mViewportScrollOffset;
SetFirstPaintViewport(nsIntPoint(NS_lround(scrollOffset.x),
NS_lround(scrollOffset.y)),
SetFirstPaintViewport(scrollOffsetDevPixels,
1/rootScaleX,
mContentRect,
metrics.mCSSContentRect);
metrics.mScrollableRect);
mIsFirstPaint = false;
} else if (!metrics.mContentRect.IsEqualEdges(mContentRect)) {
mContentRect = metrics.mContentRect;
SetPageRect(metrics.mCSSContentRect);
SetPageRect(metrics.mScrollableRect);
}
// We synchronise the viewport information with Java after sending the above
// notifications, so that Java can take these into account in its response.
// Calculate the absolute display port to send to Java
nsIntRect displayPort = metrics.mDisplayPort;
gfx::Point scrollOffset = metrics.mViewportScrollOffset;
displayPort.x += NS_lround(scrollOffset.x);
displayPort.y += NS_lround(scrollOffset.y);
gfx::Rect displayPortLayersPixels(metrics.mDisplayPort);
nsIntRect displayPortDevPixels(
NS_lround(displayPortLayersPixels.x * devPixelRatioX),
NS_lround(displayPortLayersPixels.y * devPixelRatioY),
NS_lround(displayPortLayersPixels.width * devPixelRatioX),
NS_lround(displayPortLayersPixels.height * devPixelRatioY));
SyncViewportInfo(displayPort, 1/rootScaleX, mLayersUpdated,
displayPortDevPixels.x += scrollOffsetDevPixels.x;
displayPortDevPixels.y += scrollOffsetDevPixels.y;
SyncViewportInfo(displayPortDevPixels, 1/rootScaleX, mLayersUpdated,
mScrollOffset, mXScale, mYScale);
mLayersUpdated = false;
@ -825,8 +838,7 @@ CompositorParent::TransformShadowTree(TimeStamp aCurrentFrame)
nsIntPoint metricsScrollOffset(0, 0);
if (metrics.IsScrollable()) {
metricsScrollOffset =
nsIntPoint(NS_lround(scrollOffset.x), NS_lround(scrollOffset.y));
metricsScrollOffset = scrollOffsetDevPixels;
}
nsIntPoint scrollCompensation(

View File

@ -37,7 +37,9 @@ ReusableTileStoreOGL::InvalidateTiles(TiledThebesLayerOGL* aLayer,
// This will be incorrect when the transform involves rotation, but
// it'd be quite hard to retain invalid tiles correctly in this
// situation anyway.
renderBounds = parent->GetEffectiveTransform().TransformBounds(gfxRect(metrics.mDisplayPort));
renderBounds = parent->GetEffectiveTransform().TransformBounds(
gfxRect(metrics.mDisplayPort.x, metrics.mDisplayPort.y,
metrics.mDisplayPort.width, metrics.mDisplayPort.height));
break;
}
}
@ -217,10 +219,14 @@ ReusableTileStoreOGL::DrawTiles(TiledThebesLayerOGL* aLayer,
scrollableLayer = parent;
if (!parentMetrics.mDisplayPort.IsEmpty() && scrollableLayer) {
displayPort = parent->GetEffectiveTransform().
TransformBounds(gfxRect(parentMetrics.mDisplayPort));
TransformBounds(gfxRect(
parentMetrics.mDisplayPort.x, parentMetrics.mDisplayPort.y,
parentMetrics.mDisplayPort.width, parentMetrics.mDisplayPort.height));
const FrameMetrics& metrics = scrollableLayer->GetFrameMetrics();
const nsIntSize& contentSize = metrics.mContentRect.Size();
const gfx::Point& scrollOffset = metrics.mViewportScrollOffset;
gfx::Point scrollOffset =
gfx::Point(metrics.mScrollOffset.x * metrics.LayersPixelsPerCSSPixel().width,
metrics.mScrollOffset.y * metrics.LayersPixelsPerCSSPixel().height);
const nsIntPoint& contentOrigin = metrics.mContentRect.TopLeft() -
nsIntPoint(NS_lround(scrollOffset.x), NS_lround(scrollOffset.y));
gfxRect contentRect = gfxRect(contentOrigin.x, contentOrigin.y,

View File

@ -473,12 +473,14 @@ const SkPMColor* SkGradientShaderBase::getCache32() const {
}
// Write the clamp colours into the first and last entries of fCache32
fCache32[kCache32ClampLower] = SkPackARGB32(fCacheAlpha,
fCache32[kCache32ClampLower] = SkPremultiplyARGBInline(SkMulDiv255Round(SkColorGetA(fOrigColors[0]),
fCacheAlpha),
SkColorGetR(fOrigColors[0]),
SkColorGetG(fOrigColors[0]),
SkColorGetB(fOrigColors[0]));
fCache32[kCache32ClampUpper] = SkPackARGB32(fCacheAlpha,
fCache32[kCache32ClampUpper] = SkPremultiplyARGBInline(SkMulDiv255Round(SkColorGetA(fOrigColors[fColorCount - 1]),
fCacheAlpha),
SkColorGetR(fOrigColors[fColorCount - 1]),
SkColorGetG(fOrigColors[fColorCount - 1]),
SkColorGetB(fOrigColors[fColorCount - 1]));

View File

@ -960,25 +960,31 @@ struct ParamTraits<mozilla::layers::FrameMetrics>
static void Write(Message* aMsg, const paramType& aParam)
{
WriteParam(aMsg, aParam.mCSSContentRect);
WriteParam(aMsg, aParam.mScrollableRect);
WriteParam(aMsg, aParam.mViewport);
WriteParam(aMsg, aParam.mContentRect);
WriteParam(aMsg, aParam.mViewportScrollOffset);
WriteParam(aMsg, aParam.mScrollOffset);
WriteParam(aMsg, aParam.mDisplayPort);
WriteParam(aMsg, aParam.mCompositionBounds);
WriteParam(aMsg, aParam.mScrollId);
WriteParam(aMsg, aParam.mResolution);
WriteParam(aMsg, aParam.mZoom);
WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel);
WriteParam(aMsg, aParam.mMayHaveTouchListeners);
}
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
{
return (ReadParam(aMsg, aIter, &aResult->mCSSContentRect) &&
return (ReadParam(aMsg, aIter, &aResult->mScrollableRect) &&
ReadParam(aMsg, aIter, &aResult->mViewport) &&
ReadParam(aMsg, aIter, &aResult->mContentRect) &&
ReadParam(aMsg, aIter, &aResult->mViewportScrollOffset) &&
ReadParam(aMsg, aIter, &aResult->mScrollOffset) &&
ReadParam(aMsg, aIter, &aResult->mDisplayPort) &&
ReadParam(aMsg, aIter, &aResult->mCompositionBounds) &&
ReadParam(aMsg, aIter, &aResult->mScrollId) &&
ReadParam(aMsg, aIter, &aResult->mResolution) &&
ReadParam(aMsg, aIter, &aResult->mZoom) &&
ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) &&
ReadParam(aMsg, aIter, &aResult->mMayHaveTouchListeners));
}
};

View File

@ -5914,24 +5914,7 @@ IonBuilder::jsop_getprop(HandlePropertyName name)
return makeCallBarrier(getter, 0, false, types, barrier);
}
// If the input is guaranteed to be an object, then we want
// to specialize it via an slot load or an IC. If it's
// guaranteed to be an object or NULL, then we do the same
// thing except prefixed by a fallible unbox.
bool targetIsObject = (unary.ival == MIRType_Object);
if (!targetIsObject) {
if (unaryTypes.inTypes->objectOrSentinel()) {
// Fallibly unwrap the object before getprop. Getprop
// on null or undefined will cause exception anyway.
MUnbox *unbox = MUnbox::New(obj, MIRType_Object, MUnbox::Fallible);
current->add(unbox);
obj = unbox;
targetIsObject = true;
}
}
if (targetIsObject) {
if (unary.ival == MIRType_Object) {
MIRType rvalType = MIRType_Value;
if (!barrier && !IsNullOrUndefined(unary.rval))
rvalType = unary.rval;

View File

@ -373,16 +373,6 @@ TypeInferenceOracle::elementReadGeneric(JSScript *script, jsbytecode *pc, bool *
*cacheable = (obj == MIRType_Object &&
(id == MIRType_Value || id == MIRType_Int32 || id == MIRType_String));
// Turn off cacheing if the element is int32 and we've seen non-native objects as the target
// of this getelem.
if (*cacheable) {
if (id == MIRType_Int32) {
if (script->analysis()->getCode(pc).nonNativeGetElement)
*cacheable = false;
}
}
if (*cacheable)
*monitorResult = (id == MIRType_String || script->analysis()->getCode(pc).getStringElement);
else

View File

@ -113,7 +113,6 @@ class Bytecode
*/
bool arrayWriteHole: 1; /* SETELEM which has written to an array hole. */
bool getStringElement:1; /* GETELEM which has accessed string properties. */
bool nonNativeGetElement:1; /* GETELEM on a non-native object. */
bool accessGetter: 1; /* Property read on a shape with a getter hook. */
bool notIdempotent: 1; /* Don't use an idempotent cache for this property read. */

View File

@ -571,19 +571,6 @@ class StackTypeSet : public TypeSet
/* Whether this value may be an object. */
bool maybeObject() { return unknownObject() || baseObjectCount() > 0; }
/*
* Whether this typeset represents a potentially sentineled object value:
* where the value may be an object, but maybe potentially null or undefined.
* This returns false in situations where the value cannot ever be an object.
*/
bool objectOrSentinel() {
TypeFlags flags = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_ANYOBJECT;
if (baseFlags() & (~flags & TYPE_FLAG_BASE_MASK))
return false;
return hasAnyFlag(TYPE_FLAG_ANYOBJECT) || baseObjectCount() > 0;
}
/* Whether the type set contains objects with any of a set of flags. */
bool hasObjectFlags(JSContext *cx, TypeObjectFlags flags);

View File

@ -686,19 +686,6 @@ GetObjectElementOperation(JSContext *cx, JSOp op, HandleObject obj, const Value
}
#endif
bool updateAnalysis = false;
RootedScript script(cx, NULL);
jsbytecode *pc = NULL;
if (!cx->fp()->beginsIonActivation()) {
// Don't call GetPcScript from inside Ion since it's expensive.
types::TypeScript::GetPcScript(cx, &script, &pc);
if (script->hasAnalysis())
updateAnalysis = true;
}
if (updateAnalysis && !obj->isNative())
script->analysis()->getCode(pc).nonNativeGetElement = true;
uint32_t index;
if (IsDefinitelyIndex(rref, &index)) {
do {
@ -716,8 +703,16 @@ GetObjectElementOperation(JSContext *cx, JSOp op, HandleObject obj, const Value
return false;
} while(0);
} else {
if (updateAnalysis)
if (!cx->fp()->beginsIonActivation()) {
// Don't update getStringElement if called from Ion code, since
// ion::GetPcScript is expensive.
RootedScript script(cx);
jsbytecode *pc;
types::TypeScript::GetPcScript(cx, &script, &pc);
if (script->hasAnalysis())
script->analysis()->getCode(pc).getStringElement = true;
}
SpecialId special;
res.set(rref);

View File

@ -21,7 +21,8 @@ dictionaries = [
[ 'CameraRegion', 'nsIDOMCameraManager.idl' ],
[ 'CameraPosition', 'nsIDOMCameraManager.idl' ],
[ 'CameraSelector', 'nsIDOMCameraManager.idl' ],
[ 'CameraPictureOptions', 'nsIDOMCameraManager.idl' ]
[ 'CameraPictureOptions', 'nsIDOMCameraManager.idl' ],
[ 'CameraRecordingOptions', 'nsIDOMCameraManager.idl' ]
]
# include file names

View File

@ -575,6 +575,7 @@ static void RecordFrameMetrics(nsIFrame* aForFrame,
bool aMayHaveTouchListeners) {
nsPresContext* presContext = aForFrame->PresContext();
int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
float auPerCSSPixel = nsPresContext::AppUnitsPerCSSPixel();
nsIntRect visible = aVisibleRect.ScaleToNearestPixels(
aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel);
@ -582,12 +583,18 @@ static void RecordFrameMetrics(nsIFrame* aForFrame,
FrameMetrics metrics;
metrics.mViewport = aViewport.ScaleToNearestPixels(
aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel);
metrics.mViewport = mozilla::gfx::Rect(
NSAppUnitsToDoublePixels(aViewport.x, auPerDevPixel),
NSAppUnitsToDoublePixels(aViewport.y, auPerDevPixel),
NSAppUnitsToDoublePixels(aViewport.width, auPerDevPixel),
NSAppUnitsToDoublePixels(aViewport.height, auPerDevPixel));
if (aDisplayPort) {
metrics.mDisplayPort = aDisplayPort->ScaleToNearestPixels(
aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel);
metrics.mDisplayPort = mozilla::gfx::Rect(
NSAppUnitsToDoublePixels(aDisplayPort->x, auPerDevPixel),
NSAppUnitsToDoublePixels(aDisplayPort->y, auPerDevPixel),
NSAppUnitsToDoublePixels(aDisplayPort->width, auPerDevPixel),
NSAppUnitsToDoublePixels(aDisplayPort->height, auPerDevPixel));
}
nsIScrollableFrame* scrollableFrame = nullptr;
@ -598,7 +605,7 @@ static void RecordFrameMetrics(nsIFrame* aForFrame,
nsRect contentBounds = scrollableFrame->GetScrollRange();
contentBounds.width += scrollableFrame->GetScrollPortRect().width;
contentBounds.height += scrollableFrame->GetScrollPortRect().height;
metrics.mCSSContentRect =
metrics.mScrollableRect =
mozilla::gfx::Rect(nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.x),
nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.y),
nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.width),
@ -606,13 +613,13 @@ static void RecordFrameMetrics(nsIFrame* aForFrame,
metrics.mContentRect = contentBounds.ScaleToNearestPixels(
aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel);
nsPoint scrollPosition = scrollableFrame->GetScrollPosition();
metrics.mViewportScrollOffset = mozilla::gfx::Point(
NSAppUnitsToDoublePixels(scrollPosition.x, auPerDevPixel) * aContainerParameters.mXScale,
NSAppUnitsToDoublePixels(scrollPosition.y, auPerDevPixel) * aContainerParameters.mYScale);
metrics.mScrollOffset = mozilla::gfx::Point(
NSAppUnitsToDoublePixels(scrollPosition.x, auPerCSSPixel),
NSAppUnitsToDoublePixels(scrollPosition.y, auPerCSSPixel));
}
else {
nsRect contentBounds = aForFrame->GetRect();
metrics.mCSSContentRect =
metrics.mScrollableRect =
mozilla::gfx::Rect(nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.x),
nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.y),
nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.width),
@ -626,6 +633,8 @@ static void RecordFrameMetrics(nsIFrame* aForFrame,
nsIPresShell* presShell = presContext->GetPresShell();
metrics.mResolution = gfxSize(presShell->GetXResolution(), presShell->GetYResolution());
metrics.mDevPixelsPerCSSPixel = auPerCSSPixel / auPerDevPixel;
metrics.mMayHaveTouchListeners = aMayHaveTouchListeners;
aRoot->SetFrameMetrics(metrics);
@ -1070,8 +1079,10 @@ void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder,
}
}
nsRect viewport(aBuilder->ToReferenceFrame(aForFrame), aForFrame->GetSize());
RecordFrameMetrics(aForFrame, rootScrollFrame,
root, mVisibleRect, mVisibleRect,
root, mVisibleRect, viewport,
(usingDisplayport ? &displayport : nullptr), id,
containerParameters, mayHaveTouchListeners);
if (usingDisplayport &&

View File

@ -158,7 +158,7 @@ ComputeShadowTreeTransform(nsIFrame* aContainerFrame,
nsIntPoint scrollOffset =
aConfig.mScrollOffset.ToNearestPixels(auPerDevPixel);
// metricsScrollOffset is in layer coordinates.
gfx::Point metricsScrollOffset = aMetrics->mViewportScrollOffset;
gfx::Point metricsScrollOffset = aMetrics->GetScrollOffsetInLayerPixels();
nsIntPoint roundedMetricsScrollOffset =
nsIntPoint(NS_lround(metricsScrollOffset.x), NS_lround(metricsScrollOffset.y));
@ -217,7 +217,9 @@ BuildListForLayer(Layer* aLayer,
nsRect bounds;
{
nscoord auPerDevPixel = aSubdocFrame->PresContext()->AppUnitsPerDevPixel();
bounds = metrics->mViewport.ToAppUnits(auPerDevPixel);
gfx::Rect viewport = metrics->mViewport;
bounds = nsIntRect(viewport.x, viewport.y,
viewport.width, viewport.height).ToAppUnits(auPerDevPixel);
ApplyTransform(bounds, tmpTransform, auPerDevPixel);
}
@ -362,6 +364,7 @@ BuildViewMap(ViewMap& oldContentViews, ViewMap& newContentViews,
if (metrics.IsScrollable()) {
nscoord auPerDevPixel = aFrameLoader->GetPrimaryFrameOfOwningContent()
->PresContext()->AppUnitsPerDevPixel();
nscoord auPerCSSPixel = auPerDevPixel * metrics.mDevPixelsPerCSSPixel;
nsContentView* view = FindViewForId(oldContentViews, scrollId);
if (view) {
// View already exists. Be sure to propagate scales for any values
@ -392,8 +395,8 @@ BuildViewMap(ViewMap& oldContentViews, ViewMap& newContentViews,
// The default scale is 1, so no need to propagate scale down.
ViewConfig config;
config.mScrollOffset = nsPoint(
NSIntPixelsToAppUnits(metrics.mViewportScrollOffset.x, auPerDevPixel) * aXScale,
NSIntPixelsToAppUnits(metrics.mViewportScrollOffset.y, auPerDevPixel) * aYScale);
NSIntPixelsToAppUnits(metrics.mScrollOffset.x, auPerCSSPixel) * aXScale,
NSIntPixelsToAppUnits(metrics.mScrollOffset.y, auPerCSSPixel) * aYScale);
view = new nsContentView(aFrameLoader, scrollId, config);
view->mParentScaleX = aAccConfigXScale;
view->mParentScaleY = aAccConfigYScale;
@ -728,7 +731,8 @@ void
RenderFrameParent::NotifyDimensionsChanged(int width, int height)
{
if (mPanZoomController) {
mPanZoomController->UpdateViewportSize(width, height);
mPanZoomController->UpdateCompositionBounds(
nsIntRect(0, 0, width, height));
}
}
@ -909,6 +913,14 @@ RenderFrameParent::ContentReceivedTouch(bool aPreventDefault)
}
}
void
RenderFrameParent::UpdateZoomConstraints(bool aAllowZoom, float aMinZoom, float aMaxZoom)
{
if (mPanZoomController) {
mPanZoomController->UpdateZoomConstraints(aAllowZoom, aMinZoom, aMaxZoom);
}
}
} // namespace layout
} // namespace mozilla

View File

@ -101,6 +101,8 @@ public:
void ContentReceivedTouch(bool aPreventDefault);
void UpdateZoomConstraints(bool aAllowZoom, float aMinZoom, float aMaxZoom);
protected:
void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;

View File

@ -156,6 +156,7 @@ abstract public class GeckoApp
public static int mOrientation;
private boolean mIsRestoringActivity;
private String mCurrentResponse = "";
public static boolean sIsUsingCustomProfile = false;
private PromptService mPromptService;
private Favicons mFavicons;
@ -1554,6 +1555,7 @@ abstract public class GeckoApp
if (profileName == null)
profileName = "default";
}
GeckoApp.sIsUsingCustomProfile = true;
}
if (profileName != null || profilePath != null) {
mProfile = GeckoProfile.get(this, profileName, profilePath);
@ -2252,7 +2254,8 @@ abstract public class GeckoApp
ProfileMigrator profileMigrator = new ProfileMigrator(app);
// Do a migration run on the first start after an upgrade.
if (!profileMigrator.hasMigrationRun()) {
if (!GeckoApp.sIsUsingCustomProfile &&
!profileMigrator.hasMigrationRun()) {
// Show the "Setting up Fennec" screen if this takes
// a while.
final SetupScreen setupScreen = new SetupScreen(app);
@ -2296,7 +2299,7 @@ abstract public class GeckoApp
private void checkMigrateSync() {
final File profileDir = getProfile().getDir();
if (profileDir != null) {
if (!GeckoApp.sIsUsingCustomProfile && profileDir != null) {
final GeckoApp app = GeckoApp.mAppContext;
ProfileMigrator profileMigrator = new ProfileMigrator(app);
if (!profileMigrator.hasSyncMigrated()) {

View File

@ -148,7 +148,8 @@ public final class GeckoProfile {
try {
// Check for old profiles that may need migration.
ProfileMigrator profileMigrator = new ProfileMigrator(mContext);
if (!profileMigrator.isProfileMoved()) {
if (!GeckoApp.sIsUsingCustomProfile &&
!profileMigrator.isProfileMoved()) {
Log.i(LOGTAG, "New installation or update, checking for old profiles.");
profileMigrator.launchMoveProfile();
}

View File

@ -687,6 +687,15 @@ nsDNSService::Resolve(const nsACString &hostname,
uint32_t flags,
nsIDNSRecord **result)
{
NS_WARNING("Do not use synchronous DNS resolution! This API may be removed soon.");
// We will not allow this to be called on the main thread. This is transitional
// and a bit of a test for removing the synchronous API entirely.
if (NS_IsMainThread()) {
NS_ERROR("Synchronous DNS resolve failing - not allowed on the main thread!");
return NS_ERROR_FAILURE;
}
// grab reference to global host resolver and IDN service. beware
// simultaneous shutdown!!
nsRefPtr<nsHostResolver> res;

View File

@ -59,9 +59,8 @@ interface nsIDNSService : nsISupports
in nsresult aReason);
/**
* called to synchronously resolve a hostname. warning this method may
* block the calling thread for a long period of time. it is extremely
* unwise to call this function on the UI thread of an application.
* Called to synchronously resolve a hostname. This method will fail
* if called from the main thread.
*
* @param aHostName
* the hostname or IP-address-literal to resolve.

View File

@ -118,6 +118,10 @@ ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
STATIC_LIBS += moznetd_s
endif
ifdef MOZ_B2G_CAMERA #{
OS_LIBS += -lstagefright -lstagefright_omx
endif #}
ifdef MOZ_IPDL_TESTS
STATIC_LIBS += ipdlunittest_s
endif