Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2016-10-05 17:07:19 +02:00
Родитель 38abad8db7 3522f1d38a
Коммит 750b23e14d
139 изменённых файлов: 1891 добавлений и 3141 удалений

Просмотреть файл

@ -133,8 +133,9 @@ this.PermissionPromptPrototype = {
* PopupNotification when it is shown. See the documentation
* for PopupNotification for more details.
*
* Note that prompt() will automatically set displayURI to
* be the URI of the requesting pricipal.
* Note that prompt() will automatically set displayURI to
* be the URI of the requesting pricipal, unless the displayURI is exactly
* set to false.
*/
get popupOptions() {
return {};
@ -329,7 +330,10 @@ this.PermissionPromptPrototype = {
let secondaryActions = popupNotificationActions.splice(1);
let options = this.popupOptions;
options.displayURI = this.principal.URI;
if (!options.hasOwnProperty('displayURI') || options.displayURI) {
options.displayURI = this.principal.URI;
}
this.onBeforeShow();
chromeWin.PopupNotifications.show(this.browser,

Просмотреть файл

@ -157,6 +157,55 @@ add_task(function* test_permission_prompt_for_request() {
});
});
/**
* Tests that if the PermissionPrompt sets displayURI to false in popupOptions,
* then there is no URI shown on the popupnotification.
*/
add_task(function* test_permission_prompt_for_popupOptions() {
yield BrowserTestUtils.withNewTab({
gBrowser,
url: "http://example.com/",
}, function*(browser) {
const kTestNotificationID = "test-notification";
const kTestMessage = "Test message";
let mainAction = {
label: "Main",
accessKey: "M",
};
let secondaryAction = {
label: "Secondary",
accessKey: "S",
};
let mockRequest = makeMockPermissionRequest(browser);
let TestPrompt = {
__proto__: PermissionUI.PermissionPromptForRequestPrototype,
request: mockRequest,
notificationID: kTestNotificationID,
message: kTestMessage,
promptActions: [mainAction, secondaryAction],
popupOptions: {
displayURI: false,
},
};
let shownPromise =
BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
TestPrompt.prompt();
yield shownPromise;
let notification =
PopupNotifications.getNotification(kTestNotificationID, browser);
Assert.ok(!notification.options.displayURI,
"Should not show the URI of the requesting page");
let removePromise =
BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popuphidden");
notification.remove();
yield removePromise;
});
});
/**
* Tests that if the PermissionPrompt has the permissionKey
* set that permissions can be set properly by the user. Also

Просмотреть файл

@ -803,11 +803,6 @@ def js_option(*args, **kwargs):
add_old_configure_arg(js_option)
include('pkg.configure')
# Make this assignment here rather than in pkg.configure to avoid
# requiring this file in unit tests.
add_old_configure_assignment('PKG_CONFIG', pkg_config)
# Bug 1278542: This function is a workaround to resolve
# |android_ndk_include|'s dependency on 'gonkdir.' The
# actual implementation is located in b2g/moz.configure.

Просмотреть файл

@ -4,7 +4,12 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
pkg_config = check_prog('PKG_CONFIG', ('pkg-config',), allow_missing=True)
@depends('--enable-compile-environment')
def pkg_config(compile_env):
if compile_env:
return ('pkg-config',)
pkg_config = check_prog('PKG_CONFIG', pkg_config, allow_missing=True)
@depends_if(pkg_config)
@checking('for pkg-config version')
@ -31,7 +36,12 @@ def pkg_check_modules(var, package_desc, when=always,
package_desc = ' '.join(package_desc)
package_desc = dependable(package_desc)
@depends_when(pkg_config, pkg_config_version, when=when)
@depends(when, '--enable-compile-environment')
def when_and_compile_environment(when, compile_environment):
return when and compile_environment
@depends_when(pkg_config, pkg_config_version,
when=when_and_compile_environment)
def check_pkg_config(pkg_config, version):
min_version = '0.9.0'
if pkg_config is None:
@ -42,7 +52,7 @@ def pkg_check_modules(var, package_desc, when=always,
die("*** Your version of pkg-config is too old. You need version %s or newer.",
min_version)
@depends_when(pkg_config, package_desc, when=when)
@depends_when(pkg_config, package_desc, when=when_and_compile_environment)
@imports('subprocess')
@imports('sys')
@imports(_from='mozbuild.configure.util', _import='LineIO')

Просмотреть файл

@ -144,7 +144,7 @@ var DevToolsUtils = require("devtools/shared/DevToolsUtils");
var promise = require("devtools/shared/deprecated-sync-thenables");
var Editor = require("devtools/client/sourceeditor/editor");
var DebuggerEditor = require("devtools/client/sourceeditor/debugger");
var Tooltip = require("devtools/client/shared/widgets/Tooltip");
var Tooltip = require("devtools/client/shared/widgets/tooltip/Tooltip");
var FastListWidget = require("devtools/client/shared/widgets/FastListWidget");
var {LocalizationHelper, ELLIPSIS} = require("devtools/shared/l10n");
var {PrefsHelper} = require("devtools/client/shared/prefs");

Просмотреть файл

@ -37,7 +37,7 @@ const {HTMLEditor} = require("devtools/client/inspector/markup/html-editor");
const promise = require("promise");
const defer = require("devtools/shared/defer");
const Services = require("Services");
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
const {setImageTooltip, setBrokenImageTooltip} =
require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
const {setEventTooltip} = require("devtools/client/shared/widgets/tooltip/EventTooltipHelper");

Просмотреть файл

@ -13,7 +13,7 @@
// - etc.
const {getColor} = require("devtools/client/shared/theme");
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
const {
getImageDimensions,
setImageTooltip,

Просмотреть файл

@ -22,7 +22,7 @@ const {SideMenuWidget} = require("resource://devtools/client/shared/widgets/Side
const {VariablesView} = require("resource://devtools/client/shared/widgets/VariablesView.jsm");
const {VariablesViewController} = require("resource://devtools/client/shared/widgets/VariablesViewController.jsm");
const {ToolSidebar} = require("devtools/client/framework/sidebar");
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
const {setImageTooltip, getImageDimensions} =
require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
const { testing: isTesting } = require("devtools/shared/flags");

Просмотреть файл

@ -11,7 +11,7 @@ const {SideMenuWidget} = require("resource://devtools/client/shared/widgets/Side
const promise = require("promise");
const Services = require("Services");
const EventEmitter = require("devtools/shared/event-emitter");
const Tooltip = require("devtools/client/shared/widgets/Tooltip");
const Tooltip = require("devtools/client/shared/widgets/tooltip/Tooltip");
const Editor = require("devtools/client/sourceeditor/editor");
const {LocalizationHelper} = require("devtools/shared/l10n");
const {Heritage, WidgetMethods, setNamedTimeout} =

Просмотреть файл

@ -8,7 +8,7 @@
const HTML_NS = "http://www.w3.org/1999/xhtml";
const Services = require("Services");
const {gDevTools} = require("devtools/client/framework/devtools");
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
const EventEmitter = require("devtools/shared/event-emitter");
let itemIdCounter = 0;

Просмотреть файл

@ -22,7 +22,7 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
let useXulWrapper;

Просмотреть файл

@ -24,7 +24,7 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
let useXulWrapper;

Просмотреть файл

@ -28,7 +28,7 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
let useXulWrapper;

Просмотреть файл

@ -26,7 +26,7 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
const TOOLTIP_HEIGHT = 30;

Просмотреть файл

@ -23,7 +23,7 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
const TOOLTIP_HEIGHT = 200;

Просмотреть файл

@ -46,7 +46,7 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
let useXulWrapper;

Просмотреть файл

@ -40,7 +40,7 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
let useXulWrapper;

Просмотреть файл

@ -23,7 +23,7 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
function getTooltipContent(doc) {

Просмотреть файл

@ -23,7 +23,7 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
add_task(function* () {

Просмотреть файл

@ -23,7 +23,7 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
add_task(function* () {

Просмотреть файл

@ -25,7 +25,7 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
</hbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
const TOOLBOX_WIDTH = 500;

Просмотреть файл

@ -26,7 +26,7 @@ const CONTAINER_HEIGHT = 300;
const CONTAINER_WIDTH = 200;
const TOOLTIP_HEIGHT = 50;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
add_task(function* () {

Просмотреть файл

@ -22,7 +22,7 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
let useXulWrapper;

Просмотреть файл

@ -22,7 +22,7 @@ const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
// The test toolbox will be 200px tall, the anchors are 50px tall, therefore, the maximum

Просмотреть файл

@ -20,7 +20,6 @@ DevToolsModules(
'FlameGraph.js',
'Graphs.js',
'GraphsWorker.js',
'HTMLTooltip.js',
'LineGraphWidget.js',
'MdnDocsWidget.js',
'MountainGraphWidget.js',
@ -28,7 +27,6 @@ DevToolsModules(
'SimpleListWidget.jsm',
'Spectrum.js',
'TableWidget.js',
'Tooltip.js',
'TreeWidget.js',
'VariablesView.jsm',
'VariablesViewController.jsm',

Просмотреть файл

@ -4,7 +4,7 @@
"use strict";
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
const {MdnDocsWidget} = require("devtools/client/shared/widgets/MdnDocsWidget");
const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
const XHTML_NS = "http://www.w3.org/1999/xhtml";

Просмотреть файл

@ -6,7 +6,7 @@
const EventEmitter = require("devtools/shared/event-emitter");
const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
/**
* Base class for all (color, gradient, ...)-swatch based value editors inside

Просмотреть файл

@ -7,11 +7,13 @@
DevToolsModules(
'CssDocsTooltip.js',
'EventTooltipHelper.js',
'HTMLTooltip.js',
'ImageTooltipHelper.js',
'SwatchBasedEditorTooltip.js',
'SwatchColorPickerTooltip.js',
'SwatchCubicBezierTooltip.js',
'SwatchFilterTooltip.js',
'Tooltip.js',
'TooltipToggle.js',
'VariableContentHelper.js',
)

Просмотреть файл

@ -306,7 +306,7 @@ div.CodeMirror span.eval-text {
}
}
/* XUL panel styling (see devtools/client/shared/widgets/Tooltip.js) */
/* XUL panel styling (see devtools/client/shared/widgets/tooltip/Tooltip.js) */
.theme-tooltip-panel .panel-arrowcontent {
padding: 5px;

Просмотреть файл

@ -318,7 +318,7 @@ div.CodeMirror span.eval-text {
}
}
/* XUL panel styling (see devtools/client/shared/widgets/Tooltip.js) */
/* XUL panel styling (see devtools/client/shared/widgets/tooltip/Tooltip.js) */
.theme-tooltip-panel .panel-arrowcontent {
padding: 4px;

Просмотреть файл

@ -15,7 +15,7 @@
--bezier-grid-color: rgba(0, 0, 0, 0.05);
}
/* Tooltip widget (see devtools/client/shared/widgets/Tooltip.js) */
/* Tooltip widget (see devtools/client/shared/widgets/tooltip/Tooltip.js) */
.devtools-tooltip .panel-arrowcontent {
padding: 4px;

Просмотреть файл

@ -79,14 +79,6 @@ add_task(function* () {
gBrowser.goBack();
info("Waiting for page to navigate");
yield waitForSuccess({
name: "go back",
validator: function () {
return content.location.href == TEST_URI1;
},
});
info("Waiting for messages to be cleared due to navigation");
yield cleared;

Просмотреть файл

@ -29,8 +29,11 @@
info("to call importPackagedApp(" + packagedAppLocation + ")");
ok(!win.UI._busyPromise, "UI is not busy");
let onValidated = waitForUpdate(win, "project-validated");
let onDetails = waitForUpdate(win, "details");
yield winProject.projectList.importPackagedApp(packagedAppLocation);
yield waitForUpdate(win, "project-validated");
yield onValidated;
yield onDetails;
let project = win.AppManager.selectedProject;
is(project.location, packagedAppLocation, "Location is valid");

Просмотреть файл

@ -221,6 +221,12 @@ public:
virtual bool HandleEndOfStream() { return false; }
virtual RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget)
{
MOZ_ASSERT(false, "Can't seek in this state");
return nullptr;
}
protected:
using Master = MediaDecoderStateMachine;
explicit StateObject(Master* aPtr) : mMaster(aPtr) {}
@ -376,6 +382,14 @@ public:
SetState(DECODER_STATE_DECODING_FIRSTFRAME);
return true;
}
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
{
SLOG("Not Enough Data to seek at this stage, queuing seek");
mMaster->mQueuedSeek.RejectIfExists(__func__);
mMaster->mQueuedSeek.mTarget = aTarget;
return mMaster->mQueuedSeek.mPromise.Ensure(__func__);
}
};
class MediaDecoderStateMachine::DormantState
@ -407,6 +421,14 @@ public:
}
return true;
}
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
{
SLOG("Not Enough Data to seek at this stage, queuing seek");
mMaster->mQueuedSeek.RejectIfExists(__func__);
mMaster->mQueuedSeek.mTarget = aTarget;
return mMaster->mQueuedSeek.mPromise.Ensure(__func__);
}
};
class MediaDecoderStateMachine::DecodingFirstFrameState
@ -417,7 +439,22 @@ public:
void Enter() override
{
mMaster->DecodeFirstFrame();
// Handle pending seek.
if (mMaster->mQueuedSeek.Exists() &&
(mMaster->mSentFirstFrameLoadedEvent ||
Reader()->ForceZeroStartTime())) {
mMaster->InitiateSeek(Move(mMaster->mQueuedSeek));
return;
}
// Transition to DECODING if we've decoded first frames.
if (mMaster->mSentFirstFrameLoadedEvent) {
SetState(DECODER_STATE_DECODING);
return;
}
// Dispatch tasks to decode first frames.
mMaster->DispatchDecodeTasksIfNeeded();
}
State GetState() const override
@ -428,22 +465,68 @@ public:
bool HandleAudioDecoded(MediaData* aAudio) override
{
mMaster->Push(aAudio, MediaData::AUDIO_DATA);
mMaster->MaybeFinishDecodeFirstFrame();
MaybeFinishDecodeFirstFrame();
return true;
}
bool HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
{
mMaster->Push(aVideo, MediaData::VIDEO_DATA);
mMaster->MaybeFinishDecodeFirstFrame();
MaybeFinishDecodeFirstFrame();
return true;
}
bool HandleEndOfStream() override
{
mMaster->MaybeFinishDecodeFirstFrame();
MaybeFinishDecodeFirstFrame();
return true;
}
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
{
// Should've transitioned to DECODING in Enter()
// if mSentFirstFrameLoadedEvent is true.
MOZ_ASSERT(!mMaster->mSentFirstFrameLoadedEvent);
if (!Reader()->ForceZeroStartTime()) {
SLOG("Not Enough Data to seek at this stage, queuing seek");
mMaster->mQueuedSeek.RejectIfExists(__func__);
mMaster->mQueuedSeek.mTarget = aTarget;
return mMaster->mQueuedSeek.mPromise.Ensure(__func__);
}
// Since ForceZeroStartTime() is true, we should've transitioned to SEEKING
// in Enter() if there is any queued seek.
MOZ_ASSERT(!mMaster->mQueuedSeek.Exists());
SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
SeekJob seekJob;
seekJob.mTarget = aTarget;
RefPtr<MediaDecoder::SeekPromise> p = seekJob.mPromise.Ensure(__func__);
mMaster->InitiateSeek(Move(seekJob));
return p.forget();
}
private:
// Notify FirstFrameLoaded if having decoded first frames and
// transition to SEEKING if there is any pending seek, or DECODING otherwise.
void MaybeFinishDecodeFirstFrame()
{
MOZ_ASSERT(!mMaster->mSentFirstFrameLoadedEvent);
if ((mMaster->IsAudioDecoding() && mMaster->AudioQueue().GetSize() == 0) ||
(mMaster->IsVideoDecoding() && mMaster->VideoQueue().GetSize() == 0)) {
return;
}
mMaster->FinishDecodeFirstFrame();
if (mMaster->mQueuedSeek.Exists()) {
mMaster->InitiateSeek(Move(mMaster->mQueuedSeek));
} else {
SetState(DECODER_STATE_DECODING);
}
}
};
class MediaDecoderStateMachine::DecodingState
@ -525,6 +608,17 @@ public:
return true;
}
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
{
mMaster->mQueuedSeek.RejectIfExists(__func__);
SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
SeekJob seekJob;
seekJob.mTarget = aTarget;
RefPtr<MediaDecoder::SeekPromise> p = seekJob.mPromise.Ensure(__func__);
mMaster->InitiateSeek(Move(seekJob));
return p.forget();
}
private:
void CheckSlowDecoding(TimeStamp aDecodeStart)
{
@ -573,7 +667,70 @@ class MediaDecoderStateMachine::SeekingState
: public MediaDecoderStateMachine::StateObject
{
public:
explicit SeekingState(Master* aPtr) : StateObject(aPtr) {}
explicit SeekingState(Master* aPtr, SeekJob aSeekJob)
: StateObject(aPtr), mSeekJob(Move(aSeekJob)) {}
void Enter() override
{
// Discard the existing seek task.
mMaster->DiscardSeekTaskIfExist();
mMaster->mSeekTaskRequest.DisconnectIfExists();
// SeekTask will register its callbacks to MediaDecoderReaderWrapper.
mMaster->CancelMediaDecoderReaderWrapperCallback();
// Create a new SeekTask instance for the incoming seek task.
if (mSeekJob.mTarget.IsAccurate() ||
mSeekJob.mTarget.IsFast()) {
mMaster->mSeekTask = new AccurateSeekTask(
mMaster->mDecoderID, OwnerThread(), Reader(), mSeekJob.mTarget,
mMaster->mInfo, mMaster->Duration(), mMaster->GetMediaTime());
} else if (mSeekJob.mTarget.IsNextFrame()) {
mMaster->mSeekTask = new NextFrameSeekTask(
mMaster->mDecoderID, OwnerThread(), Reader(), mSeekJob.mTarget,
mMaster->mInfo, mMaster->Duration(),mMaster->GetMediaTime(),
mMaster->AudioQueue(), mMaster->VideoQueue());
} else {
MOZ_DIAGNOSTIC_ASSERT(false, "Cannot handle this seek task.");
}
// Don't stop playback for a video-only seek since audio is playing.
if (!mSeekJob.mTarget.IsVideoOnly()) {
mMaster->StopPlayback();
}
// mSeekJob.mTarget.mTime might be different from
// mSeekTask->GetSeekTarget().mTime because the seek task might clamp the
// seek target to [0, duration]. We want to update the playback position to
// the clamped value.
mMaster->UpdatePlaybackPositionInternal(
mMaster->mSeekTask->GetSeekTarget().GetTime().ToMicroseconds());
if (mSeekJob.mTarget.mEventVisibility ==
MediaDecoderEventVisibility::Observable) {
mMaster->mOnPlaybackEvent.Notify(MediaEventType::SeekStarted);
}
// Reset our state machine and decoding pipeline before seeking.
if (mMaster->mSeekTask->NeedToResetMDSM()) {
if (mSeekJob.mTarget.IsVideoOnly()) {
mMaster->Reset(TrackInfo::kVideoTrack);
} else {
mMaster->Reset();
}
}
// Do the seek.
mMaster->mSeekTaskRequest.Begin(mMaster->mSeekTask->Seek(mMaster->Duration())
->Then(OwnerThread(), __func__, mMaster,
&MediaDecoderStateMachine::OnSeekTaskResolved,
&MediaDecoderStateMachine::OnSeekTaskRejected));
MOZ_ASSERT(!mMaster->mQueuedSeek.Exists());
MOZ_ASSERT(!mMaster->mCurrentSeek.Exists());
mMaster->mCurrentSeek = Move(mSeekJob);
}
State GetState() const override
{
@ -611,6 +768,20 @@ public:
MOZ_ASSERT(false);
return true;
}
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
{
mMaster->mQueuedSeek.RejectIfExists(__func__);
SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
SeekJob seekJob;
seekJob.mTarget = aTarget;
RefPtr<MediaDecoder::SeekPromise> p = seekJob.mPromise.Ensure(__func__);
mMaster->InitiateSeek(Move(seekJob));
return p.forget();
}
private:
SeekJob mSeekJob;
};
class MediaDecoderStateMachine::BufferingState
@ -712,6 +883,17 @@ public:
return true;
}
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
{
mMaster->mQueuedSeek.RejectIfExists(__func__);
SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
SeekJob seekJob;
seekJob.mTarget = aTarget;
RefPtr<MediaDecoder::SeekPromise> p = seekJob.mPromise.Ensure(__func__);
mMaster->InitiateSeek(Move(seekJob));
return p.forget();
}
private:
TimeStamp mBufferingStart;
};
@ -780,6 +962,17 @@ public:
return DECODER_STATE_COMPLETED;
}
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
{
mMaster->mQueuedSeek.RejectIfExists(__func__);
SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
SeekJob seekJob;
seekJob.mTarget = aTarget;
RefPtr<MediaDecoder::SeekPromise> p = seekJob.mPromise.Ensure(__func__);
mMaster->InitiateSeek(Move(seekJob));
return p.forget();
}
private:
bool mSentPlaybackEndedEvent = false;
};
@ -1249,26 +1442,6 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
mStateObj->HandleEndOfStream();
}
void
MediaDecoderStateMachine::MaybeFinishDecodeFirstFrame()
{
MOZ_ASSERT(OnTaskQueue());
MOZ_ASSERT(!mSentFirstFrameLoadedEvent);
if ((IsAudioDecoding() && AudioQueue().GetSize() == 0) ||
(IsVideoDecoding() && VideoQueue().GetSize() == 0)) {
return;
}
FinishDecodeFirstFrame();
if (mQueuedSeek.Exists()) {
InitiateSeek(Move(mQueuedSeek));
} else {
SetState(DECODER_STATE_DECODING);
}
}
void
MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideo,
TimeStamp aDecodeStartTime)
@ -1576,9 +1749,6 @@ MediaDecoderStateMachine::SetState(State aState)
case DECODER_STATE_DECODING:
mStateObj = MakeUnique<DecodingState>(this);
break;
case DECODER_STATE_SEEKING:
mStateObj = MakeUnique<SeekingState>(this);
break;
case DECODER_STATE_BUFFERING:
mStateObj = MakeUnique<BufferingState>(this);
break;
@ -1730,29 +1900,6 @@ MediaDecoderStateMachine::Shutdown()
->CompletionPromise();
}
void
MediaDecoderStateMachine::DecodeFirstFrame()
{
MOZ_ASSERT(OnTaskQueue());
MOZ_ASSERT(mState == DECODER_STATE_DECODING_FIRSTFRAME);
// Handle pending seek.
if (mQueuedSeek.Exists() &&
(mSentFirstFrameLoadedEvent || mReader->ForceZeroStartTime())) {
InitiateSeek(Move(mQueuedSeek));
return;
}
// Transition to DECODING if we've decoded first frames.
if (mSentFirstFrameLoadedEvent) {
SetState(DECODER_STATE_DECODING);
return;
}
// Dispatch tasks to decode first frames.
DispatchDecodeTasksIfNeeded();
}
void MediaDecoderStateMachine::PlayStateChanged()
{
MOZ_ASSERT(OnTaskQueue());
@ -1841,11 +1988,6 @@ void MediaDecoderStateMachine::VisibilityChanged()
return;
}
// If not playing then there's nothing to do.
if (mPlayState != MediaDecoder::PLAY_STATE_PLAYING) {
return;
}
// Start timer to trigger suspended decoding state when going invisible.
if (!mIsVisible) {
TimeStamp target = TimeStamp::Now() + SuspendBackgroundVideoDelay();
@ -1895,10 +2037,11 @@ void MediaDecoderStateMachine::VisibilityChanged()
MediaDecoderEventVisibility::Suppressed,
true /* aVideoOnly */);
InitiateSeek(Move(seekJob))
->Then(AbstractThread::MainThread(), __func__,
[start, info, hw](){ ReportRecoveryTelemetry(start, info, hw); },
[](){});
RefPtr<MediaDecoder::SeekPromise> p = seekJob.mPromise.Ensure(__func__);
p->Then(AbstractThread::MainThread(), __func__,
[start, info, hw](){ ReportRecoveryTelemetry(start, info, hw); },
[](){});
InitiateSeek(Move(seekJob));
}
}
@ -1949,24 +2092,7 @@ MediaDecoderStateMachine::Seek(SeekTarget aTarget)
MOZ_ASSERT(mDuration.Ref().isSome(), "We should have got duration already");
// Can't seek until the start time is known.
bool hasStartTime = mSentFirstFrameLoadedEvent || mReader->ForceZeroStartTime();
// Can't seek when state is WAIT_FOR_CDM or DORMANT.
bool stateAllowed = mState >= DECODER_STATE_DECODING_FIRSTFRAME;
if (!stateAllowed || !hasStartTime) {
DECODER_LOG("Seek() Not Enough Data to continue at this stage, queuing seek");
mQueuedSeek.RejectIfExists(__func__);
mQueuedSeek.mTarget = aTarget;
return mQueuedSeek.mPromise.Ensure(__func__);
}
mQueuedSeek.RejectIfExists(__func__);
DECODER_LOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
SeekJob seekJob;
seekJob.mTarget = aTarget;
return InitiateSeek(Move(seekJob));
return mStateObj->HandleSeek(aTarget);
}
RefPtr<MediaDecoder::SeekPromise>
@ -2047,69 +2173,17 @@ MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded()
}
}
RefPtr<MediaDecoder::SeekPromise>
void
MediaDecoderStateMachine::InitiateSeek(SeekJob aSeekJob)
{
MOZ_ASSERT(OnTaskQueue());
SetState(DECODER_STATE_SEEKING);
// Discard the existing seek task.
DiscardSeekTaskIfExist();
mSeekTaskRequest.DisconnectIfExists();
// SeekTask will register its callbacks to MediaDecoderReaderWrapper.
CancelMediaDecoderReaderWrapperCallback();
// Create a new SeekTask instance for the incoming seek task.
if (aSeekJob.mTarget.IsAccurate() ||
aSeekJob.mTarget.IsFast()) {
mSeekTask = new AccurateSeekTask(mDecoderID, OwnerThread(),
mReader.get(), aSeekJob.mTarget,
mInfo, Duration(), GetMediaTime());
} else if (aSeekJob.mTarget.IsNextFrame()) {
mSeekTask = new NextFrameSeekTask(mDecoderID, OwnerThread(), mReader.get(),
aSeekJob.mTarget, mInfo, Duration(),
GetMediaTime(), AudioQueue(), VideoQueue());
} else {
MOZ_DIAGNOSTIC_ASSERT(false, "Cannot handle this seek task.");
}
// Don't stop playback for a video-only seek since audio is playing.
if (!aSeekJob.mTarget.IsVideoOnly()) {
StopPlayback();
}
// aSeekJob.mTarget.mTime might be different from
// mSeekTask->GetSeekTarget().mTime because the seek task might clamp the seek
// target to [0, duration]. We want to update the playback position to the
// clamped value.
UpdatePlaybackPositionInternal(mSeekTask->GetSeekTarget().GetTime().ToMicroseconds());
if (aSeekJob.mTarget.mEventVisibility == MediaDecoderEventVisibility::Observable) {
mOnPlaybackEvent.Notify(MediaEventType::SeekStarted);
}
// Reset our state machine and decoding pipeline before seeking.
if (mSeekTask->NeedToResetMDSM()) {
if (aSeekJob.mTarget.IsVideoOnly()) {
Reset(TrackInfo::kVideoTrack);
} else {
Reset();
}
}
// Do the seek.
mSeekTaskRequest.Begin(mSeekTask->Seek(Duration())
->Then(OwnerThread(), __func__, this,
&MediaDecoderStateMachine::OnSeekTaskResolved,
&MediaDecoderStateMachine::OnSeekTaskRejected));
MOZ_ASSERT(!mQueuedSeek.Exists());
MOZ_ASSERT(!mCurrentSeek.Exists());
mCurrentSeek = Move(aSeekJob);
return mCurrentSeek.mPromise.Ensure(__func__);
// Note we can't call SetState(DECODER_STATE_SEEKING) which does nothing
// if we are already in the SEEKING state.
mStateObj->Exit();
mState = DECODER_STATE_SEEKING;
mStateObj = MakeUnique<SeekingState>(this, Move(aSeekJob));
mStateObj->Enter();
}
void

Просмотреть файл

@ -472,9 +472,6 @@ protected:
// If we don't, switch to buffering mode.
void MaybeStartBuffering();
// The entry action of DECODER_STATE_DECODING_FIRSTFRAME.
void DecodeFirstFrame();
// Moves the decoder into the shutdown state, and dispatches an error
// event to the media element. This begins shutting down the decoder.
// The decoder monitor must be held. This is only called on the
@ -489,7 +486,7 @@ protected:
void EnqueueFirstFrameLoadedEvent();
// Clears any previous seeking state and initiates a new seek on the decoder.
RefPtr<MediaDecoder::SeekPromise> InitiateSeek(SeekJob aSeekJob);
void InitiateSeek(SeekJob aSeekJob);
void DispatchAudioDecodeTaskIfNeeded();
void DispatchVideoDecodeTaskIfNeeded();
@ -533,10 +530,6 @@ protected:
// must be held when calling this. Called on the decode thread.
int64_t GetDecodedAudioDuration();
// Notify FirstFrameLoaded if having decoded first frames and
// transition to SEEKING if there is any pending seek, or DECODING otherwise.
void MaybeFinishDecodeFirstFrame();
void FinishDecodeFirstFrame();
// Completes the seek operation, moves onto the next appropriate state.

Просмотреть файл

@ -61,13 +61,16 @@ public:
void DoNotifyFinished()
{
MOZ_ASSERT(NS_IsMainThread());
mFinishPromise.ResolveIfExists(true, __func__);
}
void Forget()
{
MOZ_ASSERT(NS_IsMainThread());
mFinishPromise.ResolveIfExists(true, __func__);
AbstractThread::MainThread()->Dispatch(NS_NewRunnableFunction([this] () {
MOZ_ASSERT(NS_IsMainThread());
mFinishPromise.ResolveIfExists(true, __func__);
}));
MutexAutoLock lock(mMutex);
mStream = nullptr;
}
@ -123,6 +126,7 @@ public:
~DecodedStreamData();
void SetPlaying(bool aPlaying);
MediaEventSource<int64_t>& OnOutput();
void Forget();
/* The following group of fields are protected by the decoder's monitor
* and can be read or written on any thread.
@ -188,7 +192,6 @@ DecodedStreamData::DecodedStreamData(OutputStreamManager* aOutputStreamManager,
DecodedStreamData::~DecodedStreamData()
{
mOutputStreamManager->Disconnect();
mListener->Forget();
mStream->Destroy();
}
@ -207,6 +210,12 @@ DecodedStreamData::SetPlaying(bool aPlaying)
}
}
void
DecodedStreamData::Forget()
{
mListener->Forget();
}
DecodedStream::DecodedStream(AbstractThread* aOwnerThread,
MediaQueue<MediaData>& aAudioQueue,
MediaQueue<MediaData>& aVideoQueue,
@ -363,6 +372,7 @@ DecodedStream::DestroyData(UniquePtr<DecodedStreamData> aData)
mOutputListener.Disconnect();
DecodedStreamData* data = aData.release();
data->Forget();
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
delete data;
});

Просмотреть файл

@ -19,7 +19,6 @@ function runWithMSE(testFunction) {
addLoadEvent(function () {
SpecialPowers.pushPrefEnv({"set": [
[ "media.mediasource.enabled", true ],
[ "media.test.dumpDebugInfo", true ],
]},
bootstrapTest);
});

Просмотреть файл

@ -1545,9 +1545,6 @@ var PARALLEL_TESTS = 2;
// conditions that might not otherwise be encountered on the test data.
var gTestPrefs = [
['media.recorder.max_memory', 1024],
["media.preload.default", 2], // default preload = metadata
["media.preload.auto", 3], // auto preload = enough
["media.test.dumpDebugInfo", true],
];
// When true, we'll loop forever on whatever test we run. Use this to debug
@ -1702,14 +1699,6 @@ function mediaTestCleanup(callback) {
SpecialPowers.exactGC(callback);
}
function setMediaTestsPrefs(callback, extraPrefs) {
var prefs = gTestPrefs;
if (extraPrefs) {
prefs = prefs.concat(extraPrefs);
}
SpecialPowers.pushPrefEnv({"set": prefs}, callback);
}
// B2G emulator and Android 2.3 are condidered slow platforms
function isSlowPlatform() {
return SpecialPowers.Services.appinfo.name == "B2G" || getAndroidVersion() == 10;

Просмотреть файл

@ -28,9 +28,7 @@ function done() {
SimpleTest.finish();
}
addLoadEvent(function() {
setMediaTestsPrefs(run);
});
addLoadEvent(run);
SimpleTest.waitForExplicitFinish();

Просмотреть файл

@ -37,7 +37,7 @@ if (media == null) {
}, {once: true});
}
setMediaTestsPrefs(startTest);
startTest();
}
</script>

Просмотреть файл

@ -476,7 +476,7 @@ var commandsPeerConnectionOfferAnswer = [
}
},
function PC_REMOTE_VERIFY_SDP_AFTER_END_OF_TRICKLE(test) {
if (test.pcRemote.endOfTrickelSdp) {
if (test.pcRemote.endOfTrickleSdp) {
/* In case the endOfTrickleSdp promise is resolved already it will win the
* race because it gets evaluated first. But if endOfTrickleSdp is still
* pending the rejection will win the race. */

Просмотреть файл

@ -1996,8 +1996,6 @@ gfxWindowsPlatform::GetAcceleratedCompositorBackends(nsTArray<LayersBackend>& aB
if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
aBackends.AppendElement(LayersBackend::LAYERS_D3D11);
} else {
NS_WARNING("Direct3D 11-accelerated layers are not supported on this system.");
}
if (gfxConfig::IsEnabled(Feature::D3D9_COMPOSITING) && !gfxPrefs::LayersPreferD3D9()) {

Просмотреть файл

Просмотреть файл

@ -205,3 +205,34 @@ def more_deterministic(value):
return True
set_define('JS_MORE_DETERMINISTIC', more_deterministic)
# CTypes
# =======================================================
@depends(building_js, '--help')
def ctypes_default(building_js, _):
return not building_js
js_option('--enable-ctypes', help='Enable js-ctypes',
default=ctypes_default)
build_ctypes = depends_if('--enable-ctypes')(lambda _: True)
set_config('BUILD_CTYPES', build_ctypes)
set_define('BUILD_CTYPES', build_ctypes)
add_old_configure_assignment('BUILD_CTYPES', build_ctypes)
@depends(build_ctypes, building_js)
def js_has_ctypes(ctypes, js):
if ctypes and js:
return True
set_config('JS_HAS_CTYPES', js_has_ctypes)
set_define('JS_HAS_CTYPES', js_has_ctypes)
add_old_configure_assignment('JS_HAS_CTYPES', js_has_ctypes)
@depends('--enable-ctypes', '--enable-compile-environment', '--help')
def ctypes_and_compile_environment(ctypes, compile_environment, _):
return ctypes and compile_environment
include_when('ffi.configure', when=ctypes_and_compile_environment)

Просмотреть файл

@ -237,7 +237,7 @@ typedef enum JSWhyMagic
JS_WHY_MAGIC_COUNT
} JSWhyMagic;
#if defined(IS_LITTLE_ENDIAN)
#if MOZ_LITTLE_ENDIAN
# if defined(JS_NUNBOX32)
typedef union jsval_layout
{
@ -285,7 +285,7 @@ typedef union jsval_layout
uintptr_t asUIntPtr;
} JSVAL_ALIGNMENT jsval_layout;
# endif /* JS_PUNBOX64 */
#else /* defined(IS_LITTLE_ENDIAN) */
#else /* MOZ_LITTLE_ENDIAN */
# if defined(JS_NUNBOX32)
typedef union jsval_layout
{
@ -331,7 +331,7 @@ typedef union jsval_layout
uintptr_t asUIntPtr;
} JSVAL_ALIGNMENT jsval_layout;
# endif /* JS_PUNBOX64 */
#endif /* defined(IS_LITTLE_ENDIAN) */
#endif /* MOZ_LITTLE_ENDIAN */
JS_STATIC_ASSERT(sizeof(jsval_layout) == 8);

Просмотреть файл

@ -66,7 +66,7 @@ LIRGenerator::visitParameter(MParameter* param)
offset *= sizeof(Value);
#if defined(JS_NUNBOX32)
# if defined(IS_BIG_ENDIAN)
# if MOZ_BIG_ENDIAN
ins->getDef(0)->setOutput(LArgument(offset));
ins->getDef(1)->setOutput(LArgument(offset + 4));
# else

Просмотреть файл

@ -179,7 +179,7 @@ using mozilla::FloatingPoint;
# define PER_SHARED_ARCH DEFINED_ON(ALL_SHARED_ARCH)
#ifdef IS_LITTLE_ENDIAN
#if MOZ_LITTLE_ENDIAN
#define IMM32_16ADJ(X) X << 16
#else
#define IMM32_16ADJ(X) X

Просмотреть файл

@ -48,18 +48,6 @@
/* Define to 1 to perform extra assertions and heap poisoning. */
#undef JS_CRASH_DIAGNOSTICS
/* Define to 1 if the <endian.h> header is present and
useable. See jscpucfg.h. */
#undef JS_HAVE_ENDIAN_H
/* Define to 1 if the <machine/endian.h> header is present and
useable. See jscpucfg.h. */
#undef JS_HAVE_MACHINE_ENDIAN_H
/* Define to 1 if the <sys/isa_defs.h> header is present and
useable. See jscpucfg.h. */
#undef JS_HAVE_SYS_ISA_DEFS_H
/* Define to 1 if SpiderMonkey is in NUNBOX32 mode. */
#undef JS_NUNBOX32

Просмотреть файл

@ -568,7 +568,7 @@ js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp)
const Latin1Char* chars = reinterpret_cast<const Latin1Char*>(xdr->buf.read(length));
atom = AtomizeChars(cx, chars, length);
} else {
#if IS_LITTLE_ENDIAN
#if MOZ_LITTLE_ENDIAN
/* Directly access the little endian chars in the XDR buffer. */
const char16_t* chars = reinterpret_cast<const char16_t*>(xdr->buf.read(length * sizeof(char16_t)));
atom = AtomizeChars(cx, chars, length);
@ -596,7 +596,7 @@ js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp)
atom = AtomizeChars(cx, chars, length);
if (chars != stackChars)
js_free(chars);
#endif /* !IS_LITTLE_ENDIAN */
#endif /* !MOZ_LITTLE_ENDIAN */
}
if (!atom)

Просмотреть файл

@ -7,119 +7,7 @@
#ifndef jscpucfg_h
#define jscpucfg_h
#define JS_HAVE_LONG_LONG
#if defined(_WIN64)
# if defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)
# define IS_LITTLE_ENDIAN 1
# undef IS_BIG_ENDIAN
# else /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */
# error "CPU type is unknown"
# endif /* !(defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)) */
#elif defined(_WIN32)
# ifdef __WATCOMC__
# define HAVE_VA_LIST_AS_ARRAY 1
# endif
# define IS_LITTLE_ENDIAN 1
# undef IS_BIG_ENDIAN
#elif defined(__APPLE__) || defined(__powerpc__) || defined(__ppc__)
# if __LITTLE_ENDIAN__
# define IS_LITTLE_ENDIAN 1
# undef IS_BIG_ENDIAN
# elif __BIG_ENDIAN__
# undef IS_LITTLE_ENDIAN
# define IS_BIG_ENDIAN 1
# endif
#elif defined(JS_HAVE_ENDIAN_H)
# include <endian.h>
/*
* Historically, OSes providing <endian.h> only defined
* __BYTE_ORDER to either __LITTLE_ENDIAN or __BIG_ENDIAN.
* The Austin group decided to standardise <endian.h> in
* POSIX around 2011, expecting it to provide a BYTE_ORDER
* #define set to either LITTLE_ENDIAN or BIG_ENDIAN. We
* should try to cope with both possibilities here.
*/
# if defined(__BYTE_ORDER) || defined(BYTE_ORDER)
# if defined(__BYTE_ORDER)
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define IS_LITTLE_ENDIAN 1
# undef IS_BIG_ENDIAN
# elif __BYTE_ORDER == __BIG_ENDIAN
# undef IS_LITTLE_ENDIAN
# define IS_BIG_ENDIAN 1
# endif
# endif
# if defined(BYTE_ORDER)
# if BYTE_ORDER == LITTLE_ENDIAN
# define IS_LITTLE_ENDIAN 1
# undef IS_BIG_ENDIAN
# elif BYTE_ORDER == BIG_ENDIAN
# undef IS_LITTLE_ENDIAN
# define IS_BIG_ENDIAN 1
# endif
# endif
# else /* !defined(__BYTE_ORDER) */
# error "endian.h does not define __BYTE_ORDER nor BYTE_ORDER. Cannot determine endianness."
# endif
/* BSDs */
#elif defined(JS_HAVE_MACHINE_ENDIAN_H)
# include <sys/types.h>
# include <machine/endian.h>
# if defined(_BYTE_ORDER)
# if _BYTE_ORDER == _LITTLE_ENDIAN
# define IS_LITTLE_ENDIAN 1
# undef IS_BIG_ENDIAN
# elif _BYTE_ORDER == _BIG_ENDIAN
# undef IS_LITTLE_ENDIAN
# define IS_BIG_ENDIAN 1
# endif
# else /* !defined(_BYTE_ORDER) */
# error "machine/endian.h does not define _BYTE_ORDER. Cannot determine endianness."
# endif
#elif defined(JS_HAVE_SYS_ISA_DEFS_H)
# include <sys/isa_defs.h>
# if defined(_BIG_ENDIAN)
# undef IS_LITTLE_ENDIAN
# define IS_BIG_ENDIAN 1
# elif defined(_LITTLE_ENDIAN)
# define IS_LITTLE_ENDIAN 1
# undef IS_BIG_ENDIAN
# else /* !defined(_LITTLE_ENDIAN) */
# error "sys/isa_defs.h does not define _BIG_ENDIAN or _LITTLE_ENDIAN. Cannot determine endianness."
# endif
# if !defined(JS_STACK_GROWTH_DIRECTION)
# if defined(_STACK_GROWS_UPWARD)
# define JS_STACK_GROWTH_DIRECTION (1)
# elif defined(_STACK_GROWS_DOWNWARD)
# define JS_STACK_GROWTH_DIRECTION (-1)
# endif
# endif
#elif defined(__sparc) || defined(__sparc__) || \
defined(_POWER) || defined(__hppa) || \
defined(_MIPSEB) || defined(_BIG_ENDIAN)
/* IA64 running HP-UX will have _BIG_ENDIAN defined.
* IA64 running Linux will have endian.h and be handled above.
*/
# undef IS_LITTLE_ENDIAN
# define IS_BIG_ENDIAN 1
#else /* !defined(__sparc) && !defined(__sparc__) && ... */
# error "Cannot determine endianness of your platform. Please add support to jscpucfg.h."
#endif
#include "mozilla/EndianUtils.h"
#ifndef JS_STACK_GROWTH_DIRECTION
# ifdef __hppa

Просмотреть файл

@ -16,7 +16,7 @@
using namespace js;
#ifdef IS_LITTLE_ENDIAN
#if MOZ_LITTLE_ENDIAN
#define IEEE_8087
#else
#define IEEE_MC68k

Просмотреть файл

@ -999,21 +999,6 @@ else
AC_MSG_RESULT(no)
fi
MOZ_CHECK_HEADERS(endian.h)
if test "$ac_cv_header_endian_h" = yes; then
AC_DEFINE(JS_HAVE_ENDIAN_H)
fi
MOZ_CHECK_HEADERS([machine/endian.h],[],[],[#include <sys/types.h>])
if test "$ac_cv_header_machine_endian_h" = yes; then
AC_DEFINE(JS_HAVE_MACHINE_ENDIAN_H)
fi
MOZ_CHECK_HEADERS(sys/isa_defs.h)
if test "$ac_cv_header_sys_isa_defs_h" = yes; then
AC_DEFINE(JS_HAVE_SYS_ISA_DEFS_H)
fi
AC_LANG_CPLUSPLUS
MOZ_CXX11

Просмотреть файл

@ -1784,7 +1784,7 @@ DataViewObject::getDataPointer(JSContext* cx, Handle<DataViewObject*> obj, doubl
static inline bool
needToSwapBytes(bool littleEndian)
{
#if IS_LITTLE_ENDIAN
#if MOZ_LITTLE_ENDIAN
return !littleEndian;
#else
return littleEndian;

Просмотреть файл

@ -397,6 +397,7 @@ BufferList<AllocPolicy>::MoveFallible(bool* aSuccess, OtherAllocPolicy aAP)
if (!toAdvance || !result.mSegments.append(typename BufferList<OtherAllocPolicy>::Segment(iter.mData, toAdvance, toAdvance))) {
*aSuccess = false;
result.mSegments.clear();
return result;
}
iter.Advance(*this, toAdvance);

Просмотреть файл

@ -51,11 +51,6 @@ public class AppConstants {
* If MIN_SDK_VERSION is greater than or equal to the number, there
* is no need to do the runtime check.
*/
public static final boolean feature10Plus = MIN_SDK_VERSION >= 10 || (MAX_SDK_VERSION >= 10 && Build.VERSION.SDK_INT >= 10);
public static final boolean feature11Plus = MIN_SDK_VERSION >= 11 || (MAX_SDK_VERSION >= 11 && Build.VERSION.SDK_INT >= 11);
public static final boolean feature12Plus = MIN_SDK_VERSION >= 12 || (MAX_SDK_VERSION >= 12 && Build.VERSION.SDK_INT >= 12);
public static final boolean feature14Plus = MIN_SDK_VERSION >= 14 || (MAX_SDK_VERSION >= 14 && Build.VERSION.SDK_INT >= 14);
public static final boolean feature15Plus = MIN_SDK_VERSION >= 15 || (MAX_SDK_VERSION >= 15 && Build.VERSION.SDK_INT >= 15);
public static final boolean feature16Plus = MIN_SDK_VERSION >= 16 || (MAX_SDK_VERSION >= 16 && Build.VERSION.SDK_INT >= 16);
public static final boolean feature17Plus = MIN_SDK_VERSION >= 17 || (MAX_SDK_VERSION >= 17 && Build.VERSION.SDK_INT >= 17);
public static final boolean feature19Plus = MIN_SDK_VERSION >= 19 || (MAX_SDK_VERSION >= 19 && Build.VERSION.SDK_INT >= 19);
@ -238,7 +233,7 @@ public class AppConstants {
// it if this APK doesn't include API14 support.
public static final boolean MOZ_ANDROID_BEAM =
//#ifdef MOZ_ANDROID_BEAM
Versions.feature14Plus;
true;
//#else
false;
//#endif
@ -285,7 +280,7 @@ public class AppConstants {
public static final boolean ANDROID_DOWNLOADS_INTEGRATION =
//#ifdef MOZ_ANDROID_DOWNLOADS_INTEGRATION
AppConstants.Versions.feature12Plus;
true;
//#else
false;
//#endif

Просмотреть файл

@ -515,7 +515,7 @@ public class BrowserApp extends GeckoApp
// Check if this was a shortcut. Meta keys exists only on 11+.
final Tab tab = Tabs.getInstance().getSelectedTab();
if (Versions.feature11Plus && tab != null && event.isCtrlPressed()) {
if (tab != null && event.isCtrlPressed()) {
switch (keyCode) {
case KeyEvent.KEYCODE_LEFT_BRACKET:
tab.doBack();
@ -749,7 +749,8 @@ public class BrowserApp extends GeckoApp
"Sanitize:ClearSyncedTabs",
"Settings:Show",
"Telemetry:Gather",
"Updater:Launch");
"Updater:Launch",
"Website:Metadata");
final GeckoProfile profile = getProfile();
@ -1490,7 +1491,8 @@ public class BrowserApp extends GeckoApp
"Sanitize:ClearSyncedTabs",
"Settings:Show",
"Telemetry:Gather",
"Updater:Launch");
"Updater:Launch",
"Website:Metadata");
if (AppConstants.MOZ_ANDROID_BEAM) {
NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this);
@ -1930,6 +1932,13 @@ public class BrowserApp extends GeckoApp
}
break;
case "Website:Metadata":
final NativeJSObject metadata = message.getObject("metadata");
final String location = message.getString("location");
// TODO: Store metadata (Bug 1301717)
break;
default:
super.handleMessage(event, message, callback);
break;
@ -2761,10 +2770,6 @@ public class BrowserApp extends GeckoApp
mMenu.clear();
onCreateOptionsMenu(mMenu);
}
if (!Versions.feature14Plus) {
conditionallyNotifyEOL();
}
}
@Override
@ -3180,12 +3185,8 @@ public class BrowserApp extends GeckoApp
destination = menu;
} else if (info.parent == GECKO_TOOLS_MENU) {
// The tools menu only exists in our -v11 resources.
if (Versions.feature11Plus) {
final MenuItem tools = menu.findItem(R.id.tools);
destination = tools != null ? tools.getSubMenu() : menu;
} else {
destination = menu;
}
final MenuItem tools = menu.findItem(R.id.tools);
destination = tools != null ? tools.getSubMenu() : menu;
} else {
final MenuItem parent = menu.findItem(info.parent);
if (parent == null) {
@ -3310,13 +3311,11 @@ public class BrowserApp extends GeckoApp
}
// Action providers are available only ICS+.
if (Versions.feature14Plus) {
GeckoMenuItem share = (GeckoMenuItem) mMenu.findItem(R.id.share);
GeckoMenuItem share = (GeckoMenuItem) mMenu.findItem(R.id.share);
GeckoActionProvider provider = GeckoActionProvider.getForType(GeckoActionProvider.DEFAULT_MIME_TYPE, this);
GeckoActionProvider provider = GeckoActionProvider.getForType(GeckoActionProvider.DEFAULT_MIME_TYPE, this);
share.setActionProvider(provider);
}
share.setActionProvider(provider);
return true;
}
@ -3445,10 +3444,7 @@ public class BrowserApp extends GeckoApp
// NOTE: Use MenuUtils.safeSetEnabled because some actions might
// be on the BrowserToolbar context menu.
if (Versions.feature11Plus) {
// There is no page menu prior to v11 resources.
MenuUtils.safeSetEnabled(aMenu, R.id.page, false);
}
MenuUtils.safeSetEnabled(aMenu, R.id.page, false);
MenuUtils.safeSetEnabled(aMenu, R.id.subscribe, false);
MenuUtils.safeSetEnabled(aMenu, R.id.add_search_engine, false);
MenuUtils.safeSetEnabled(aMenu, R.id.add_to_launcher, false);
@ -3469,10 +3465,8 @@ public class BrowserApp extends GeckoApp
bookmark.setChecked(tab.isBookmark());
bookmark.setTitle(resolveBookmarkTitleID(tab.isBookmark()));
if (Versions.feature11Plus) {
// We don't use icons on GB builds so not resolving icons might conserve resources.
bookmark.setIcon(resolveBookmarkIconID(tab.isBookmark()));
}
// We don't use icons on GB builds so not resolving icons might conserve resources.
bookmark.setIcon(resolveBookmarkIconID(tab.isBookmark()));
back.setEnabled(tab.canDoBack());
forward.setEnabled(tab.canDoForward());
@ -3526,63 +3520,58 @@ public class BrowserApp extends GeckoApp
// NOTE: Use MenuUtils.safeSetEnabled because some actions might
// be on the BrowserToolbar context menu.
if (Versions.feature11Plus) {
MenuUtils.safeSetEnabled(aMenu, R.id.page, !isAboutHome(tab));
}
MenuUtils.safeSetEnabled(aMenu, R.id.page, !isAboutHome(tab));
MenuUtils.safeSetEnabled(aMenu, R.id.subscribe, tab.hasFeeds());
MenuUtils.safeSetEnabled(aMenu, R.id.add_search_engine, tab.hasOpenSearch());
MenuUtils.safeSetEnabled(aMenu, R.id.add_to_launcher, !isAboutHome(tab));
// Action providers are available only ICS+.
if (Versions.feature14Plus) {
// This provider also applies to the quick share menu item.
final GeckoActionProvider provider = ((GeckoMenuItem) share).getGeckoActionProvider();
if (provider != null) {
Intent shareIntent = provider.getIntent();
// This provider also applies to the quick share menu item.
final GeckoActionProvider provider = ((GeckoMenuItem) share).getGeckoActionProvider();
if (provider != null) {
Intent shareIntent = provider.getIntent();
// For efficiency, the provider's intent is only set once
if (shareIntent == null) {
shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
provider.setIntent(shareIntent);
}
// For efficiency, the provider's intent is only set once
if (shareIntent == null) {
shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
provider.setIntent(shareIntent);
}
// Replace the existing intent's extras
shareIntent.putExtra(Intent.EXTRA_TEXT, url);
shareIntent.putExtra(Intent.EXTRA_SUBJECT, tab.getDisplayTitle());
shareIntent.putExtra(Intent.EXTRA_TITLE, tab.getDisplayTitle());
shareIntent.putExtra(ShareDialog.INTENT_EXTRA_DEVICES_ONLY, true);
// Replace the existing intent's extras
shareIntent.putExtra(Intent.EXTRA_TEXT, url);
shareIntent.putExtra(Intent.EXTRA_SUBJECT, tab.getDisplayTitle());
shareIntent.putExtra(Intent.EXTRA_TITLE, tab.getDisplayTitle());
shareIntent.putExtra(ShareDialog.INTENT_EXTRA_DEVICES_ONLY, true);
// Clear the existing thumbnail extras so we don't share an old thumbnail.
shareIntent.removeExtra("share_screenshot_uri");
// Clear the existing thumbnail extras so we don't share an old thumbnail.
shareIntent.removeExtra("share_screenshot_uri");
// Include the thumbnail of the page being shared.
BitmapDrawable drawable = tab.getThumbnail();
if (drawable != null) {
Bitmap thumbnail = drawable.getBitmap();
// Include the thumbnail of the page being shared.
BitmapDrawable drawable = tab.getThumbnail();
if (drawable != null) {
Bitmap thumbnail = drawable.getBitmap();
// Kobo uses a custom intent extra for sharing thumbnails.
if (Build.MANUFACTURER.equals("Kobo") && thumbnail != null) {
File cacheDir = getExternalCacheDir();
// Kobo uses a custom intent extra for sharing thumbnails.
if (Build.MANUFACTURER.equals("Kobo") && thumbnail != null) {
File cacheDir = getExternalCacheDir();
if (cacheDir != null) {
File outFile = new File(cacheDir, "thumbnail.png");
if (cacheDir != null) {
File outFile = new File(cacheDir, "thumbnail.png");
try {
final java.io.FileOutputStream out = new java.io.FileOutputStream(outFile);
try {
final java.io.FileOutputStream out = new java.io.FileOutputStream(outFile);
thumbnail.compress(Bitmap.CompressFormat.PNG, 90, out);
} finally {
try {
thumbnail.compress(Bitmap.CompressFormat.PNG, 90, out);
} finally {
try {
out.close();
} catch (final IOException e) { /* Nothing to do here. */ }
}
} catch (FileNotFoundException e) {
Log.e(LOGTAG, "File not found", e);
out.close();
} catch (final IOException e) { /* Nothing to do here. */ }
}
shareIntent.putExtra("share_screenshot_uri", Uri.parse(outFile.getPath()));
} catch (FileNotFoundException e) {
Log.e(LOGTAG, "File not found", e);
}
shareIntent.putExtra("share_screenshot_uri", Uri.parse(outFile.getPath()));
}
}
}
@ -3671,18 +3660,12 @@ public class BrowserApp extends GeckoApp
Telemetry.sendUIEvent(TelemetryContract.Event.UNSAVE, TelemetryContract.Method.MENU, extra);
tab.removeBookmark();
item.setTitle(resolveBookmarkTitleID(false));
if (Versions.feature11Plus) {
// We don't use icons on GB builds so not resolving icons might conserve resources.
item.setIcon(resolveBookmarkIconID(false));
}
item.setIcon(resolveBookmarkIconID(false));
} else {
Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, extra);
tab.addBookmark();
item.setTitle(resolveBookmarkTitleID(true));
if (Versions.feature11Plus) {
// We don't use icons on GB builds so not resolving icons might conserve resources.
item.setIcon(resolveBookmarkIconID(true));
}
item.setIcon(resolveBookmarkIconID(true));
}
}
return true;

Просмотреть файл

@ -323,12 +323,7 @@ public class DoorHangerPopup extends AnchoredPopup
return;
}
// Make the popup focusable for accessibility. This gets done here
// so the node can be accessibility focused, but on pre-ICS devices this
// causes crashes, so it is done after the popup is shown.
if (Versions.feature14Plus) {
setFocusable(true);
}
setFocusable(true);
show();
}

Просмотреть файл

@ -385,9 +385,7 @@ public abstract class GeckoApp
onPrepareOptionsMenu(mMenu);
if (Versions.feature11Plus) {
super.invalidateOptionsMenu();
}
super.invalidateOptionsMenu();
}
@Override
@ -852,22 +850,10 @@ public abstract class GeckoApp
dialog.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
if (Versions.feature11Plus) {
if (listView.getCheckedItemCount() == 0) {
clearButton.setEnabled(false);
} else {
clearButton.setEnabled(true);
}
} else {
final SparseBooleanArray items = listView.getCheckedItemPositions();
for (int j = 0; j < items.size(); j++) {
if (items.valueAt(j) == true) {
clearButton.setEnabled(true);
return;
}
}
if (listView.getCheckedItemCount() == 0) {
clearButton.setEnabled(false);
} else {
clearButton.setEnabled(true);
}
}
});
@ -1890,9 +1876,7 @@ public abstract class GeckoApp
// Try to make it fully transparent.
if (mCameraView instanceof SurfaceView) {
if (Versions.feature11Plus) {
mCameraView.setAlpha(0.0f);
}
mCameraView.setAlpha(0.0f);
ViewGroup mCameraLayout = (ViewGroup) findViewById(R.id.camera_layout);
// Some phones (eg. nexus S) need at least a 8x16 preview size
mCameraLayout.addView(mCameraView,
@ -2129,13 +2113,6 @@ public abstract class GeckoApp
refreshChrome();
}
if (!Versions.feature14Plus) {
// Update accessibility settings in case it has been changed by the
// user. On API14+, this is handled in LayerView by registering an
// accessibility state change listener.
GeckoAccessibility.updateAccessibilitySettings(this);
}
if (mAppStateListeners != null) {
for (GeckoAppShell.AppStateListener listener : mAppStateListeners) {
listener.onResume();

Просмотреть файл

@ -372,10 +372,6 @@ public final class IntentHelper implements GeckoEventListener,
// We create a separate method to better encapsulate the @TargetApi use.
@TargetApi(15)
private static void nullIntentSelector(final Intent intent) {
if (!AppConstants.Versions.feature15Plus) {
return;
}
intent.setSelector(null);
}

Просмотреть файл

@ -56,10 +56,6 @@ public class ScreenshotObserver {
* have been granted by the user. Calling this method will not prompt for permissions.
*/
public void start() {
if (!Versions.feature14Plus) {
return;
}
Permissions.from(context)
.withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.doNotPrompt()
@ -83,10 +79,6 @@ public class ScreenshotObserver {
}
public void stop() {
if (!Versions.feature14Plus) {
return;
}
if (mediaObserver == null) {
return;
}

Просмотреть файл

@ -866,8 +866,7 @@ public class Tabs implements GeckoEventListener {
needsNewTab = (flags & LOADURL_NEW_TAB) != 0;
} else {
// If you modify this code, be careful that intent != null.
final boolean extraCreateNewTab = (Versions.feature12Plus) ?
intent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false) : false;
final boolean extraCreateNewTab = intent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false);
final Tab applicationTab = getTabForApplicationId(applicationId);
if (applicationTab == null || extraCreateNewTab) {
needsNewTab = true;

Просмотреть файл

@ -173,7 +173,7 @@ public class PropertyAnimator implements Runnable {
// in the current view tree. OnPreDrawListener seems broken
// on pre-Honeycomb devices, start animation immediatelly
// in this case.
if (Versions.feature11Plus && treeObserver != null && treeObserver.isAlive()) {
if (treeObserver != null && treeObserver.isAlive()) {
treeObserver.addOnPreDrawListener(preDrawListener);
} else {
mFramePoster.postFirstAnimationFrame();

Просмотреть файл

@ -95,7 +95,7 @@ public abstract class AbstractTransactionalProvider extends ContentProvider {
*/
@SuppressWarnings("static-method")
protected boolean shouldUseTransactions() {
return Versions.feature11Plus;
return true;
}
private boolean isInBatch() {

Просмотреть файл

@ -409,21 +409,8 @@ public class DBUtils {
statement.execute();
return 0;
}
if (AppConstants.Versions.feature11Plus) {
// This is a separate method so we can annotate it with @TargetApi.
return executeStatementReturningChangedRows(statement);
} else {
statement.execute();
final Cursor cursor = db.rawQuery("SELECT changes()", null);
try {
cursor.moveToFirst();
return cursor.getInt(0);
} finally {
cursor.close();
}
}
// This is a separate method so we can annotate it with @TargetApi.
return executeStatementReturningChangedRows(statement);
} finally {
statement.close();
}

Просмотреть файл

@ -98,10 +98,7 @@ public abstract class SQLiteBridgeContentProvider extends ContentProvider {
}
mDatabasePerProfile = null;
}
if (AppConstants.Versions.feature11Plus) {
super.shutdown();
}
super.shutdown();
}
@Override

Просмотреть файл

@ -213,7 +213,7 @@ public class HomePager extends ViewPager implements HomeScreen {
}
// Only animate on post-HC devices, when a non-null animator is given
final boolean shouldAnimate = Versions.feature11Plus && animator != null;
final boolean shouldAnimate = animator != null;
final HomeAdapter adapter = new HomeAdapter(mContext, fm);
adapter.setOnAddPanelListener(mAddPanelListener);

Просмотреть файл

@ -357,13 +357,8 @@ public class LightweightTheme implements GeckoEventListener {
ViewParent parent;
View curView = view;
do {
if (Versions.feature11Plus) {
offsetX += (int) curView.getTranslationX() - curView.getScrollX();
offsetY += (int) curView.getTranslationY() - curView.getScrollY();
} else {
offsetX -= curView.getScrollX();
offsetY -= curView.getScrollY();
}
offsetX += (int) curView.getTranslationX() - curView.getScrollX();
offsetY += (int) curView.getTranslationY() - curView.getScrollY();
parent = curView.getParent();

Просмотреть файл

@ -152,9 +152,7 @@ public class GeckoMenuInflater extends MenuInflater {
.setCheckable(item.checkable)
.setIcon(item.iconRes);
if (Versions.feature11Plus) {
menuItem.setShowAsAction(item.showAsAction);
}
menuItem.setShowAsAction(item.showAsAction);
if (geckoItem != null) {
// We don't need to allow presenter updates during inflation,

Просмотреть файл

@ -29,9 +29,7 @@ public class MenuPanel extends LinearLayout {
@Override
public boolean dispatchPopulateAccessibilityEvent (AccessibilityEvent event) {
if (Versions.feature14Plus) {
onPopulateAccessibilityEvent(event);
}
onPopulateAccessibilityEvent(event);
return true;
}

Просмотреть файл

@ -71,13 +71,7 @@ public class SendTabDeviceListArrayAdapter extends ArrayAdapter<RemoteClient> {
clear();
setNotifyOnChange(false); // So we don't notify for each add.
if (AppConstants.Versions.feature11Plus) {
addAll(records);
} else {
for (RemoteClient record : records) {
add(record);
}
}
addAll(records);
notifyDataSetChanged();
}

Просмотреть файл

@ -166,7 +166,7 @@ public class GeckoPreferenceFragment extends PreferenceFragment {
}
final GeckoPreferences activity = (GeckoPreferences) getActivity();
if (Versions.feature11Plus && activity.isMultiPane()) {
if (activity.isMultiPane()) {
// In a multi-pane activity, the title is "Settings", and the action
// bar is along the top of the screen. We don't want to change those.
activity.showBreadCrumbs(newTitle, newTitle);
@ -237,8 +237,7 @@ public class GeckoPreferenceFragment extends PreferenceFragment {
// The resource was invalid. Use the default resource.
Log.e(LOGTAG, "Failed to find resource: " + resourceName + ". Displaying default settings.");
boolean isMultiPane = Versions.feature11Plus &&
((GeckoPreferences) activity).isMultiPane();
boolean isMultiPane = ((GeckoPreferences) activity).isMultiPane();
resid = isMultiPane ? R.xml.preferences_general_tablet : R.xml.preferences;
}

Просмотреть файл

@ -222,13 +222,11 @@ OnSharedPreferenceChangeListener
}
}
private void updateActionBarTitle(int title) {
if (Versions.feature14Plus) {
final String newTitle = getString(title);
if (newTitle != null) {
Log.v(LOGTAG, "Setting action bar title to " + newTitle);
final String newTitle = getString(title);
if (newTitle != null) {
Log.v(LOGTAG, "Setting action bar title to " + newTitle);
setTitle(newTitle);
}
setTitle(newTitle);
}
}
@ -341,11 +339,9 @@ OnSharedPreferenceChangeListener
// the correct Fragment resource.
// Note: this seems to only be required for non-multipane devices, multipane
// manages to automatically select the correct fragments.
if (Versions.feature11Plus) {
if (!getIntent().hasExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT)) {
// Set up the default fragment if there is no explicit fragment to show.
setupTopLevelFragmentIntent();
}
if (!getIntent().hasExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT)) {
// Set up the default fragment if there is no explicit fragment to show.
setupTopLevelFragmentIntent();
}
// We must call this before setTitle to avoid crashes. Most devices don't seem to care
@ -354,7 +350,7 @@ OnSharedPreferenceChangeListener
// likely other strange devices (other Asus devices, some Samsungs) could do the same.
super.onCreate(savedInstanceState);
if (Versions.feature11Plus && onIsMultiPane()) {
if (onIsMultiPane()) {
// So that Android doesn't put the fragment title (or nothing at
// all) in the action bar.
updateActionBarTitle(R.string.settings_title);
@ -521,11 +517,9 @@ OnSharedPreferenceChangeListener
@Override
public void onPause() {
// Symmetric with onResume.
if (Versions.feature11Plus) {
if (isMultiPane()) {
SharedPreferences prefs = GeckoSharedPrefs.forApp(this);
prefs.unregisterOnSharedPreferenceChangeListener(this);
}
if (isMultiPane()) {
SharedPreferences prefs = GeckoSharedPrefs.forApp(this);
prefs.unregisterOnSharedPreferenceChangeListener(this);
}
super.onPause();
@ -543,14 +537,12 @@ OnSharedPreferenceChangeListener
((GeckoApplication) getApplication()).onActivityResume(this);
}
if (Versions.feature11Plus) {
// Watch prefs, otherwise we don't reliably get told when they change.
// See documentation for onSharedPreferenceChange for more.
// Inexplicably only needed on tablet.
if (isMultiPane()) {
SharedPreferences prefs = GeckoSharedPrefs.forApp(this);
prefs.registerOnSharedPreferenceChangeListener(this);
}
// Watch prefs, otherwise we don't reliably get told when they change.
// See documentation for onSharedPreferenceChange for more.
// Inexplicably only needed on tablet.
if (isMultiPane()) {
SharedPreferences prefs = GeckoSharedPrefs.forApp(this);
prefs.registerOnSharedPreferenceChangeListener(this);
}
}

Просмотреть файл

@ -52,13 +52,10 @@ class SyncPreference extends Preference {
public void run() {
setTitle(R.string.pref_sync);
setSummary(R.string.pref_sync_summary);
if (AppConstants.Versions.feature11Plus) {
// Cancel any pending task.
Picasso.with(mContext).cancelRequest(profileAvatarTarget);
// Clear previously set icon.
setIcon(R.drawable.sync_avatar_default);
}
}
});
return;
@ -73,11 +70,6 @@ class SyncPreference extends Preference {
}
});
// Updating icons from Java is not supported prior to API 11.
if (!AppConstants.Versions.feature11Plus) {
return;
}
final ExtendedJSONObject profileJSON = fxAccount.getProfileJSON();
if (profileJSON == null) {
return;

Просмотреть файл

@ -85,7 +85,7 @@ public class IconGridInput extends PromptInput implements OnItemClickListener {
// Despite what the docs say, setItemChecked was not moved into the AbsListView class until sometime between
// Android 2.3.7 and Android 4.0.3. For other versions the item won't be visually highlighted, BUT we really only
// mSelected will still be set so that we default to its behavior.
if (Versions.feature11Plus && mSelected > -1) {
if (mSelected > -1) {
view.setItemChecked(mSelected, true);
}

Просмотреть файл

@ -38,7 +38,7 @@ public class TabQueueHelper {
private static final String LOGTAG = "Gecko" + TabQueueHelper.class.getSimpleName();
// Disable Tab Queue for API level 10 (GB) - Bug 1206055
public static final boolean TAB_QUEUE_ENABLED = AppConstants.Versions.feature11Plus;
public static final boolean TAB_QUEUE_ENABLED = true;
public static final String FILE_NAME = "tab_queue_url_list.json";
public static final String LOAD_URLS_ACTION = "TAB_QUEUE_LOAD_URLS_ACTION";

Просмотреть файл

@ -294,9 +294,7 @@ class TabsGridLayout extends GridView
((TabsLayoutItemView) tabView).setChecked(checked);
}
// setItemChecked doesn't exist until API 11, despite what the API docs say!
if (AppConstants.Versions.feature11Plus) {
setItemChecked(i, checked);
}
setItemChecked(i, checked);
}
}
});

Просмотреть файл

@ -191,9 +191,7 @@ public abstract class BrowserToolbar extends ThemedRelativeLayout
tabsButton = (ThemedImageButton) findViewById(R.id.tabs);
tabsCounter = (TabCounter) findViewById(R.id.tabs_counter);
if (Versions.feature11Plus) {
tabsCounter.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
tabsCounter.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
menuButton = (ThemedFrameLayout) findViewById(R.id.menu);
menuIcon = (ThemedImageView) findViewById(R.id.menu_icon);

Просмотреть файл

@ -48,20 +48,8 @@ class CanvasDelegate {
if (path != null && !path.isEmpty()) {
// ICS added double-buffering, which made it easier for drawing the Path directly over the DST.
// In pre-ICS, drawPath() doesn't seem to use ARGB_8888 mode for performance, hence transparency is not preserved.
if (Versions.feature14Plus) {
mPaint.setXfermode(mMode);
canvas.drawPath(path, mPaint);
} else {
// Allocate a bitmap and draw the masking/clipping path.
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
(new Canvas(bitmap)).drawPath(path, mPaint);
mPaint.setXfermode(mMode);
canvas.drawBitmap(bitmap, 0, 0, mPaint);
bitmap.recycle();
mPaint.setXfermode(null);
}
mPaint.setXfermode(mMode);
canvas.drawPath(path, mPaint);
}
// Restore the canvas.

Просмотреть файл

@ -228,11 +228,9 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
} else {
password = login.getString("password");
}
if (AppConstants.Versions.feature11Plus) {
manager.setPrimaryClip(ClipData.newPlainText("password", password));
} else {
manager.setText(password);
}
manager.setPrimaryClip(ClipData.newPlainText("password", password));
SnackbarBuilder.builder(activity)
.message(R.string.doorhanger_login_select_toast_copy)
.duration(Snackbar.LENGTH_SHORT)

Просмотреть файл

@ -618,8 +618,7 @@ public class ToolbarEditText extends CustomEditText
}
if ((keyCode == KeyEvent.KEYCODE_DEL ||
(Versions.feature11Plus &&
keyCode == KeyEvent.KEYCODE_FORWARD_DEL)) &&
(keyCode == KeyEvent.KEYCODE_FORWARD_DEL)) &&
removeAutocomplete(getText())) {
// Delete autocomplete text when backspacing or forward deleting.
return true;

Просмотреть файл

@ -101,82 +101,45 @@ public class DateTimePicker extends FrameLayout {
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
updateInputState();
mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis());
final boolean newBehavior = Versions.feature11Plus;
if (newBehavior) {
if (DEBUG) {
Log.d(LOGTAG, "SDK version > 10, using new behavior");
}
if (DEBUG) {
Log.d(LOGTAG, "SDK version > 10, using new behavior");
}
// The native date picker widget on these SDKs increments
// the next field when one field reaches the maximum.
if (picker == mDaySpinner && mDayEnabled) {
int maxDayOfMonth = mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH);
int old = mTempDate.get(Calendar.DAY_OF_MONTH);
setTempDate(Calendar.DAY_OF_MONTH, old, newVal, 1, maxDayOfMonth);
} else if (picker == mMonthSpinner && mMonthEnabled) {
int old = mTempDate.get(Calendar.MONTH);
setTempDate(Calendar.MONTH, old, newVal, Calendar.JANUARY, Calendar.DECEMBER);
} else if (picker == mWeekSpinner) {
int old = mTempDate.get(Calendar.WEEK_OF_YEAR);
int maxWeekOfYear = mTempDate.getActualMaximum(Calendar.WEEK_OF_YEAR);
setTempDate(Calendar.WEEK_OF_YEAR, old, newVal, 0, maxWeekOfYear);
} else if (picker == mYearSpinner && mYearEnabled) {
int month = mTempDate.get(Calendar.MONTH);
mTempDate.set(Calendar.YEAR, newVal);
// Changing the year shouldn't change the month. (in case of non-leap year a Feb 29)
// change the day instead;
if (month != mTempDate.get(Calendar.MONTH)) {
mTempDate.set(Calendar.MONTH, month);
mTempDate.set(Calendar.DAY_OF_MONTH,
mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH));
}
} else if (picker == mHourSpinner && mHourEnabled) {
if (mIs12HourMode) {
setTempDate(Calendar.HOUR, oldVal, newVal, 1, 12);
} else {
setTempDate(Calendar.HOUR_OF_DAY, oldVal, newVal, 0, 23);
}
} else if (picker == mMinuteSpinner && mMinuteEnabled) {
setTempDate(Calendar.MINUTE, oldVal, newVal, 0, 59);
} else if (picker == mAMPMSpinner && mHourEnabled) {
mTempDate.set(Calendar.AM_PM, newVal);
} else {
throw new IllegalArgumentException();
// The native date picker widget on these SDKs increments
// the next field when one field reaches the maximum.
if (picker == mDaySpinner && mDayEnabled) {
int maxDayOfMonth = mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH);
int old = mTempDate.get(Calendar.DAY_OF_MONTH);
setTempDate(Calendar.DAY_OF_MONTH, old, newVal, 1, maxDayOfMonth);
} else if (picker == mMonthSpinner && mMonthEnabled) {
int old = mTempDate.get(Calendar.MONTH);
setTempDate(Calendar.MONTH, old, newVal, Calendar.JANUARY, Calendar.DECEMBER);
} else if (picker == mWeekSpinner) {
int old = mTempDate.get(Calendar.WEEK_OF_YEAR);
int maxWeekOfYear = mTempDate.getActualMaximum(Calendar.WEEK_OF_YEAR);
setTempDate(Calendar.WEEK_OF_YEAR, old, newVal, 0, maxWeekOfYear);
} else if (picker == mYearSpinner && mYearEnabled) {
int month = mTempDate.get(Calendar.MONTH);
mTempDate.set(Calendar.YEAR, newVal);
// Changing the year shouldn't change the month. (in case of non-leap year a Feb 29)
// change the day instead;
if (month != mTempDate.get(Calendar.MONTH)) {
mTempDate.set(Calendar.MONTH, month);
mTempDate.set(Calendar.DAY_OF_MONTH,
mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH));
}
} else if (picker == mHourSpinner && mHourEnabled) {
if (mIs12HourMode) {
setTempDate(Calendar.HOUR, oldVal, newVal, 1, 12);
} else {
setTempDate(Calendar.HOUR_OF_DAY, oldVal, newVal, 0, 23);
}
} else if (picker == mMinuteSpinner && mMinuteEnabled) {
setTempDate(Calendar.MINUTE, oldVal, newVal, 0, 59);
} else if (picker == mAMPMSpinner && mHourEnabled) {
mTempDate.set(Calendar.AM_PM, newVal);
} else {
if (DEBUG) Log.d(LOGTAG, "Sdk version < 10, using old behavior");
if (picker == mDaySpinner && mDayEnabled) {
mTempDate.set(Calendar.DAY_OF_MONTH, newVal);
} else if (picker == mMonthSpinner && mMonthEnabled) {
mTempDate.set(Calendar.MONTH, newVal);
if (mTempDate.get(Calendar.MONTH) == newVal + 1) {
mTempDate.set(Calendar.MONTH, newVal);
mTempDate.set(Calendar.DAY_OF_MONTH,
mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH));
}
} else if (picker == mWeekSpinner) {
mTempDate.set(Calendar.WEEK_OF_YEAR, newVal);
} else if (picker == mYearSpinner && mYearEnabled) {
int month = mTempDate.get(Calendar.MONTH);
mTempDate.set(Calendar.YEAR, newVal);
if (month != mTempDate.get(Calendar.MONTH)) {
mTempDate.set(Calendar.MONTH, month);
mTempDate.set(Calendar.DAY_OF_MONTH,
mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH));
}
} else if (picker == mHourSpinner && mHourEnabled) {
if (mIs12HourMode) {
mTempDate.set(Calendar.HOUR, newVal);
} else {
mTempDate.set(Calendar.HOUR_OF_DAY, newVal);
}
} else if (picker == mMinuteSpinner && mMinuteEnabled) {
mTempDate.set(Calendar.MINUTE, newVal);
} else if (picker == mAMPMSpinner && mHourEnabled) {
mTempDate.set(Calendar.AM_PM, newVal);
} else {
throw new IllegalArgumentException();
}
throw new IllegalArgumentException();
}
setDate(mTempDate);
if (mDayEnabled) {

Просмотреть файл

@ -41,7 +41,7 @@ public class RoundedCornerLayout extends LinearLayout {
private void init(Context context) {
// Bug 1201081 - clipPath with hardware acceleration crashes on r11-18.
cannotClipPath = AppConstants.Versions.feature11Plus && !AppConstants.Versions.feature19Plus;
cannotClipPath = !AppConstants.Versions.feature19Plus;
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();

Просмотреть файл

@ -236,13 +236,9 @@ gvjar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x
'GeckoViewChrome.java',
'GeckoViewContent.java',
'GeckoViewFragment.java',
'gfx/Axis.java',
'gfx/BitmapUtils.java',
'gfx/BufferedImage.java',
'gfx/BufferedImageGLInfo.java',
'gfx/DisplayPortCalculator.java',
'gfx/DisplayPortMetrics.java',
'gfx/DrawTimingQueue.java',
'gfx/DynamicToolbarAnimator.java',
'gfx/FloatSize.java',
'gfx/FullScreenState.java',
@ -261,9 +257,7 @@ gvjar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x
'gfx/ProgressiveUpdateData.java',
'gfx/RectUtils.java',
'gfx/RenderTask.java',
'gfx/SimpleScaleGestureDetector.java',
'gfx/StackScroller.java',
'gfx/SubdocumentScrollHelper.java',
'gfx/SurfaceTextureListener.java',
'gfx/ViewTransform.java',
'InputConnectionListener.java',

Просмотреть файл

@ -116,6 +116,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Sna
XPCOMUtils.defineLazyModuleGetter(this, "RuntimePermissions", "resource://gre/modules/RuntimePermissions.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "WebsiteMetadata", "resource://gre/modules/WebsiteMetadata.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "FontEnumerator",
"@mozilla.org/gfx/fontenumerator;1",
"nsIFontEnumerator");
@ -3930,6 +3932,11 @@ Tab.prototype = {
this.browser.addEventListener("pagehide", listener, true);
}
if (AppConstants.NIGHTLY_BUILD || AppConstants.MOZ_ANDROID_ACTIVITY_STREAM) {
WebsiteMetadata.parseAsynchronously(this.browser.contentDocument);
}
break;
}

Просмотреть файл

@ -93,16 +93,12 @@ public class GeckoAccessibility {
event.setItemCount(message.optInt("itemCount", -1));
event.setCurrentItemIndex(message.optInt("currentItemIndex", -1));
event.setBeforeText(message.optString("beforeText"));
if (Versions.feature14Plus) {
event.setToIndex(message.optInt("toIndex", -1));
event.setScrollable(message.optBoolean("scrollable"));
event.setScrollX(message.optInt("scrollX", -1));
event.setScrollY(message.optInt("scrollY", -1));
}
if (Versions.feature15Plus) {
event.setMaxScrollX(message.optInt("maxScrollX", -1));
event.setMaxScrollY(message.optInt("maxScrollY", -1));
}
event.setToIndex(message.optInt("toIndex", -1));
event.setScrollable(message.optBoolean("scrollable"));
event.setScrollX(message.optInt("scrollX", -1));
event.setScrollY(message.optInt("scrollY", -1));
event.setMaxScrollX(message.optInt("maxScrollX", -1));
event.setMaxScrollY(message.optInt("maxScrollY", -1));
}
private static void sendDirectAccessibilityEvent(int eventType, JSONObject message) {

Просмотреть файл

@ -824,21 +824,9 @@ public class GeckoAppShell
@JNITarget
static public int getPreferredIconSize() {
if (Versions.feature11Plus) {
ActivityManager am = (ActivityManager)
getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
return am.getLauncherLargeIconSize();
} else {
switch (getDpi()) {
case DisplayMetrics.DENSITY_MEDIUM:
return 48;
case DisplayMetrics.DENSITY_XHIGH:
return 96;
case DisplayMetrics.DENSITY_HIGH:
default:
return 72;
}
}
ActivityManager am = (ActivityManager)
getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
return am.getLauncherLargeIconSize();
}
@WrapForJNI(calledFrom = "gecko")

Просмотреть файл

@ -614,10 +614,8 @@ final class GeckoEditable extends JNIObject
float tpUnderlineThickness = 0.0f;
// These TextPaint fields only exist on Android ICS+ and are not in the SDK.
if (Versions.feature14Plus) {
tpUnderlineColor = (Integer)getField(tp, "underlineColor", 0);
tpUnderlineThickness = (Float)getField(tp, "underlineThickness", 0.0f);
}
tpUnderlineColor = (Integer)getField(tp, "underlineColor", 0);
tpUnderlineThickness = (Float)getField(tp, "underlineThickness", 0.0f);
if (tpUnderlineColor != 0) {
rangeStyles |= IME_RANGE_UNDERLINE | IME_RANGE_LINECOLOR;
rangeLineColor = tpUnderlineColor;

Просмотреть файл

@ -994,10 +994,10 @@ class GeckoInputConnection
if (typeHint != null &&
(typeHint.equalsIgnoreCase("date") ||
typeHint.equalsIgnoreCase("time") ||
(Versions.feature11Plus && (typeHint.equalsIgnoreCase("datetime") ||
typeHint.equalsIgnoreCase("month") ||
typeHint.equalsIgnoreCase("week") ||
typeHint.equalsIgnoreCase("datetime-local"))))) {
typeHint.equalsIgnoreCase("datetime") ||
typeHint.equalsIgnoreCase("month") ||
typeHint.equalsIgnoreCase("week") ||
typeHint.equalsIgnoreCase("datetime-local"))) {
state = IME_STATE_DISABLED;
}

Просмотреть файл

@ -1,532 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.gfx;
import java.util.HashMap;
import java.util.Map;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.PrefsHelper;
import org.mozilla.gecko.util.FloatUtils;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
/**
* This class represents the physics for one axis of movement (i.e. either
* horizontal or vertical). It tracks the different properties of movement
* like displacement, velocity, viewport dimensions, etc. pertaining to
* a particular axis.
*/
abstract class Axis {
private static final String LOGTAG = "GeckoAxis";
private static final String PREF_SCROLLING_FRICTION_SLOW = "ui.scrolling.friction_slow";
private static final String PREF_SCROLLING_FRICTION_FAST = "ui.scrolling.friction_fast";
private static final String PREF_SCROLLING_MAX_EVENT_ACCELERATION = "ui.scrolling.max_event_acceleration";
private static final String PREF_SCROLLING_OVERSCROLL_DECEL_RATE = "ui.scrolling.overscroll_decel_rate";
private static final String PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT = "ui.scrolling.overscroll_snap_limit";
private static final String PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE = "ui.scrolling.min_scrollable_distance";
private static final String PREF_FLING_ACCEL_INTERVAL = "ui.scrolling.fling_accel_interval";
private static final String PREF_FLING_ACCEL_BASE_MULTIPLIER = "ui.scrolling.fling_accel_base_multiplier";
private static final String PREF_FLING_ACCEL_SUPPLEMENTAL_MULTIPLIER = "ui.scrolling.fling_accel_supplemental_multiplier";
private static final String PREF_FLING_CURVE_FUNCTION_X1 = "ui.scrolling.fling_curve_function_x1";
private static final String PREF_FLING_CURVE_FUNCTION_Y1 = "ui.scrolling.fling_curve_function_y1";
private static final String PREF_FLING_CURVE_FUNCTION_X2 = "ui.scrolling.fling_curve_function_x2";
private static final String PREF_FLING_CURVE_FUNCTION_Y2 = "ui.scrolling.fling_curve_function_y2";
private static final String PREF_FLING_CURVE_THRESHOLD_VELOCITY = "ui.scrolling.fling_curve_threshold_velocity";
private static final String PREF_FLING_CURVE_MAXIMUM_VELOCITY = "ui.scrolling.fling_curve_max_velocity";
private static final String PREF_FLING_CURVE_NEWTON_ITERATIONS = "ui.scrolling.fling_curve_newton_iterations";
// This fraction of velocity remains after every animation frame when the velocity is low.
private static float FRICTION_SLOW;
// This fraction of velocity remains after every animation frame when the velocity is high.
private static float FRICTION_FAST;
// Below this velocity (in pixels per frame), the friction starts increasing from FRICTION_FAST
// to FRICTION_SLOW.
private static float VELOCITY_THRESHOLD;
// The maximum velocity change factor between events, per ms, in %.
// Direction changes are excluded.
private static float MAX_EVENT_ACCELERATION;
// The rate of deceleration when the surface has overscrolled.
private static float OVERSCROLL_DECEL_RATE;
// The percentage of the surface which can be overscrolled before it must snap back.
private static float SNAP_LIMIT;
// The minimum amount of space that must be present for an axis to be considered scrollable,
// in pixels.
private static float MIN_SCROLLABLE_DISTANCE;
// The interval within which if two flings are done then scrolling effect is accelerated.
private static long FLING_ACCEL_INTERVAL;
// The multiplication constant of the base velocity in case of accelerated scrolling.
private static float FLING_ACCEL_BASE_MULTIPLIER;
// The multiplication constant of the supplemental velocity in case of accelerated scrolling.
private static float FLING_ACCEL_SUPPLEMENTAL_MULTIPLIER;
// x co-ordinate of the second bezier control point
private static float FLING_CURVE_FUNCTION_X1;
// y co-ordinate of the second bezier control point
private static float FLING_CURVE_FUNCTION_Y1;
// x co-ordinate of the third bezier control point
private static float FLING_CURVE_FUNCTION_X2;
// y co-ordinate of the third bezier control point
private static float FLING_CURVE_FUNCTION_Y2;
// Minimum velocity for curve to be implemented i.e fling curving
private static float FLING_CURVE_THRESHOLD_VELOCITY;
// Maximum permitted velocity
private static float FLING_CURVE_MAXIMUM_VELOCITY;
// Number of iterations in the Newton-Raphson method
private static int FLING_CURVE_NEWTON_ITERATIONS;
private static float getFloatPref(Map<String, Integer> prefs, String prefName, int defaultValue) {
Integer value = (prefs == null ? null : prefs.get(prefName));
return (value == null || value < 0 ? defaultValue : value) / 1000f;
}
private static int getIntPref(Map<String, Integer> prefs, String prefName, int defaultValue) {
Integer value = (prefs == null ? null : prefs.get(prefName));
return (value == null || value < 0 ? defaultValue : value);
}
static void initPrefs() {
final String[] prefs = { PREF_SCROLLING_FRICTION_FAST,
PREF_SCROLLING_FRICTION_SLOW,
PREF_SCROLLING_MAX_EVENT_ACCELERATION,
PREF_SCROLLING_OVERSCROLL_DECEL_RATE,
PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT,
PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE,
PREF_FLING_ACCEL_INTERVAL,
PREF_FLING_ACCEL_BASE_MULTIPLIER,
PREF_FLING_ACCEL_SUPPLEMENTAL_MULTIPLIER,
PREF_FLING_CURVE_FUNCTION_X1,
PREF_FLING_CURVE_FUNCTION_Y1,
PREF_FLING_CURVE_FUNCTION_X2,
PREF_FLING_CURVE_FUNCTION_Y2,
PREF_FLING_CURVE_THRESHOLD_VELOCITY,
PREF_FLING_CURVE_MAXIMUM_VELOCITY,
PREF_FLING_CURVE_NEWTON_ITERATIONS };
PrefsHelper.getPrefs(prefs, new PrefsHelper.PrefHandlerBase() {
Map<String, Integer> mPrefs = new HashMap<String, Integer>();
@Override public void prefValue(String name, int value) {
mPrefs.put(name, value);
}
@Override public void finish() {
setPrefs(mPrefs);
}
});
}
static final float MS_PER_FRAME = 1000.0f / 60.0f;
static final long NS_PER_FRAME = Math.round(1000000000f / 60f);
private static final float FRAMERATE_MULTIPLIER = (1000f / 60f) / MS_PER_FRAME;
private static final int FLING_VELOCITY_POINTS = 8;
// The values we use for friction are based on a 16.6ms frame, adjust them to currentNsPerFrame:
static float getFrameAdjustedFriction(float baseFriction, long currentNsPerFrame) {
float framerateMultiplier = (float)currentNsPerFrame / NS_PER_FRAME;
return (float)Math.pow(Math.E, (Math.log(baseFriction) / framerateMultiplier));
}
static void setPrefs(Map<String, Integer> prefs) {
FRICTION_SLOW = getFloatPref(prefs, PREF_SCROLLING_FRICTION_SLOW, 850);
FRICTION_FAST = getFloatPref(prefs, PREF_SCROLLING_FRICTION_FAST, 970);
VELOCITY_THRESHOLD = 10 / FRAMERATE_MULTIPLIER;
MAX_EVENT_ACCELERATION = getFloatPref(prefs, PREF_SCROLLING_MAX_EVENT_ACCELERATION, GeckoAppShell.getDpi() > 300 ? 100 : 40);
OVERSCROLL_DECEL_RATE = getFloatPref(prefs, PREF_SCROLLING_OVERSCROLL_DECEL_RATE, 40);
SNAP_LIMIT = getFloatPref(prefs, PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT, 300);
MIN_SCROLLABLE_DISTANCE = getFloatPref(prefs, PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE, 500);
FLING_ACCEL_INTERVAL = getIntPref(prefs, PREF_FLING_ACCEL_INTERVAL, 500);
FLING_ACCEL_BASE_MULTIPLIER = getFloatPref(prefs, PREF_FLING_ACCEL_BASE_MULTIPLIER, 1000);
FLING_ACCEL_SUPPLEMENTAL_MULTIPLIER = getFloatPref(prefs, PREF_FLING_ACCEL_SUPPLEMENTAL_MULTIPLIER, 1000);
FLING_CURVE_FUNCTION_X1 = getFloatPref(prefs, PREF_FLING_CURVE_FUNCTION_X1, 410);
FLING_CURVE_FUNCTION_Y1 = getFloatPref(prefs, PREF_FLING_CURVE_FUNCTION_Y1, 0);
FLING_CURVE_FUNCTION_X2 = getFloatPref(prefs, PREF_FLING_CURVE_FUNCTION_X2, 800);
FLING_CURVE_FUNCTION_Y2 = getFloatPref(prefs, PREF_FLING_CURVE_FUNCTION_Y2, 1000);
FLING_CURVE_THRESHOLD_VELOCITY = getFloatPref(prefs, PREF_FLING_CURVE_THRESHOLD_VELOCITY, 30);
FLING_CURVE_MAXIMUM_VELOCITY = getFloatPref(prefs, PREF_FLING_CURVE_MAXIMUM_VELOCITY, 70);
FLING_CURVE_NEWTON_ITERATIONS = getIntPref(prefs, PREF_FLING_CURVE_NEWTON_ITERATIONS, 5);
Log.i(LOGTAG, "Prefs: " + FRICTION_SLOW + "," + FRICTION_FAST + "," + VELOCITY_THRESHOLD + ","
+ MAX_EVENT_ACCELERATION + "," + OVERSCROLL_DECEL_RATE + "," + SNAP_LIMIT + "," + MIN_SCROLLABLE_DISTANCE);
}
static {
// set the scrolling parameters to default values on startup
setPrefs(null);
}
private enum FlingStates {
STOPPED,
PANNING,
FLINGING,
}
private enum Overscroll {
NONE,
MINUS, // Overscrolled in the negative direction
PLUS, // Overscrolled in the positive direction
BOTH, // Overscrolled in both directions (page is zoomed to smaller than screen)
}
private final SubdocumentScrollHelper mSubscroller;
private int mOverscrollMode; /* Default to only overscrolling if we're allowed to scroll in a direction */
private float mFirstTouchPos; /* Position of the first touch event on the current drag. */
private float mTouchPos; /* Position of the most recent touch event on the current drag. */
private float mLastTouchPos; /* Position of the touch event before touchPos. */
private float mVelocity; /* Velocity in this direction; pixels per animation frame. */
private final float[] mRecentVelocities; /* Circular buffer of recent velocities since last touch start. */
private int mRecentVelocityCount; /* Number of values put into mRecentVelocities (unbounded). */
private boolean mScrollingDisabled; /* Whether movement on this axis is locked. */
private boolean mDisableSnap; /* Whether overscroll snapping is disabled. */
private float mDisplacement;
private long mLastFlingTime;
private float mLastFlingVelocity;
private FlingStates mFlingState = FlingStates.STOPPED; /* The fling state we're in on this axis. */
protected abstract float getOrigin();
protected abstract float getViewportLength();
protected abstract float getPageStart();
protected abstract float getPageLength();
protected abstract float getVisibleEndOfLayerView();
Axis(SubdocumentScrollHelper subscroller) {
mSubscroller = subscroller;
mOverscrollMode = View.OVER_SCROLL_IF_CONTENT_SCROLLS;
mRecentVelocities = new float[FLING_VELOCITY_POINTS];
}
// Implementors can override these to show effects when the axis overscrolls
protected void overscrollFling(float velocity) { }
protected void overscrollPan(float displacement) { }
public void setOverScrollMode(int overscrollMode) {
mOverscrollMode = overscrollMode;
}
public int getOverScrollMode() {
return mOverscrollMode;
}
private float getViewportEnd() {
return getOrigin() + getViewportLength();
}
private float getPageEnd() {
return getPageStart() + getPageLength();
}
void startTouch(float pos) {
mVelocity = 0.0f;
mScrollingDisabled = false;
mFirstTouchPos = mTouchPos = mLastTouchPos = pos;
mRecentVelocityCount = 0;
}
float panDistance(float currentPos) {
return currentPos - mFirstTouchPos;
}
void setScrollingDisabled(boolean disabled) {
mScrollingDisabled = disabled;
}
void saveTouchPos() {
mLastTouchPos = mTouchPos;
}
// Calculates and return the slope of the curve at given parameter t
float getSlope(float t) {
float y1 = FLING_CURVE_FUNCTION_Y1;
float y2 = FLING_CURVE_FUNCTION_Y2;
return (3 * y1)
+ t * (6 * y2 - 12 * y1)
+ t * t * (9 * y1 - 9 * y2 + 3);
}
// Calculates and returns the value of the bezier curve with the given parameter t and control points p1 and p2
float cubicBezier(float p1, float p2, float t) {
return (3 * t * (1 - t) * (1 - t) * p1)
+ (3 * t * t * (1 - t) * p2)
+ (t * t * t);
}
// Responsible for mapping the physical velocity to a the velocity obtained after applying bezier curve (with control points (X1,Y1) and (X2,Y2))
float flingCurve(float By) {
int ni = FLING_CURVE_NEWTON_ITERATIONS;
float[] guess = new float[ni];
float y1 = FLING_CURVE_FUNCTION_Y1;
float y2 = FLING_CURVE_FUNCTION_Y2;
guess[0] = By;
for (int i = 1; i < ni; i++) {
guess[i] = guess[i - 1] - (cubicBezier(y1, y2, guess[i - 1]) - By) / getSlope(guess[i - 1]);
}
// guess[4] is the final approximate root the cubic equation.
float t = guess[4];
float x1 = FLING_CURVE_FUNCTION_X1;
float x2 = FLING_CURVE_FUNCTION_X2;
return cubicBezier(x1, x2, t);
}
void updateWithTouchAt(float pos, float timeDelta) {
float curveVelocityThreshold = FLING_CURVE_THRESHOLD_VELOCITY * GeckoAppShell.getDpi() * MS_PER_FRAME;
float maxVelocity = FLING_CURVE_MAXIMUM_VELOCITY * GeckoAppShell.getDpi() * MS_PER_FRAME;
float newVelocity = (mTouchPos - pos) / timeDelta * MS_PER_FRAME;
if (Math.abs(newVelocity) > curveVelocityThreshold && Math.abs(newVelocity) < maxVelocity) {
float sign = Math.signum(newVelocity);
newVelocity = newVelocity * sign;
float scale = maxVelocity - curveVelocityThreshold;
float functInp = (newVelocity - curveVelocityThreshold) / scale;
float functOut = flingCurve(functInp);
newVelocity = functOut * scale + curveVelocityThreshold;
newVelocity = newVelocity * sign;
}
mRecentVelocities[mRecentVelocityCount % FLING_VELOCITY_POINTS] = newVelocity;
mRecentVelocityCount++;
// If there's a direction change, or current velocity is very low,
// allow setting of the velocity outright. Otherwise, use the current
// velocity and a maximum change factor to set the new velocity.
boolean curVelocityIsLow = Math.abs(mVelocity) < 1.0f / FRAMERATE_MULTIPLIER;
boolean directionChange = (mVelocity > 0) != (newVelocity > 0);
if (curVelocityIsLow || (directionChange && !FloatUtils.fuzzyEquals(newVelocity, 0.0f))) {
mVelocity = newVelocity;
} else {
float maxChange = Math.abs(mVelocity * timeDelta * MAX_EVENT_ACCELERATION);
mVelocity = Math.min(mVelocity + maxChange, Math.max(mVelocity - maxChange, newVelocity));
}
mTouchPos = pos;
}
boolean overscrolled() {
return getOverscroll() != Overscroll.NONE;
}
private Overscroll getOverscroll() {
boolean minus = (getOrigin() < getPageStart());
boolean plus = (getViewportEnd() > getPageEnd());
if (minus && plus) {
return Overscroll.BOTH;
} else if (minus) {
return Overscroll.MINUS;
} else if (plus) {
return Overscroll.PLUS;
} else {
return Overscroll.NONE;
}
}
// Returns the amount that the page has been overscrolled. If the page hasn't been
// overscrolled on this axis, returns 0.
private float getExcess() {
switch (getOverscroll()) {
case MINUS: return getPageStart() - getOrigin();
case PLUS: return getViewportEnd() - getPageEnd();
case BOTH: return (getViewportEnd() - getPageEnd()) + (getPageStart() - getOrigin());
default: return 0.0f;
}
}
/*
* Returns true if the page is zoomed in to some degree along this axis such that scrolling is
* possible and this axis has not been scroll locked while panning. Otherwise, returns false.
*/
boolean scrollable() {
// If we're scrolling a subdocument, ignore the viewport length restrictions (since those
// apply to the top-level document) and only take into account axis locking.
if (mSubscroller.scrolling()) {
return !mScrollingDisabled;
}
// if we are axis locked, return false
if (mScrollingDisabled) {
return false;
}
// there is scrollable space, and we're not disabled, or the document fits the viewport
// but we always allow overscroll anyway
return getViewportLength() <= getPageLength() - MIN_SCROLLABLE_DISTANCE ||
getOverScrollMode() == View.OVER_SCROLL_ALWAYS;
}
/*
* Returns the resistance, as a multiplier, that should be taken into account when
* tracking or pinching.
*/
float getEdgeResistance(boolean forPinching) {
float excess = getExcess();
if (excess > 0.0f && (getOverscroll() == Overscroll.BOTH || !forPinching)) {
// excess can be greater than viewport length, but the resistance
// must never drop below 0.0
return Math.max(0.0f, SNAP_LIMIT - excess / getViewportLength());
}
return 1.0f;
}
/* Returns the velocity. If the axis is locked, returns 0. */
float getRealVelocity() {
return scrollable() ? mVelocity : 0f;
}
void startPan() {
mFlingState = FlingStates.PANNING;
}
private float calculateFlingVelocity() {
int usablePoints = Math.min(mRecentVelocityCount, FLING_VELOCITY_POINTS);
if (usablePoints <= 1) {
return mVelocity;
}
float average = 0;
for (int i = 0; i < usablePoints; i++) {
average += mRecentVelocities[i];
}
return average / usablePoints;
}
float accelerate(float velocity, float lastFlingVelocity) {
return (FLING_ACCEL_BASE_MULTIPLIER * velocity + FLING_ACCEL_SUPPLEMENTAL_MULTIPLIER * lastFlingVelocity);
}
void startFling(boolean stopped) {
mDisableSnap = mSubscroller.scrolling();
if (stopped) {
mFlingState = FlingStates.STOPPED;
} else {
long now = SystemClock.uptimeMillis();
mVelocity = calculateFlingVelocity();
if ((now - mLastFlingTime < FLING_ACCEL_INTERVAL) && Math.signum(mVelocity) == Math.signum(mLastFlingVelocity)) {
mVelocity = accelerate(mVelocity, mLastFlingVelocity);
}
mFlingState = FlingStates.FLINGING;
mLastFlingVelocity = mVelocity;
mLastFlingTime = now;
}
}
/* Advances a fling animation by one step. */
boolean advanceFling(long realNsPerFrame) {
if (mFlingState != FlingStates.FLINGING) {
return false;
}
if (mSubscroller.scrolling() && !mSubscroller.lastScrollSucceeded()) {
// if the subdocument stopped scrolling, it's because it reached the end
// of the subdocument. we don't do overscroll on subdocuments, so there's
// no point in continuing this fling.
return false;
}
float excess = getExcess();
Overscroll overscroll = getOverscroll();
boolean decreasingOverscroll = false;
if ((overscroll == Overscroll.MINUS && mVelocity > 0) ||
(overscroll == Overscroll.PLUS && mVelocity < 0))
{
decreasingOverscroll = true;
}
if (mDisableSnap || FloatUtils.fuzzyEquals(excess, 0.0f) || decreasingOverscroll) {
// If we aren't overscrolled, just apply friction.
if (Math.abs(mVelocity) >= VELOCITY_THRESHOLD) {
mVelocity *= getFrameAdjustedFriction(FRICTION_FAST, realNsPerFrame);
} else {
float t = mVelocity / VELOCITY_THRESHOLD;
mVelocity *= FloatUtils.interpolate(getFrameAdjustedFriction(FRICTION_SLOW, realNsPerFrame),
getFrameAdjustedFriction(FRICTION_FAST, realNsPerFrame), t);
}
} else {
// Otherwise, decrease the velocity linearly.
float elasticity = 1.0f - excess / (getViewportLength() * SNAP_LIMIT);
float overscrollDecelRate = getFrameAdjustedFriction(OVERSCROLL_DECEL_RATE, realNsPerFrame);
if (overscroll == Overscroll.MINUS) {
mVelocity = Math.min((mVelocity + overscrollDecelRate) * elasticity, 0.0f);
} else { // must be Overscroll.PLUS
mVelocity = Math.max((mVelocity - overscrollDecelRate) * elasticity, 0.0f);
}
}
return true;
}
void stopFling() {
mVelocity = 0.0f;
mFlingState = FlingStates.STOPPED;
}
// Performs displacement of the viewport position according to the current velocity.
void displace() {
// if this isn't scrollable just return
if (!scrollable())
return;
if (mFlingState == FlingStates.PANNING)
mDisplacement += (mLastTouchPos - mTouchPos) * getEdgeResistance(false);
else
mDisplacement += mVelocity * getEdgeResistance(false);
// if overscroll is disabled and we're trying to overscroll, reset the displacement
// to remove any excess. Using getExcess alone isn't enough here since it relies on
// getOverscroll which doesn't take into account any new displacment being applied.
// If we using a subscroller, we don't want to alter the scrolling being done
if (getOverScrollMode() == View.OVER_SCROLL_NEVER && !mSubscroller.scrolling()) {
float originalDisplacement = mDisplacement;
if (mDisplacement + getOrigin() < getPageStart()) {
mDisplacement = getPageStart() - getOrigin();
} else if (mDisplacement + getOrigin() + getVisibleEndOfLayerView() > getPageEnd()) {
mDisplacement = getPageEnd() - getOrigin() - getVisibleEndOfLayerView();
}
// Return the amount of overscroll so that the overscroll controller can draw it for us
if (originalDisplacement != mDisplacement) {
if (mFlingState == FlingStates.FLINGING) {
overscrollFling(mVelocity / MS_PER_FRAME * 1000);
stopFling();
} else if (mFlingState == FlingStates.PANNING) {
overscrollPan(originalDisplacement - mDisplacement);
}
}
}
}
float resetDisplacement() {
float d = mDisplacement;
mDisplacement = 0.0f;
return d;
}
void setAutoscrollVelocity(float velocity) {
if (mFlingState != FlingStates.STOPPED) {
Log.e(LOGTAG, "Setting autoscroll velocity while in a fling is not allowed!");
return;
}
mVelocity = velocity;
}
}

Просмотреть файл

@ -1,771 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.gfx;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.PrefsHelper;
import org.mozilla.gecko.util.FloatUtils;
import org.json.JSONArray;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.Log;
import java.util.HashMap;
import java.util.Map;
final class DisplayPortCalculator {
private static final String LOGTAG = "GeckoDisplayPort";
private static final PointF ZERO_VELOCITY = new PointF(0, 0);
private static final String PREF_DISPLAYPORT_STRATEGY = "gfx.displayport.strategy";
private static final String PREF_DISPLAYPORT_FM_MULTIPLIER = "gfx.displayport.strategy_fm.multiplier";
private static final String PREF_DISPLAYPORT_FM_DANGER_X = "gfx.displayport.strategy_fm.danger_x";
private static final String PREF_DISPLAYPORT_FM_DANGER_Y = "gfx.displayport.strategy_fm.danger_y";
private static final String PREF_DISPLAYPORT_VB_MULTIPLIER = "gfx.displayport.strategy_vb.multiplier";
private static final String PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD = "gfx.displayport.strategy_vb.threshold";
private static final String PREF_DISPLAYPORT_VB_REVERSE_BUFFER = "gfx.displayport.strategy_vb.reverse_buffer";
private static final String PREF_DISPLAYPORT_VB_DANGER_X_BASE = "gfx.displayport.strategy_vb.danger_x_base";
private static final String PREF_DISPLAYPORT_VB_DANGER_Y_BASE = "gfx.displayport.strategy_vb.danger_y_base";
private static final String PREF_DISPLAYPORT_VB_DANGER_X_INCR = "gfx.displayport.strategy_vb.danger_x_incr";
private static final String PREF_DISPLAYPORT_VB_DANGER_Y_INCR = "gfx.displayport.strategy_vb.danger_y_incr";
private static final String PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD = "gfx.displayport.strategy_pb.threshold";
private static DisplayPortStrategy sStrategy = new VelocityBiasStrategy(null);
static DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
return sStrategy.calculate(metrics, (velocity == null ? ZERO_VELOCITY : velocity));
}
static boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
if (displayPort == null) {
return true;
}
return sStrategy.aboutToCheckerboard(metrics, (velocity == null ? ZERO_VELOCITY : velocity), displayPort);
}
static boolean drawTimeUpdate(long millis, int pixels) {
return sStrategy.drawTimeUpdate(millis, pixels);
}
static void resetPageState() {
sStrategy.resetPageState();
}
static void initPrefs() {
final String[] prefs = { PREF_DISPLAYPORT_STRATEGY,
PREF_DISPLAYPORT_FM_MULTIPLIER,
PREF_DISPLAYPORT_FM_DANGER_X,
PREF_DISPLAYPORT_FM_DANGER_Y,
PREF_DISPLAYPORT_VB_MULTIPLIER,
PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD,
PREF_DISPLAYPORT_VB_REVERSE_BUFFER,
PREF_DISPLAYPORT_VB_DANGER_X_BASE,
PREF_DISPLAYPORT_VB_DANGER_Y_BASE,
PREF_DISPLAYPORT_VB_DANGER_X_INCR,
PREF_DISPLAYPORT_VB_DANGER_Y_INCR,
PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD };
PrefsHelper.getPrefs(prefs, new PrefsHelper.PrefHandlerBase() {
private final Map<String, Integer> mValues = new HashMap<String, Integer>();
@Override public void prefValue(String pref, int value) {
mValues.put(pref, value);
}
@Override public void finish() {
setStrategy(mValues);
}
});
}
/**
* Set the active strategy to use.
* See the gfx.displayport.strategy pref in mobile/android/app/mobile.js to see the
* mapping between ints and strategies.
*/
static boolean setStrategy(Map<String, Integer> prefs) {
Integer strategy = prefs.get(PREF_DISPLAYPORT_STRATEGY);
if (strategy == null) {
return false;
}
switch (strategy) {
case 0:
sStrategy = new FixedMarginStrategy(prefs);
break;
case 1:
sStrategy = new VelocityBiasStrategy(prefs);
break;
case 2:
sStrategy = new DynamicResolutionStrategy(prefs);
break;
case 3:
sStrategy = new NoMarginStrategy(prefs);
break;
case 4:
sStrategy = new PredictionBiasStrategy(prefs);
break;
default:
Log.e(LOGTAG, "Invalid strategy index specified");
return false;
}
Log.i(LOGTAG, "Set strategy " + sStrategy.toString());
return true;
}
private static float getFloatPref(Map<String, Integer> prefs, String prefName, int defaultValue) {
Integer value = (prefs == null ? null : prefs.get(prefName));
return (value == null || value < 0 ? defaultValue : value) / 1000f;
}
private static abstract class DisplayPortStrategy {
/** Calculates a displayport given a viewport and panning velocity. */
public abstract DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity);
/** Returns true if a checkerboard is about to be visible and we should not throttle drawing. */
public abstract boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort);
/** Notify the strategy of a new recorded draw time. Return false to turn off draw time recording. */
public boolean drawTimeUpdate(long millis, int pixels) { return false; }
/** Reset any page-specific state stored, as the page being displayed has changed. */
public void resetPageState() {}
}
/**
* Return the dimensions for a rect that has area (width*height) that does not exceed the page size in the
* given metrics object. The area in the returned FloatSize may be less than width*height if the page is
* small, but it will never be larger than width*height.
* Note that this process may change the relative aspect ratio of the given dimensions.
*/
private static FloatSize reshapeForPage(float width, float height, ImmutableViewportMetrics metrics) {
// figure out how much of the desired buffer amount we can actually use on the horizontal axis
float usableWidth = Math.min(width, metrics.getPageWidth());
// if we reduced the buffer amount on the horizontal axis, we should take that saved memory and
// use it on the vertical axis
float extraUsableHeight = (float)Math.floor(((width - usableWidth) * height) / usableWidth);
float usableHeight = Math.min(height + extraUsableHeight, metrics.getPageHeight());
if (usableHeight < height && usableWidth == width) {
// and the reverse - if we shrunk the buffer on the vertical axis we can add it to the horizontal
float extraUsableWidth = (float)Math.floor(((height - usableHeight) * width) / usableHeight);
usableWidth = Math.min(width + extraUsableWidth, metrics.getPageWidth());
}
return new FloatSize(usableWidth, usableHeight);
}
/**
* Expand the given rect in all directions by a "danger zone". The size of the danger zone on an axis
* is the size of the view on that axis multiplied by the given multiplier. The expanded rect is then
* clamped to page bounds and returned.
*/
private static RectF expandByDangerZone(RectF rect, float dangerZoneXMultiplier, float dangerZoneYMultiplier, ImmutableViewportMetrics metrics) {
// calculate the danger zone amounts in pixels
float dangerZoneX = metrics.getWidth() * dangerZoneXMultiplier;
float dangerZoneY = metrics.getHeight() * dangerZoneYMultiplier;
rect = RectUtils.expand(rect, dangerZoneX, dangerZoneY);
// clamp to page bounds
return clampToPageBounds(rect, metrics);
}
/**
* Calculate the display port by expanding the viewport by the specified
* margins, then clamping to the page size.
*/
private static DisplayPortMetrics getPageClampedDisplayPortMetrics(RectF margins, float zoom, ImmutableViewportMetrics metrics) {
float left = metrics.viewportRectLeft - margins.left;
float top = metrics.viewportRectTop - margins.top;
float right = metrics.viewportRectRight() + margins.right;
float bottom = metrics.viewportRectBottom() + margins.bottom;
left = Math.max(metrics.pageRectLeft, left);
top = Math.max(metrics.pageRectTop, top);
right = Math.min(metrics.pageRectRight, right);
bottom = Math.min(metrics.pageRectBottom, bottom);
return new DisplayPortMetrics(left, top, right, bottom, zoom);
}
/**
* Adjust the given margins so if they are applied on the viewport in the metrics, the resulting rect
* does not exceed the page bounds. This code will maintain the total margin amount for a given axis;
* it assumes that margins.left + metrics.getWidth() + margins.right is less than or equal to
* metrics.getPageWidth(); and the same for the y axis.
*/
private static RectF shiftMarginsForPageBounds(RectF margins, ImmutableViewportMetrics metrics) {
// check how much we're overflowing in each direction. note that at most one of leftOverflow
// and rightOverflow can be greater than zero, and at most one of topOverflow and bottomOverflow
// can be greater than zero, because of the assumption described in the method javadoc.
float leftOverflow = metrics.pageRectLeft - (metrics.viewportRectLeft - margins.left);
float rightOverflow = (metrics.viewportRectRight() + margins.right) - metrics.pageRectRight;
float topOverflow = metrics.pageRectTop - (metrics.viewportRectTop - margins.top);
float bottomOverflow = (metrics.viewportRectBottom() + margins.bottom) - metrics.pageRectBottom;
// if the margins overflow the page bounds, shift them to other side on the same axis
if (leftOverflow > 0) {
margins.left -= leftOverflow;
margins.right += leftOverflow;
} else if (rightOverflow > 0) {
margins.right -= rightOverflow;
margins.left += rightOverflow;
}
if (topOverflow > 0) {
margins.top -= topOverflow;
margins.bottom += topOverflow;
} else if (bottomOverflow > 0) {
margins.bottom -= bottomOverflow;
margins.top += bottomOverflow;
}
return margins;
}
/**
* Clamp the given rect to the page bounds and return it.
*/
private static RectF clampToPageBounds(RectF rect, ImmutableViewportMetrics metrics) {
if (rect.top < metrics.pageRectTop) rect.top = metrics.pageRectTop;
if (rect.left < metrics.pageRectLeft) rect.left = metrics.pageRectLeft;
if (rect.right > metrics.pageRectRight) rect.right = metrics.pageRectRight;
if (rect.bottom > metrics.pageRectBottom) rect.bottom = metrics.pageRectBottom;
return rect;
}
/**
* This class implements the variation where we basically don't bother with a a display port.
*/
private static class NoMarginStrategy extends DisplayPortStrategy {
NoMarginStrategy(Map<String, Integer> prefs) {
// no prefs in this strategy
}
@Override
public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
return new DisplayPortMetrics(metrics.viewportRectLeft,
metrics.viewportRectTop,
metrics.viewportRectRight(),
metrics.viewportRectBottom(),
metrics.zoomFactor);
}
@Override
public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
return true;
}
@Override
public String toString() {
return "NoMarginStrategy";
}
}
/**
* This class implements the variation where we use a fixed-size margin on the display port.
* The margin is always 300 pixels in all directions, except when we are (a) approaching a page
* boundary, and/or (b) if we are limited by the page size. In these cases we try to maintain
* the area of the display port by (a) shifting the buffer to the other side on the same axis,
* and/or (b) increasing the buffer on the other axis to compensate for the reduced buffer on
* one axis.
*/
private static class FixedMarginStrategy extends DisplayPortStrategy {
// The length of each axis of the display port will be the corresponding view length
// multiplied by this factor.
private final float SIZE_MULTIPLIER;
// If the visible rect is within the danger zone (measured as a fraction of the view size
// from the edge of the displayport) we start redrawing to minimize checkerboarding.
private final float DANGER_ZONE_X_MULTIPLIER;
private final float DANGER_ZONE_Y_MULTIPLIER;
FixedMarginStrategy(Map<String, Integer> prefs) {
SIZE_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_FM_MULTIPLIER, 2000);
DANGER_ZONE_X_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_FM_DANGER_X, 100);
DANGER_ZONE_Y_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_FM_DANGER_Y, 200);
}
@Override
public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
float displayPortWidth = metrics.getWidth() * SIZE_MULTIPLIER;
float displayPortHeight = metrics.getHeight() * SIZE_MULTIPLIER;
// we need to avoid having a display port that is larger than the page, or we will end up
// painting things outside the page bounds (bug 729169). we simultaneously need to make
// the display port as large as possible so that we redraw less. reshape the display
// port dimensions to accomplish this.
FloatSize usableSize = reshapeForPage(displayPortWidth, displayPortHeight, metrics);
float horizontalBuffer = usableSize.width - metrics.getWidth();
float verticalBuffer = usableSize.height - metrics.getHeight();
// and now calculate the display port margins based on how much buffer we've decided to use and
// the page bounds, ensuring we use all of the available buffer amounts on one side or the other
// on any given axis. (i.e. if we're scrolled to the top of the page, the vertical buffer is
// entirely below the visible viewport, but if we're halfway down the page, the vertical buffer
// is split).
RectF margins = new RectF();
margins.left = horizontalBuffer / 2.0f;
margins.right = horizontalBuffer - margins.left;
margins.top = verticalBuffer / 2.0f;
margins.bottom = verticalBuffer - margins.top;
margins = shiftMarginsForPageBounds(margins, metrics);
return getPageClampedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
}
@Override
public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
// Increase the size of the viewport based on the danger zone multiplier (and clamp to page
// boundaries), and intersect it with the current displayport to determine whether we're
// close to checkerboarding.
RectF adjustedViewport = expandByDangerZone(metrics.getViewport(), DANGER_ZONE_X_MULTIPLIER, DANGER_ZONE_Y_MULTIPLIER, metrics);
return !displayPort.contains(adjustedViewport);
}
@Override
public String toString() {
return "FixedMarginStrategy mult=" + SIZE_MULTIPLIER + ", dangerX=" + DANGER_ZONE_X_MULTIPLIER + ", dangerY=" + DANGER_ZONE_Y_MULTIPLIER;
}
}
/**
* This class implements the variation with a small fixed-size margin with velocity bias.
* In this variation, the default margins are pretty small relative to the view size, but
* they are affected by the panning velocity. Specifically, if we are panning on one axis,
* we remove the margins on the other axis because we are likely axis-locked. Also once
* we are panning in one direction above a certain threshold velocity, we shift the buffer
* so that it is almost entirely in the direction of the pan, with a little bit in the
* reverse direction.
*/
private static class VelocityBiasStrategy extends DisplayPortStrategy {
// The length of each axis of the display port will be the corresponding view length
// multiplied by this factor.
private final float SIZE_MULTIPLIER;
// The velocity above which we apply the velocity bias
private final float VELOCITY_THRESHOLD;
// How much of the buffer to keep in the reverse direction of the velocity
private final float REVERSE_BUFFER;
// If the visible rect is within the danger zone we start redrawing to minimize
// checkerboarding. the danger zone amount is a linear function of the form:
// viewportsize * (base + velocity * incr)
// where base and incr are configurable values.
private final float DANGER_ZONE_BASE_X_MULTIPLIER;
private final float DANGER_ZONE_BASE_Y_MULTIPLIER;
private final float DANGER_ZONE_INCR_X_MULTIPLIER;
private final float DANGER_ZONE_INCR_Y_MULTIPLIER;
VelocityBiasStrategy(Map<String, Integer> prefs) {
SIZE_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_MULTIPLIER, 2000);
VELOCITY_THRESHOLD = GeckoAppShell.getDpi() * getFloatPref(prefs, PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD, 32);
REVERSE_BUFFER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_REVERSE_BUFFER, 200);
DANGER_ZONE_BASE_X_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_X_BASE, 1000);
DANGER_ZONE_BASE_Y_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_Y_BASE, 1000);
DANGER_ZONE_INCR_X_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_X_INCR, 0);
DANGER_ZONE_INCR_Y_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_Y_INCR, 0);
}
/**
* Split the given amounts into margins based on the VELOCITY_THRESHOLD and REVERSE_BUFFER values.
* If the velocity is above the VELOCITY_THRESHOLD on an axis, split the amount into REVERSE_BUFFER
* and 1.0 - REVERSE_BUFFER fractions. The REVERSE_BUFFER fraction is set as the margin in the
* direction opposite to the velocity, and the remaining fraction is set as the margin in the direction
* of the velocity. If the velocity is lower than VELOCITY_THRESHOLD, split the amount evenly into the
* two margins on that axis.
*/
private RectF velocityBiasedMargins(float xAmount, float yAmount, PointF velocity) {
RectF margins = new RectF();
if (velocity.x > VELOCITY_THRESHOLD) {
margins.left = xAmount * REVERSE_BUFFER;
} else if (velocity.x < -VELOCITY_THRESHOLD) {
margins.left = xAmount * (1.0f - REVERSE_BUFFER);
} else {
margins.left = xAmount / 2.0f;
}
margins.right = xAmount - margins.left;
if (velocity.y > VELOCITY_THRESHOLD) {
margins.top = yAmount * REVERSE_BUFFER;
} else if (velocity.y < -VELOCITY_THRESHOLD) {
margins.top = yAmount * (1.0f - REVERSE_BUFFER);
} else {
margins.top = yAmount / 2.0f;
}
margins.bottom = yAmount - margins.top;
return margins;
}
@Override
public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
float displayPortWidth = metrics.getWidth() * SIZE_MULTIPLIER;
float displayPortHeight = metrics.getHeight() * SIZE_MULTIPLIER;
// but if we're panning on one axis, set the margins for the other axis to zero since we are likely
// axis locked and won't be displaying that extra area.
if (Math.abs(velocity.x) > VELOCITY_THRESHOLD && FloatUtils.fuzzyEquals(velocity.y, 0)) {
displayPortHeight = metrics.getHeight();
} else if (Math.abs(velocity.y) > VELOCITY_THRESHOLD && FloatUtils.fuzzyEquals(velocity.x, 0)) {
displayPortWidth = metrics.getWidth();
}
// we need to avoid having a display port that is larger than the page, or we will end up
// painting things outside the page bounds (bug 729169).
displayPortWidth = Math.min(displayPortWidth, metrics.getPageWidth());
displayPortHeight = Math.min(displayPortHeight, metrics.getPageHeight());
float horizontalBuffer = displayPortWidth - metrics.getWidth();
float verticalBuffer = displayPortHeight - metrics.getHeight();
// split the buffer amounts into margins based on velocity, and shift it to
// take into account the page bounds
RectF margins = velocityBiasedMargins(horizontalBuffer, verticalBuffer, velocity);
margins = shiftMarginsForPageBounds(margins, metrics);
return getPageClampedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
}
@Override
public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
// calculate the danger zone amounts based on the prefs
float dangerZoneX = metrics.getWidth() * (DANGER_ZONE_BASE_X_MULTIPLIER + (velocity.x * DANGER_ZONE_INCR_X_MULTIPLIER));
float dangerZoneY = metrics.getHeight() * (DANGER_ZONE_BASE_Y_MULTIPLIER + (velocity.y * DANGER_ZONE_INCR_Y_MULTIPLIER));
// clamp it such that when added to the viewport, they don't exceed page size.
// this is a prerequisite to calling shiftMarginsForPageBounds as we do below.
dangerZoneX = Math.min(dangerZoneX, metrics.getPageWidth() - metrics.getWidth());
dangerZoneY = Math.min(dangerZoneY, metrics.getPageHeight() - metrics.getHeight());
// split the danger zone into margins based on velocity, and ensure it doesn't exceed
// page bounds.
RectF dangerMargins = velocityBiasedMargins(dangerZoneX, dangerZoneY, velocity);
dangerMargins = shiftMarginsForPageBounds(dangerMargins, metrics);
// we're about to checkerboard if the current viewport area + the danger zone margins
// fall out of the current displayport anywhere.
RectF adjustedViewport = new RectF(
metrics.viewportRectLeft - dangerMargins.left,
metrics.viewportRectTop - dangerMargins.top,
metrics.viewportRectRight() + dangerMargins.right,
metrics.viewportRectBottom() + dangerMargins.bottom);
return !displayPort.contains(adjustedViewport);
}
@Override
public String toString() {
return "VelocityBiasStrategy mult=" + SIZE_MULTIPLIER + ", threshold=" + VELOCITY_THRESHOLD + ", reverse=" + REVERSE_BUFFER
+ ", dangerBaseX=" + DANGER_ZONE_BASE_X_MULTIPLIER + ", dangerBaseY=" + DANGER_ZONE_BASE_Y_MULTIPLIER
+ ", dangerIncrX=" + DANGER_ZONE_INCR_Y_MULTIPLIER + ", dangerIncrY=" + DANGER_ZONE_INCR_Y_MULTIPLIER;
}
}
/**
* This class implements the variation where we draw more of the page at low resolution while panning.
* In this variation, as we pan faster, we increase the page area we are drawing, but reduce the draw
* resolution to compensate. This results in the same device-pixel area drawn; the compositor then
* scales this up to the viewport zoom level. This results in a large area of the page drawn but it
* looks blurry. The assumption is that drawing extra that we never display is better than checkerboarding,
* where we draw less but never even show it on the screen.
*/
private static class DynamicResolutionStrategy extends DisplayPortStrategy {
// The length of each axis of the display port will be the corresponding view length
// multiplied by this factor.
private static final float SIZE_MULTIPLIER = 1.5f;
// The velocity above which we start zooming out the display port to keep up
// with the panning.
private static final float VELOCITY_EXPANSION_THRESHOLD = GeckoAppShell.getDpi() / 16f;
// How much we increase the display port based on velocity. Assuming no friction and
// splitting (see below), this should be be the number of frames (@60fps) between us
// calculating the display port and the draw of the *next* display port getting composited
// and displayed on the screen. This is because the timeline looks like this:
// Java: pan pan pan pan pan pan ! pan pan pan pan pan pan !
// Gecko: \-> draw -> composite / \-> draw -> composite /
// The display port calculated on the first "pan" gets composited to the screen at the
// first exclamation mark, and remains on the screen until the second exclamation mark.
// In order to avoid checkerboarding, that display port must be able to contain all of
// the panning until the second exclamation mark, which encompasses two entire draw/composite
// cycles.
// If we take into account friction, our velocity multiplier should be reduced as the
// amount of pan will decrease each time. If we take into account display port splitting,
// it should be increased as the splitting means some of the display port will be used to
// draw in the opposite direction of the velocity. For now I'm assuming these two cancel
// each other out.
private static final float VELOCITY_MULTIPLIER = 60.0f;
// The following constants adjust how biased the display port is in the direction of panning.
// When panning fast (above the FAST_THRESHOLD) we use the fast split factor to split the
// display port "buffer" area, otherwise we use the slow split factor. This is based on the
// assumption that if the user is panning fast, they are less likely to reverse directions
// and go backwards, so we should spend more of our display port buffer in the direction of
// panning.
private static final float VELOCITY_FAST_THRESHOLD = VELOCITY_EXPANSION_THRESHOLD * 2.0f;
private static final float FAST_SPLIT_FACTOR = 0.95f;
private static final float SLOW_SPLIT_FACTOR = 0.8f;
// The following constants are used for viewport prediction; we use them to estimate where
// the viewport will be soon and whether or not we should trigger a draw right now. "soon"
// in the previous sentence really refers to the amount of time it would take to draw and
// composite from the point at which we do the calculation, and that is not really a known
// quantity. The velocity multiplier is how much we multiply the velocity by; it has the
// same caveats as the VELOCITY_MULTIPLIER above except that it only needs to take into account
// one draw/composite cycle instead of two. The danger zone multiplier is a multiplier of the
// viewport size that we use as an extra "danger zone" around the viewport; if this danger
// zone falls outside the display port then we are approaching the point at which we will
// checkerboard, and hence should start drawing. Note that if DANGER_ZONE_MULTIPLIER is
// greater than (SIZE_MULTIPLIER - 1.0f), then at zero velocity we will always be in the
// danger zone, and thus will be constantly drawing.
private static final float PREDICTION_VELOCITY_MULTIPLIER = 30.0f;
private static final float DANGER_ZONE_MULTIPLIER = 0.20f; // must be less than (SIZE_MULTIPLIER - 1.0f)
DynamicResolutionStrategy(Map<String, Integer> prefs) {
// ignore prefs for now
}
@Override
public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
float displayPortWidth = metrics.getWidth() * SIZE_MULTIPLIER;
float displayPortHeight = metrics.getHeight() * SIZE_MULTIPLIER;
// for resolution calculation purposes, we need to know what the adjusted display port dimensions
// would be if we had zero velocity, so calculate that here before we increase the display port
// based on velocity.
FloatSize reshapedSize = reshapeForPage(displayPortWidth, displayPortHeight, metrics);
// increase displayPortWidth and displayPortHeight based on the velocity, but maintaining their
// relative aspect ratio.
if (velocity.length() > VELOCITY_EXPANSION_THRESHOLD) {
float velocityFactor = Math.max(Math.abs(velocity.x) / displayPortWidth,
Math.abs(velocity.y) / displayPortHeight);
velocityFactor *= VELOCITY_MULTIPLIER;
displayPortWidth += (displayPortWidth * velocityFactor);
displayPortHeight += (displayPortHeight * velocityFactor);
}
// at this point, displayPortWidth and displayPortHeight are how much of the page (in device pixels)
// we want to be rendered by Gecko. Note here "device pixels" is equivalent to CSS pixels multiplied
// by metrics.zoomFactor
// we need to avoid having a display port that is larger than the page, or we will end up
// painting things outside the page bounds (bug 729169). we simultaneously need to make
// the display port as large as possible so that we redraw less. reshape the display
// port dimensions to accomplish this. this may change the aspect ratio of the display port,
// but we are assuming that this is desirable because the advantages from pre-drawing will
// outweigh the disadvantages from any buffer reallocations that might occur.
FloatSize usableSize = reshapeForPage(displayPortWidth, displayPortHeight, metrics);
float horizontalBuffer = usableSize.width - metrics.getWidth();
float verticalBuffer = usableSize.height - metrics.getHeight();
// at this point, horizontalBuffer and verticalBuffer are the dimensions of the buffer area we have.
// the buffer area is the off-screen area that is part of the display port and will be pre-drawn in case
// the user scrolls there. we now need to split the buffer area on each axis so that we know
// what the exact margins on each side will be. first we split the buffer amount based on the direction
// we're moving, so that we have a larger buffer in the direction of travel.
RectF margins = new RectF();
margins.left = splitBufferByVelocity(horizontalBuffer, velocity.x);
margins.right = horizontalBuffer - margins.left;
margins.top = splitBufferByVelocity(verticalBuffer, velocity.y);
margins.bottom = verticalBuffer - margins.top;
// then, we account for running into the page bounds - so that if we hit the top of the page, we need
// to drop the top margin and move that amount to the bottom margin.
margins = shiftMarginsForPageBounds(margins, metrics);
// finally, we calculate the resolution we want to render the display port area at. We do this
// so that as we expand the display port area (because of velocity), we reduce the resolution of
// the painted area so as to maintain the size of the buffer Gecko is painting into. we calculate
// the reduction in resolution by comparing the display port size with and without the velocity
// changes applied.
// this effectively means that as we pan faster and faster, the display port grows, but we paint
// at lower resolutions. this paints more area to reduce checkerboard at the cost of increasing
// compositor-scaling and blurriness. Once we stop panning, the blurriness must be entirely gone.
// Note that usable* could be less than base* if we are pinch-zoomed out into overscroll, so we
// clamp it to make sure this doesn't increase our display resolution past metrics.zoomFactor.
float scaleFactor = Math.min(reshapedSize.width / usableSize.width, reshapedSize.height / usableSize.height);
float displayResolution = metrics.zoomFactor * Math.min(1.0f, scaleFactor);
DisplayPortMetrics dpMetrics = new DisplayPortMetrics(
metrics.viewportRectLeft - margins.left,
metrics.viewportRectTop - margins.top,
metrics.viewportRectRight() + margins.right,
metrics.viewportRectBottom() + margins.bottom,
displayResolution);
return dpMetrics;
}
/**
* Split the given buffer amount into two based on the velocity.
* Given an amount of total usable buffer on an axis, this will
* return the amount that should be used on the left/top side of
* the axis (the side which a negative velocity vector corresponds
* to).
*/
private float splitBufferByVelocity(float amount, float velocity) {
// if no velocity, so split evenly
if (FloatUtils.fuzzyEquals(velocity, 0)) {
return amount / 2.0f;
}
// if we're moving quickly, assign more of the amount in that direction
// since is is less likely that we will reverse direction immediately
if (velocity < -VELOCITY_FAST_THRESHOLD) {
return amount * FAST_SPLIT_FACTOR;
}
if (velocity > VELOCITY_FAST_THRESHOLD) {
return amount * (1.0f - FAST_SPLIT_FACTOR);
}
// if we're moving slowly, then assign less of the amount in that direction
if (velocity < 0) {
return amount * SLOW_SPLIT_FACTOR;
} else {
return amount * (1.0f - SLOW_SPLIT_FACTOR);
}
}
@Override
public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
// Expand the viewport based on our velocity (and clamp it to page boundaries).
// Then intersect it with the last-requested displayport to determine whether we're
// close to checkerboarding.
RectF predictedViewport = metrics.getViewport();
// first we expand the viewport in the direction we're moving based on some
// multiple of the current velocity.
if (velocity.length() > 0) {
if (velocity.x < 0) {
predictedViewport.left += velocity.x * PREDICTION_VELOCITY_MULTIPLIER;
} else if (velocity.x > 0) {
predictedViewport.right += velocity.x * PREDICTION_VELOCITY_MULTIPLIER;
}
if (velocity.y < 0) {
predictedViewport.top += velocity.y * PREDICTION_VELOCITY_MULTIPLIER;
} else if (velocity.y > 0) {
predictedViewport.bottom += velocity.y * PREDICTION_VELOCITY_MULTIPLIER;
}
}
// then we expand the viewport evenly in all directions just to have an extra
// safety zone. this also clamps it to page bounds.
predictedViewport = expandByDangerZone(predictedViewport, DANGER_ZONE_MULTIPLIER, DANGER_ZONE_MULTIPLIER, metrics);
return !displayPort.contains(predictedViewport);
}
@Override
public String toString() {
return "DynamicResolutionStrategy";
}
}
/**
* This class implements the variation where we use the draw time to predict where we will be when
* a draw completes, and draw that instead of where we are now. In this variation, when our panning
* speed drops below a certain threshold, we draw 9 viewports' worth of content so that the user can
* pan in any direction without encountering checkerboarding.
* Once the user is panning, we modify the displayport to encompass an area range of where we think
* the user will be when the draw completes. This heuristic relies on both the estimated draw time
* the panning velocity; unexpected changes in either of these values will cause the heuristic to
* fail and show checkerboard.
*/
private static class PredictionBiasStrategy extends DisplayPortStrategy {
private static float VELOCITY_THRESHOLD;
private int mPixelArea; // area of the viewport, used in draw time calculations
private int mMinFramesToDraw; // minimum number of frames we take to draw
private int mMaxFramesToDraw; // maximum number of frames we take to draw
PredictionBiasStrategy(Map<String, Integer> prefs) {
VELOCITY_THRESHOLD = GeckoAppShell.getDpi() * getFloatPref(prefs, PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD, 16);
resetPageState();
}
@Override
public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
float width = metrics.getWidth();
float height = metrics.getHeight();
mPixelArea = (int)(width * height);
if (velocity.length() < VELOCITY_THRESHOLD) {
// if we're going slow, expand the displayport to 9x viewport size
RectF margins = new RectF(width, height, width, height);
return getPageClampedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
}
// figure out how far we expect to be
float minDx = velocity.x * mMinFramesToDraw;
float minDy = velocity.y * mMinFramesToDraw;
float maxDx = velocity.x * mMaxFramesToDraw;
float maxDy = velocity.y * mMaxFramesToDraw;
// figure out how many pixels we will be drawing when we draw the above-calculated range.
// this will be larger than the viewport area.
float pixelsToDraw = (width + Math.abs(maxDx - minDx)) * (height + Math.abs(maxDy - minDy));
// adjust how far we will get because of the time spent drawing all these extra pixels. this
// will again increase the number of pixels drawn so really we could keep iterating this over
// and over, but once seems enough for now.
maxDx = maxDx * pixelsToDraw / mPixelArea;
maxDy = maxDy * pixelsToDraw / mPixelArea;
// and finally generate the displayport. the min/max stuff takes care of
// negative velocities as well as positive.
RectF margins = new RectF(
-Math.min(minDx, maxDx),
-Math.min(minDy, maxDy),
Math.max(minDx, maxDx),
Math.max(minDy, maxDy));
return getPageClampedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
}
@Override
public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
// the code below is the same as in calculate() but is awkward to refactor since it has multiple outputs.
// refer to the comments in calculate() to understand what this is doing.
float minDx = velocity.x * mMinFramesToDraw;
float minDy = velocity.y * mMinFramesToDraw;
float maxDx = velocity.x * mMaxFramesToDraw;
float maxDy = velocity.y * mMaxFramesToDraw;
float pixelsToDraw = (metrics.getWidth() + Math.abs(maxDx - minDx)) * (metrics.getHeight() + Math.abs(maxDy - minDy));
maxDx = maxDx * pixelsToDraw / mPixelArea;
maxDy = maxDy * pixelsToDraw / mPixelArea;
// now that we have an idea of how far we will be when the draw completes, take the farthest
// end of that range and see if it falls outside the displayport bounds. if it does, allow
// the draw to go through
RectF predictedViewport = metrics.getViewport();
predictedViewport.left += maxDx;
predictedViewport.top += maxDy;
predictedViewport.right += maxDx;
predictedViewport.bottom += maxDy;
predictedViewport = clampToPageBounds(predictedViewport, metrics);
return !displayPort.contains(predictedViewport);
}
@Override
public boolean drawTimeUpdate(long millis, int pixels) {
// calculate the number of frames it took to draw a viewport-sized area
float normalizedTime = (float)mPixelArea * millis / pixels;
int normalizedFrames = (int) Math.ceil(normalizedTime * 60f / 1000f);
// broaden our range on how long it takes to draw if the draw falls outside
// the range. this allows it to grow gradually. this heuristic may need to
// be tweaked into more of a floating window average or something.
if (normalizedFrames <= mMinFramesToDraw) {
mMinFramesToDraw--;
} else if (normalizedFrames > mMaxFramesToDraw) {
mMaxFramesToDraw++;
} else {
return true;
}
Log.d(LOGTAG, "Widened draw range to [" + mMinFramesToDraw + ", " + mMaxFramesToDraw + "]");
return true;
}
@Override
public void resetPageState() {
mMinFramesToDraw = 0;
mMaxFramesToDraw = 2;
}
@Override
public String toString() {
return "PredictionBiasStrategy threshold=" + VELOCITY_THRESHOLD;
}
}
}

Просмотреть файл

@ -1,78 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.gfx;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.util.FloatUtils;
import android.graphics.RectF;
/*
* This class keeps track of the area we request Gecko to paint, as well
* as the resolution of the paint. The area may be different from the visible
* area of the page, and the resolution may be different from the resolution
* used in the compositor to render the page. This is so that we can ask Gecko
* to paint a much larger area without using extra memory, and then render some
* subsection of that with compositor scaling.
*/
public final class DisplayPortMetrics {
@WrapForJNI(calledFrom = "gecko")
public final float resolution;
@WrapForJNI(calledFrom = "gecko")
private final RectF mPosition;
public DisplayPortMetrics() {
this(0, 0, 0, 0, 1);
}
@WrapForJNI(calledFrom = "gecko")
public DisplayPortMetrics(float left, float top, float right, float bottom, float resolution) {
this.resolution = resolution;
mPosition = new RectF(left, top, right, bottom);
}
public float getLeft() {
return mPosition.left;
}
public float getTop() {
return mPosition.top;
}
public float getRight() {
return mPosition.right;
}
public float getBottom() {
return mPosition.bottom;
}
public boolean contains(RectF rect) {
return mPosition.contains(rect);
}
public boolean fuzzyEquals(DisplayPortMetrics metrics) {
return RectUtils.fuzzyEquals(mPosition, metrics.mPosition)
&& FloatUtils.fuzzyEquals(resolution, metrics.resolution);
}
public String toJSON() {
StringBuilder sb = new StringBuilder(256);
sb.append("{ \"left\": ").append(mPosition.left)
.append(", \"top\": ").append(mPosition.top)
.append(", \"right\": ").append(mPosition.right)
.append(", \"bottom\": ").append(mPosition.bottom)
.append(", \"resolution\": ").append(resolution)
.append('}');
return sb.toString();
}
@Override
public String toString() {
return "DisplayPortMetrics v=(" + mPosition.left + "," + mPosition.top + "," + mPosition.right + ","
+ mPosition.bottom + ") z=" + resolution;
}
}

Просмотреть файл

@ -1,94 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.gfx;
import android.os.SystemClock;
/**
* A custom-built data structure to assist with measuring draw times.
*
* This class maintains a fixed-size circular buffer of DisplayPortMetrics
* objects and associated timestamps. It provides only three operations, which
* is all we require for our purposes of measuring draw times. Note
* in particular that the class is designed so that even though it is
* accessed from multiple threads, it does not require synchronization;
* any concurrency errors that result from this are handled gracefully.
*
* Assuming an unrolled buffer so that mTail is greater than mHead, the data
* stored in the buffer at entries [mHead, mTail) will never be modified, and
* so are "safe" to read. If this reading is done on the same thread that
* owns mHead, then reading the range [mHead, mTail) is guaranteed to be safe
* since the range itself will not shrink.
*/
final class DrawTimingQueue {
private static final String LOGTAG = "GeckoDrawTimingQueue";
private static final int BUFFER_SIZE = 16;
private final DisplayPortMetrics[] mMetrics;
private final long[] mTimestamps;
private int mHead;
private int mTail;
DrawTimingQueue() {
mMetrics = new DisplayPortMetrics[BUFFER_SIZE];
mTimestamps = new long[BUFFER_SIZE];
mHead = BUFFER_SIZE - 1;
}
/**
* Add a new entry to the tail of the queue. If the buffer is full,
* do nothing. This must only be called from the Java UI thread.
*/
boolean add(DisplayPortMetrics metrics) {
if (mHead == mTail) {
return false;
}
mMetrics[mTail] = metrics;
mTimestamps[mTail] = SystemClock.uptimeMillis();
mTail = (mTail + 1) % BUFFER_SIZE;
return true;
}
/**
* Find the timestamp associated with the given metrics, AND remove
* all metrics objects from the start of the queue up to and including
* the one provided. Note that because of draw coalescing, the metrics
* object passed in here may not be the one at the head of the queue,
* and so we must iterate our way through the list to find it.
* This must only be called from the compositor thread.
*/
long findTimeFor(DisplayPortMetrics metrics) {
// keep a copy of the tail pointer so that we ignore new items
// added to the queue while we are searching. this is fine because
// the one we are looking for will either have been added already
// or will not be in the queue at all.
int tail = mTail;
// walk through the "safe" range from mHead to tail; these entries
// will not be modified unless we change mHead.
int i = (mHead + 1) % BUFFER_SIZE;
while (i != tail) {
if (mMetrics[i].fuzzyEquals(metrics)) {
// found it, copy out the timestamp to a local var BEFORE
// changing mHead or add could clobber the timestamp.
long timestamp = mTimestamps[i];
mHead = i;
return timestamp;
}
i = (i + 1) % BUFFER_SIZE;
}
return -1;
}
/**
* Reset the buffer to empty.
* This must only be called from the compositor thread.
*/
void reset() {
// we can only modify mHead on this thread.
mHead = (mTail + BUFFER_SIZE - 1) % BUFFER_SIZE;
}
}

Просмотреть файл

@ -39,20 +39,6 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
private final Context mContext;
private IntSize mScreenSize;
private IntSize mWindowSize;
private DisplayPortMetrics mDisplayPort;
private boolean mRecordDrawTimes;
private final DrawTimingQueue mDrawTimingQueue;
/* The Gecko viewport as per the UI thread. Must be touched only on the UI thread.
* If any events being sent to Gecko that are relative to the Gecko viewport position,
* they must (a) be relative to this viewport, and (b) be sent on the UI thread to
* avoid races. As long as these two conditions are satisfied, and the events being
* sent to Gecko are processed in FIFO order, the events will properly be relative
* to the Gecko viewport position. Note that if Gecko updates its viewport independently,
* we get notified synchronously and also update this on the UI thread.
*/
private ImmutableViewportMetrics mGeckoViewport;
/*
* The viewport metrics being used to draw the current frame. This is only
@ -65,12 +51,6 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
/* Used as temporaries by syncViewportInfo */
private final ViewTransform mCurrentViewTransform;
/* Used as the return value of progressiveUpdateCallback */
private final ProgressiveUpdateData mProgressiveUpdateData;
private DisplayPortMetrics mProgressiveUpdateDisplayPort;
private boolean mLastProgressiveUpdateWasLowPrecision;
private boolean mProgressiveUpdateWasInDanger;
private boolean mForceRedraw;
/* The current viewport metrics.
@ -113,12 +93,7 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
mContext = context;
mScreenSize = new IntSize(0, 0);
mWindowSize = new IntSize(0, 0);
mDisplayPort = new DisplayPortMetrics();
mRecordDrawTimes = true;
mDrawTimingQueue = new DrawTimingQueue();
mCurrentViewTransform = new ViewTransform(0, 0, 1);
mProgressiveUpdateData = new ProgressiveUpdateData();
mProgressiveUpdateDisplayPort = new DisplayPortMetrics();
mForceRedraw = true;
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
@ -157,8 +132,6 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
sendResizeEventIfNecessary(true, null);
DisplayPortCalculator.initPrefs();
// Gecko being ready is one of the two conditions (along with having an available
// surface) that cause us to create the compositor. So here, now that we know gecko
// is ready, call updateCompositor() to see if we can actually do the creation.
@ -294,7 +267,7 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
// Page size is owned by the layer client, so no need to notify it of
// this change.
post(new Runnable() {
mView.post(new Runnable() {
@Override
public void run() {
mView.requestRender();
@ -342,17 +315,10 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
.setZoomFactor(zoom)
.setPageRect(pageRect, cssPageRect);
// Since we have switched to displaying a different document, we need to update any
// viewport-related state we have lying around. This includes mGeckoViewport and
// mViewportMetrics. Usually this information is updated via handleViewportMessage
// viewport-related state we have lying around (i.e. mViewportMetrics).
// Usually this information is updated via handleViewportMessage
// while we remain on the same document.
post(new Runnable() {
@Override
public void run() {
mGeckoViewport = newMetrics;
}
});
setViewportMetrics(newMetrics);
setViewportMetrics(newMetrics, true);
// Indicate that the document is about to be composited so the
// LayerView background can be removed.
@ -360,8 +326,6 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
mView.setPaintState(LayerView.PAINT_BEFORE_FIRST);
}
}
DisplayPortCalculator.resetPageState();
mDrawTimingQueue.reset();
mContentDocumentIsDisplayed = true;
}
@ -406,19 +370,6 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
}
mToolbarAnimator.populateViewTransform(mCurrentViewTransform, mFrameMetrics);
if (layersUpdated && mRecordDrawTimes) {
// If we got a layers update, that means a draw finished. Check to see if the area drawn matches
// one of our requested displayports; if it does calculate the draw time and notify the
// DisplayPortCalculator
DisplayPortMetrics drawn = new DisplayPortMetrics(x, y, x + width, y + height, resolution);
long time = mDrawTimingQueue.findTimeFor(drawn);
if (time >= 0) {
long now = SystemClock.uptimeMillis();
time = now - time;
mRecordDrawTimes = DisplayPortCalculator.drawTimeUpdate(time, width * height);
}
}
if (layersUpdated) {
for (DrawListener listener : mDrawListeners) {
listener.drawFinished();
@ -678,7 +629,7 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
}
}
private void geometryChanged(DisplayPortMetrics displayPort) {
private void geometryChanged() {
/* Let Gecko know if the screensize has changed */
sendResizeEventIfNecessary(false, null);
}
@ -690,44 +641,10 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
setViewportSize(viewportSize.width, viewportSize.height, null);
}
/** Implementation of PanZoomTarget */
@Override
public ImmutableViewportMetrics getViewportMetrics() {
ImmutableViewportMetrics getViewportMetrics() {
return mViewportMetrics;
}
/** Implementation of PanZoomTarget */
@Override
public FullScreenState getFullScreenState() {
return mView.getFullScreenState();
}
/** Implementation of PanZoomTarget */
@Override
public PointF getVisibleEndOfLayerView() {
return mToolbarAnimator.getVisibleEndOfLayerView();
}
/** Implementation of PanZoomTarget */
@Override
public void setAnimationTarget(ImmutableViewportMetrics metrics) {
if (mGeckoIsReady) {
// We know what the final viewport of the animation is going to be, so
// immediately request a draw of that area by setting the display port
// accordingly. This way we should have the content pre-rendered by the
// time the animation is done.
DisplayPortMetrics displayPort = DisplayPortCalculator.calculate(metrics, null);
}
}
/** Implementation of PanZoomTarget
* You must hold the monitor while calling this.
*/
@Override
public void setViewportMetrics(ImmutableViewportMetrics metrics) {
setViewportMetrics(metrics, true);
}
/*
* You must hold the monitor while calling this.
*/
@ -751,7 +668,7 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
mView.requestRender();
if (notifyGecko && mGeckoIsReady) {
geometryChanged(null);
geometryChanged();
}
}
@ -768,70 +685,24 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
viewportMetricsChanged(notifyGecko);
}
/** Implementation of PanZoomTarget
* Scroll the viewport by a certain amount. This will take viewport margins
* and margin animation into account. If margins are currently animating,
* this will just go ahead and modify the viewport origin, otherwise the
* delta will be applied to the margins and the remainder will be applied to
* the viewport origin.
*
* You must hold the monitor while calling this.
*/
@Override
public void scrollBy(float dx, float dy) {
// Set mViewportMetrics manually so the margin changes take.
mViewportMetrics = mViewportMetrics.offsetViewportBy(dx, dy);
viewportMetricsChanged(true);
}
/** Implementation of PanZoomTarget */
@Override
public void panZoomStopped() {
mToolbarAnimator.onPanZoomStopped();
}
/** Implementation of PanZoomTarget */
@Override
public void forceRedraw(DisplayPortMetrics displayPort) {
mForceRedraw = true;
if (mGeckoIsReady) {
geometryChanged(displayPort);
}
}
/** Implementation of PanZoomTarget */
@Override
public boolean post(Runnable action) {
return mView.post(action);
}
/** Implementation of PanZoomTarget */
@Override
public void postRenderTask(RenderTask task) {
mView.postRenderTask(task);
}
/** Implementation of PanZoomTarget */
@Override
public void removeRenderTask(RenderTask task) {
mView.removeRenderTask(task);
}
/** Implementation of PanZoomTarget */
@Override
public Object getLock() {
Object getLock() {
return this;
}
/** Implementation of PanZoomTarget
/**
* Converts a point from layer view coordinates to layer coordinates. In other words, given a
* point measured in pixels from the top left corner of the layer view, returns the point in
* pixels measured from the last scroll position we sent to Gecko, in CSS pixels. Assuming the
* events being sent to Gecko are processed in FIFO order, this calculation should always be
* correct.
*/
@Override
public PointF convertViewPointToLayerPoint(PointF viewPoint) {
PointF convertViewPointToLayerPoint(PointF viewPoint) {
if (!mGeckoIsReady) {
return null;
}
@ -855,8 +726,7 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
return layerPoint;
}
@Override
public Matrix getMatrixForLayerRectToViewRect() {
Matrix getMatrixForLayerRectToViewRect() {
if (!mGeckoIsReady) {
return null;
}

Просмотреть файл

@ -160,11 +160,7 @@ public class LayerView extends FrameLayout {
mPaintState = PAINT_START;
mFullScreenState = FullScreenState.NONE;
if (Versions.feature14Plus) {
mOverscroll = new OverscrollEdgeEffect(this);
} else {
mOverscroll = null;
}
mOverscroll = new OverscrollEdgeEffect(this);
}
public LayerView(Context context) {
@ -676,10 +672,6 @@ public class LayerView extends FrameLayout {
return mFullScreenState != FullScreenState.NONE;
}
public FullScreenState getFullScreenState() {
return mFullScreenState;
}
public void setMaxTranslation(float aMaxTranslation) {
mToolbarAnimator.setMaxTranslation(aMaxTranslation);
}

Просмотреть файл

@ -9,23 +9,7 @@ import android.graphics.Matrix;
import android.graphics.PointF;
public interface PanZoomTarget {
public ImmutableViewportMetrics getViewportMetrics();
public FullScreenState getFullScreenState();
public PointF getVisibleEndOfLayerView();
public void setAnimationTarget(ImmutableViewportMetrics viewport);
public void setViewportMetrics(ImmutableViewportMetrics viewport);
public void scrollBy(float dx, float dy);
public void panZoomStopped();
/** This triggers an (asynchronous) viewport update/redraw. */
public void forceRedraw(DisplayPortMetrics displayPort);
public boolean isGeckoReady();
public boolean post(Runnable action);
public void postRenderTask(RenderTask task);
public void removeRenderTask(RenderTask task);
public Object getLock();
public PointF convertViewPointToLayerPoint(PointF viewPoint);
public Matrix getMatrixForLayerRectToViewRect();
public void setScrollingRootContent(boolean isRootContent);
}

Просмотреть файл

@ -1,322 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.gfx;
import org.json.JSONException;
import android.graphics.PointF;
import android.util.Log;
import android.view.MotionEvent;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Stack;
/**
* A less buggy, and smoother, replacement for the built-in Android ScaleGestureDetector.
*
* This gesture detector is more reliable than the built-in ScaleGestureDetector because:
*
* - It doesn't assume that pointer IDs are numbered 0 and 1.
*
* - It doesn't attempt to correct for "slop" when resting one's hand on the device. On some
* devices (e.g. the Droid X) this can cause the ScaleGestureDetector to lose track of how many
* pointers are down, with disastrous results (bug 706684).
*
* - Cancelling a zoom into a pan is handled correctly.
*
* - Starting with three or more fingers down, releasing fingers so that only two are down, and
* then performing a scale gesture is handled correctly.
*
* - It doesn't take pressure into account, which results in smoother scaling.
*/
class SimpleScaleGestureDetector {
private static final String LOGTAG = "GeckoSimpleScaleGestureDetector";
private final SimpleScaleGestureListener mListener;
private long mLastEventTime;
private boolean mScaleResult;
/* Information about all pointers that are down. */
private final LinkedList<PointerInfo> mPointerInfo;
/** Creates a new gesture detector with the given listener. */
SimpleScaleGestureDetector(SimpleScaleGestureListener listener) {
mListener = listener;
mPointerInfo = new LinkedList<PointerInfo>();
}
/** Forward touch events to this function. */
public void onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
// If we get ACTION_DOWN while still tracking any pointers,
// something is wrong. Cancel the current gesture and start over.
if (getPointersDown() > 0)
onTouchEnd(event);
onTouchStart(event);
break;
case MotionEvent.ACTION_POINTER_DOWN:
onTouchStart(event);
break;
case MotionEvent.ACTION_MOVE:
onTouchMove(event);
break;
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
onTouchEnd(event);
break;
}
}
private int getPointersDown() {
return mPointerInfo.size();
}
private int getActionIndex(MotionEvent event) {
return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
}
private void onTouchStart(MotionEvent event) {
mLastEventTime = event.getEventTime();
mPointerInfo.addFirst(PointerInfo.create(event, getActionIndex(event)));
if (getPointersDown() == 2) {
sendScaleGesture(EventType.BEGIN);
}
}
private void onTouchMove(MotionEvent event) {
mLastEventTime = event.getEventTime();
for (int i = 0; i < event.getPointerCount(); i++) {
PointerInfo pointerInfo = pointerInfoForEventIndex(event, i);
if (pointerInfo != null) {
pointerInfo.populate(event, i);
}
}
if (getPointersDown() == 2) {
sendScaleGesture(EventType.CONTINUE);
}
}
private void onTouchEnd(MotionEvent event) {
mLastEventTime = event.getEventTime();
int action = event.getAction() & MotionEvent.ACTION_MASK;
boolean isCancel = (action == MotionEvent.ACTION_CANCEL ||
action == MotionEvent.ACTION_DOWN);
int id = event.getPointerId(getActionIndex(event));
ListIterator<PointerInfo> iterator = mPointerInfo.listIterator();
while (iterator.hasNext()) {
PointerInfo pointerInfo = iterator.next();
if (!(isCancel || pointerInfo.getId() == id)) {
continue;
}
// One of the pointers we were tracking was lifted. Remove its info object from the
// list, recycle it to avoid GC pauses, and send an onScaleEnd() notification if this
// ended the gesture.
iterator.remove();
pointerInfo.recycle();
if (getPointersDown() == 1) {
sendScaleGesture(EventType.END);
}
}
}
/**
* Returns the X coordinate of the focus location (the midpoint of the two fingers). If only
* one finger is down, returns the location of that finger.
*/
public float getFocusX() {
switch (getPointersDown()) {
case 1:
return mPointerInfo.getFirst().getCurrent().x;
case 2:
PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
return (pointerA.getCurrent().x + pointerB.getCurrent().x) / 2.0f;
}
Log.e(LOGTAG, "No gesture taking place in getFocusX()!");
return 0.0f;
}
/**
* Returns the Y coordinate of the focus location (the midpoint of the two fingers). If only
* one finger is down, returns the location of that finger.
*/
public float getFocusY() {
switch (getPointersDown()) {
case 1:
return mPointerInfo.getFirst().getCurrent().y;
case 2:
PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
return (pointerA.getCurrent().y + pointerB.getCurrent().y) / 2.0f;
}
Log.e(LOGTAG, "No gesture taking place in getFocusY()!");
return 0.0f;
}
/** Returns the most recent distance between the two pointers. */
public float getCurrentSpan() {
if (getPointersDown() != 2) {
Log.e(LOGTAG, "No gesture taking place in getCurrentSpan()!");
return 0.0f;
}
PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
return PointUtils.distance(pointerA.getCurrent(), pointerB.getCurrent());
}
/** Returns the second most recent distance between the two pointers. */
public float getPreviousSpan() {
if (getPointersDown() != 2) {
Log.e(LOGTAG, "No gesture taking place in getPreviousSpan()!");
return 0.0f;
}
PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
PointF a = pointerA.getPrevious(), b = pointerB.getPrevious();
if (a == null || b == null) {
a = pointerA.getCurrent();
b = pointerB.getCurrent();
}
return PointUtils.distance(a, b);
}
/** Returns the time of the last event related to the gesture. */
public long getEventTime() {
return mLastEventTime;
}
/** Returns true if the scale gesture is in progress and false otherwise. */
public boolean isInProgress() {
return getPointersDown() == 2;
}
/* Sends the requested scale gesture notification to the listener. */
private void sendScaleGesture(EventType eventType) {
switch (eventType) {
case BEGIN:
mScaleResult = mListener.onScaleBegin(this);
break;
case CONTINUE:
if (mScaleResult) {
mListener.onScale(this);
}
break;
case END:
if (mScaleResult) {
mListener.onScaleEnd(this);
}
break;
}
}
/*
* Returns the pointer info corresponding to the given pointer index, or null if the pointer
* isn't one that's being tracked.
*/
private PointerInfo pointerInfoForEventIndex(MotionEvent event, int index) {
int id = event.getPointerId(index);
for (PointerInfo pointerInfo : mPointerInfo) {
if (pointerInfo.getId() == id) {
return pointerInfo;
}
}
return null;
}
private enum EventType {
BEGIN,
CONTINUE,
END,
}
/* Encapsulates information about one of the two fingers involved in the gesture. */
private static class PointerInfo {
/* A free list that recycles pointer info objects, to reduce GC pauses. */
private static Stack<PointerInfo> sPointerInfoFreeList;
private int mId;
private PointF mCurrent, mPrevious;
private PointerInfo() {
// External users should use create() instead.
}
/* Creates or recycles a new PointerInfo instance from an event and a pointer index. */
public static PointerInfo create(MotionEvent event, int index) {
if (sPointerInfoFreeList == null) {
sPointerInfoFreeList = new Stack<PointerInfo>();
}
PointerInfo pointerInfo;
if (sPointerInfoFreeList.empty()) {
pointerInfo = new PointerInfo();
} else {
pointerInfo = sPointerInfoFreeList.pop();
}
pointerInfo.populate(event, index);
return pointerInfo;
}
/*
* Fills in the fields of this instance from the given motion event and pointer index
* within that event.
*/
public void populate(MotionEvent event, int index) {
mId = event.getPointerId(index);
mPrevious = mCurrent;
mCurrent = new PointF(event.getX(index), event.getY(index));
}
public void recycle() {
mId = -1;
mPrevious = mCurrent = null;
sPointerInfoFreeList.push(this);
}
public int getId() { return mId; }
public PointF getCurrent() { return mCurrent; }
public PointF getPrevious() { return mPrevious; }
@Override
public String toString() {
if (mId == -1) {
return "(up)";
}
try {
String prevString;
if (mPrevious == null) {
prevString = "n/a";
} else {
prevString = PointUtils.toJSON(mPrevious).toString();
}
// The current position should always be non-null.
String currentString = PointUtils.toJSON(mCurrent).toString();
return "id=" + mId + " cur=" + currentString + " prev=" + prevString;
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
}
public static interface SimpleScaleGestureListener {
public boolean onScale(SimpleScaleGestureDetector detector);
public boolean onScaleBegin(SimpleScaleGestureDetector detector);
public void onScaleEnd(SimpleScaleGestureDetector detector);
}
}

Просмотреть файл

@ -1,141 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.gfx;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.util.GeckoEventListener;
import org.json.JSONException;
import org.json.JSONObject;
import android.graphics.PointF;
import android.os.Handler;
import android.util.Log;
class SubdocumentScrollHelper implements GeckoEventListener {
private static final String LOGTAG = "GeckoSubdocScroll";
private static final String MESSAGE_PANNING_OVERRIDE = "Panning:Override";
private static final String MESSAGE_CANCEL_OVERRIDE = "Panning:CancelOverride";
private static final String MESSAGE_SCROLL = "Gesture:Scroll";
private static final String MESSAGE_SCROLL_ACK = "Gesture:ScrollAck";
private final Handler mUiHandler;
private final EventDispatcher mEventDispatcher;
/* This is the amount of displacement we have accepted but not yet sent to JS; this is
* only valid when mOverrideScrollPending is true. */
private final PointF mPendingDisplacement;
/* When this is true, we're sending scroll events to JS to scroll the active subdocument. */
private boolean mOverridePanning;
/* When this is true, we have received an ack for the last scroll event we sent to JS, and
* are ready to send the next scroll event. Note we only ever have one scroll event inflight
* at a time. */
private boolean mOverrideScrollAck;
/* When this is true, we have a pending scroll that we need to send to JS; we were unable
* to send it when it was initially requested because mOverrideScrollAck was not true. */
private boolean mOverrideScrollPending;
/* When this is true, the last scroll event we sent actually did some amount of scrolling on
* the subdocument; we use this to decide when we have reached the end of the subdocument. */
private boolean mScrollSucceeded;
SubdocumentScrollHelper(EventDispatcher eventDispatcher) {
// mUiHandler will be bound to the UI thread since that's where this constructor runs
mUiHandler = new Handler();
mPendingDisplacement = new PointF();
mEventDispatcher = eventDispatcher;
mEventDispatcher.registerGeckoThreadListener(this,
MESSAGE_PANNING_OVERRIDE,
MESSAGE_CANCEL_OVERRIDE,
MESSAGE_SCROLL_ACK);
}
void destroy() {
mEventDispatcher.unregisterGeckoThreadListener(this,
MESSAGE_PANNING_OVERRIDE,
MESSAGE_CANCEL_OVERRIDE,
MESSAGE_SCROLL_ACK);
}
boolean scrollBy(PointF displacement) {
if (! mOverridePanning) {
return false;
}
if (! mOverrideScrollAck) {
mOverrideScrollPending = true;
mPendingDisplacement.x += displacement.x;
mPendingDisplacement.y += displacement.y;
return true;
}
JSONObject json = new JSONObject();
try {
json.put("x", displacement.x);
json.put("y", displacement.y);
} catch (JSONException e) {
Log.e(LOGTAG, "Error forming subwindow scroll message: ", e);
}
GeckoAppShell.notifyObservers(MESSAGE_SCROLL, json.toString());
mOverrideScrollAck = false;
mOverrideScrollPending = false;
// clear the |mPendingDisplacement| after serializing |displacement| to
// JSON because they might be the same object
mPendingDisplacement.x = 0;
mPendingDisplacement.y = 0;
return true;
}
void cancel() {
mOverridePanning = false;
}
boolean scrolling() {
return mOverridePanning;
}
boolean lastScrollSucceeded() {
return mScrollSucceeded;
}
// GeckoEventListener implementation
@Override
public void handleMessage(final String event, final JSONObject message) {
// This comes in on the Gecko thread; hand off the handling to the UI thread.
mUiHandler.post(new Runnable() {
@Override
public void run() {
try {
if (MESSAGE_PANNING_OVERRIDE.equals(event)) {
mOverridePanning = true;
mOverrideScrollAck = true;
mOverrideScrollPending = false;
mScrollSucceeded = true;
} else if (MESSAGE_CANCEL_OVERRIDE.equals(event)) {
mOverridePanning = false;
} else if (MESSAGE_SCROLL_ACK.equals(event)) {
mOverrideScrollAck = true;
mScrollSucceeded = message.getBoolean("scrolled");
if (mOverridePanning && mOverrideScrollPending) {
scrollBy(mPendingDisplacement);
}
}
} catch (Exception e) {
Log.e(LOGTAG, "Exception handling message", e);
}
}
});
}
}

Просмотреть файл

@ -63,26 +63,19 @@ public final class Clipboard {
public static void setText(final CharSequence text) {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
@SuppressWarnings("deprecation")
public void run() {
// In API Level 11 and above, CLIPBOARD_SERVICE returns android.content.ClipboardManager,
// which is a subclass of android.text.ClipboardManager.
if (Versions.feature11Plus) {
final android.content.ClipboardManager cm = (android.content.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
final ClipData clip = ClipData.newPlainText("Text", text);
try {
cm.setPrimaryClip(clip);
} catch (NullPointerException e) {
// Bug 776223: This is a Samsung clipboard bug. setPrimaryClip() can throw
// a NullPointerException if Samsung's /data/clipboard directory is full.
// Fortunately, the text is still successfully copied to the clipboard.
}
return;
final android.content.ClipboardManager cm = (android.content.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
final ClipData clip = ClipData.newPlainText("Text", text);
try {
cm.setPrimaryClip(clip);
} catch (NullPointerException e) {
// Bug 776223: This is a Samsung clipboard bug. setPrimaryClip() can throw
// a NullPointerException if Samsung's /data/clipboard directory is full.
// Fortunately, the text is still successfully copied to the clipboard.
}
// Deprecated.
android.text.ClipboardManager cm = (android.text.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
cm.setText(text);
return;
}
});
}
@ -92,14 +85,8 @@ public final class Clipboard {
*/
@WrapForJNI(calledFrom = "gecko")
public static boolean hasText() {
if (Versions.feature11Plus) {
android.content.ClipboardManager cm = (android.content.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
return cm.hasPrimaryClip();
}
// Deprecated.
android.text.ClipboardManager cm = (android.text.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
return cm.hasText();
android.content.ClipboardManager cm = (android.content.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
return cm.hasPrimaryClip();
}
/**
@ -117,19 +104,12 @@ public final class Clipboard {
*/
@SuppressWarnings("deprecation")
static String getClipboardTextImpl() {
if (Versions.feature11Plus) {
android.content.ClipboardManager cm = (android.content.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
if (cm.hasPrimaryClip()) {
ClipData clip = cm.getPrimaryClip();
if (clip != null) {
ClipData.Item item = clip.getItemAt(0);
return item.coerceToText(mContext).toString();
}
}
} else {
android.text.ClipboardManager cm = (android.text.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
if (cm.hasText()) {
return cm.getText().toString();
android.content.ClipboardManager cm = (android.content.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
if (cm.hasPrimaryClip()) {
ClipData clip = cm.getPrimaryClip();
if (clip != null) {
ClipData.Item item = clip.getItemAt(0);
return item.coerceToText(mContext).toString();
}
}
return null;

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше