Merge the last PGO-green inbound changeset to m-c.

This commit is contained in:
Ryan VanderMeulen 2012-11-01 21:26:34 -04:00
commit b356d6fd9d
84 changed files with 2822 additions and 1852 deletions

View File

@ -3084,7 +3084,7 @@ Accessible::GetAttrValue(nsIAtom *aProperty, double *aValue)
uint32_t
Accessible::GetActionRule()
{
if (InteractiveState() & states::UNAVAILABLE)
if (!HasOwnContent() || (InteractiveState() & states::UNAVAILABLE))
return eNoAction;
// Check if it's simple xlink.

View File

@ -874,8 +874,13 @@
this.showTab(this.mCurrentTab);
var backForwardContainer = document.getElementById("unified-back-forward-button");
if (backForwardContainer)
if (backForwardContainer) {
backForwardContainer.setAttribute("switchingtabs", "true");
window.addEventListener("MozAfterPaint", function removeSwitchingtabsAttr() {
window.removeEventListener("MozAfterPaint", removeSwitchingtabsAttr);
backForwardContainer.removeAttribute("switchingtabs");
});
}
if (updatePageReport)
this.mCurrentBrowser.updatePageReport();
@ -946,9 +951,6 @@
true, false);
}
if (backForwardContainer)
backForwardContainer.removeAttribute("switchingtabs");
if (this.mCurrentTab.selected)
this._setCloseKeyState(!this.mCurrentTab.pinned);

View File

@ -20,10 +20,10 @@ XPCOMUtils.defineLazyModuleGetter(this,
"DebuggerServer", "resource://gre/modules/devtools/dbg-server.jsm");
XPCOMUtils.defineLazyModuleGetter(this,
"Services", "resource:///modules/Services.jsm");
"Services", "resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this,
"FileUtils", "resource:///modules/FileUtils.jsm");
"FileUtils", "resource://gre/modules/FileUtils.jsm");
this.EXPORTED_SYMBOLS = ["DebuggerUI"];

View File

@ -2,12 +2,15 @@ simplejson.pth:python/simplejson-2.1.1
manifestdestiny.pth:testing/mozbase/manifestdestiny
mozcrash.pth:testing/mozbase/mozcrash
mozdevice.pth:testing/mozbase/mozdevice
mozfile.pth:testing/mozbase/mozfile
mozhttpd.pth:testing/mozbase/mozhttpd
mozinfo.pth:testing/mozbase/mozinfo
mozinstall.pth:testing/mozbase/mozinstall
mozlog.pth:testing/mozbase/mozlog
mozprocess.pth:testing/mozbase/mozprocess
mozprofile.pth:testing/mozbase/mozprofile
mozrunner.pth:testing/mozbase/mozrunner
marionette.pth:testing/marionette/client
blessings.pth:python/blessings
mozbuild.pth:python/mozbuild
pymake.pth:build/pymake

View File

@ -64,6 +64,7 @@ class JarMaker(object):
ignore = re.compile('\s*(\#.*)?$')
jarline = re.compile('(?:(?P<jarfile>[\w\d.\-\_\\\/]+).jar\:)|(?:\s*(\#.*)?)\s*$')
relsrcline = re.compile('relativesrcdir\s+(?P<relativesrcdir>.+?):')
regline = re.compile('\%\s+(.*)$')
entryre = '(?P<optPreprocess>\*)?(?P<optOverwrite>\+?)\s+'
entryline = re.compile(entryre + '(?P<output>[\w\d.\-\_\\\/\+\@]+)\s*(\((?P<locale>\%?)(?P<source>[\w\d.\-\_\\\/\@]+)\))?\s*$')
@ -77,6 +78,9 @@ class JarMaker(object):
self.topsourcedir = None
self.sourcedirs = []
self.localedirs = None
self.l10nbase = None
self.l10nmerge = None
self.relativesrcdir = None
def getCommandLineParser(self):
'''Get a optparse.OptionParser for jarmaker.
@ -105,6 +109,12 @@ class JarMaker(object):
help="top source directory")
p.add_option('-c', '--l10n-src', type="string", action="append",
help="localization directory")
p.add_option('--l10n-base', type="string", action="store",
help="base directory to be used for localization (requires relativesrcdir)")
p.add_option('--locale-mergedir', type="string", action="store",
help="base directory to be used for l10n-merge (requires l10n-base and relativesrcdir)")
p.add_option('--relativesrcdir', type="string",
help="relativesrcdir to be used for localization")
p.add_option('-j', type="string",
help="jarfile directory")
return p
@ -170,7 +180,7 @@ class JarMaker(object):
mf.close()
finally:
lock = None
def makeJar(self, infile, jardir):
'''makeJar is the main entry point to JarMaker.
@ -183,6 +193,8 @@ class JarMaker(object):
self.sourcedirs = [_normpath(p) for p in self.sourcedirs]
if self.localedirs:
self.localedirs = [_normpath(p) for p in self.localedirs]
elif self.relativesrcdir:
self.localedirs = self.generateLocaleDirs(self.relativesrcdir)
if isinstance(infile, basestring):
logging.info("processing " + infile)
self.sourcedirs.append(_normpath(os.path.dirname(infile)))
@ -205,6 +217,23 @@ class JarMaker(object):
pass
return
def generateLocaleDirs(self, relativesrcdir):
if os.path.basename(relativesrcdir) == 'locales':
# strip locales
l10nrelsrcdir = os.path.dirname(relativesrcdir)
else:
l10nrelsrcdir = relativesrcdir
locdirs = []
# generate locales dirs, merge, l10nbase, en-US
if self.l10nmerge:
locdirs.append(os.path.join(self.l10nmerge, l10nrelsrcdir))
if self.l10nbase:
locdirs.append(os.path.join(self.l10nbase, l10nrelsrcdir))
if self.l10nmerge or not self.l10nbase:
# add en-US if we merge, or if it's not l10n
locdirs.append(os.path.join(self.topsourcedir, relativesrcdir, 'en-US'))
return locdirs
def processJarSection(self, jarfile, lines, jardir):
'''Internal method called by makeJar to actually process a section
of a jar.mn file.
@ -254,6 +283,11 @@ class JarMaker(object):
raise
if self.ignore.match(l):
continue
m = self.relsrcline.match(l)
if m:
relativesrcdir = m.group('relativesrcdir')
self.localedirs = self.generateLocaleDirs(relativesrcdir)
continue
m = self.regline.match(l)
if m:
rline = m.group(1)
@ -323,7 +357,7 @@ class JarMaker(object):
outf.write(inf.read())
outf.close()
inf.close()
class OutputHelper_jar(object):
'''Provide getDestModTime and getOutput for a given jarfile.
@ -402,6 +436,16 @@ def main():
if options.bothManifests:
jm.useChromeManifest = True
jm.useJarfileManifest = True
if options.l10n_base:
if not options.relativesrcdir:
p.error('relativesrcdir required when using l10n-base')
if options.l10n_src:
p.error('both l10n-src and l10n-base are not supported')
jm.l10nbase = options.l10n_base
jm.relativesrcdir = options.relativesrcdir
jm.l10nmerge = options.locale_mergedir
elif options.locale_mergedir:
p.error('l10n-base required when using locale-mergedir')
jm.localedirs = options.l10n_src
noise = logging.INFO
if options.verbose is not None:

View File

@ -703,17 +703,21 @@ ifdef relativesrcdir
LOCALE_SRCDIR = $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir))
endif
ifdef LOCALE_SRCDIR
# if LOCALE_MERGEDIR is set, use mergedir first, then the localization,
# and finally en-US
ifdef relativesrcdir
MAKE_JARS_FLAGS += --relativesrcdir=$(relativesrcdir)
ifneq (en-US,$(AB_CD))
ifdef LOCALE_MERGEDIR
MAKE_JARS_FLAGS += -c $(LOCALE_MERGEDIR)/$(subst /locales,,$(relativesrcdir))
MAKE_JARS_FLAGS += --locale-mergedir=$(LOCALE_MERGEDIR)
endif
ifdef IS_LANGUAGE_REPACK
MAKE_JARS_FLAGS += --l10n-base=$(L10NBASEDIR)
endif
else
MAKE_JARS_FLAGS += -c $(LOCALE_SRCDIR)
ifdef LOCALE_MERGEDIR
MAKE_JARS_FLAGS += -c $(topsrcdir)/$(relativesrcdir)/en-US
endif
endif
endif # en-US
else
MAKE_JARS_FLAGS += -c $(LOCALE_SRCDIR)
endif # ! relativesrcdir
ifdef LOCALE_MERGEDIR
MERGE_FILE = $(firstword \

View File

@ -4,6 +4,7 @@ import os, sys, os.path, time, inspect
from filecmp import dircmp
from tempfile import mkdtemp
from shutil import rmtree, copy2
from StringIO import StringIO
from zipfile import ZipFile
import mozunit
from JarMaker import JarMaker
@ -240,5 +241,64 @@ class TestJarMaker(unittest.TestCase):
"%s is not a symlink to %s" % (destfoo, srcbar))
class Test_relativesrcdir(unittest.TestCase):
def setUp(self):
self.jm = JarMaker()
self.jm.topsourcedir = '/TOPSOURCEDIR'
self.jm.relativesrcdir = 'browser/locales'
self.fake_empty_file = StringIO()
self.fake_empty_file.name = 'fake_empty_file'
def tearDown(self):
del self.jm
del self.fake_empty_file
def test_en_US(self):
jm = self.jm
jm.makeJar(self.fake_empty_file, '/NO_OUTPUT_REQUIRED')
self.assertEquals(jm.localedirs,
[
os.path.join(os.path.abspath('/TOPSOURCEDIR'),
'browser/locales', 'en-US')
])
def test_l10n_no_merge(self):
jm = self.jm
jm.l10nbase = '/L10N_BASE'
jm.makeJar(self.fake_empty_file, '/NO_OUTPUT_REQUIRED')
self.assertEquals(jm.localedirs, [os.path.join('/L10N_BASE', 'browser')])
def test_l10n_merge(self):
jm = self.jm
jm.l10nbase = '/L10N_BASE'
jm.l10nmerge = '/L10N_MERGE'
jm.makeJar(self.fake_empty_file, '/NO_OUTPUT_REQUIRED')
self.assertEquals(jm.localedirs,
[os.path.join('/L10N_MERGE', 'browser'),
os.path.join('/L10N_BASE', 'browser'),
os.path.join(os.path.abspath('/TOPSOURCEDIR'),
'browser/locales', 'en-US')
])
def test_override(self):
jm = self.jm
jm.outputFormat = 'flat' # doesn't touch chrome dir without files
jarcontents = StringIO('''en-US.jar:
relativesrcdir dom/locales:
''')
jarcontents.name = 'override.mn'
jm.makeJar(jarcontents, '/NO_OUTPUT_REQUIRED')
self.assertEquals(jm.localedirs,
[
os.path.join(os.path.abspath('/TOPSOURCEDIR'),
'dom/locales', 'en-US')
])
def test_override_l10n(self):
jm = self.jm
jm.l10nbase = '/L10N_BASE'
jm.outputFormat = 'flat' # doesn't touch chrome dir without files
jarcontents = StringIO('''en-US.jar:
relativesrcdir dom/locales:
''')
jarcontents.name = 'override.mn'
jm.makeJar(jarcontents, '/NO_OUTPUT_REQUIRED')
self.assertEquals(jm.localedirs, [os.path.join('/L10N_BASE', 'dom')])
if __name__ == '__main__':
mozunit.main()

View File

@ -2866,11 +2866,21 @@ public:
}
mContext->MakeContextCurrent();
WebGLuint renderbuffername = wrb ? wrb->GLName() : 0;
WebGLuint parambuffername = wrb ? wrb->GLName() : 0;
if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_DEPTH_ATTACHMENT, rbtarget, renderbuffername);
mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_STENCIL_ATTACHMENT, rbtarget, renderbuffername);
WebGLuint depthbuffername = parambuffername;
WebGLuint stencilbuffername = parambuffername;
if (!parambuffername){
depthbuffername = mDepthAttachment.Renderbuffer() ? mDepthAttachment.Renderbuffer()->GLName() : 0;
stencilbuffername = mStencilAttachment.Renderbuffer() ? mStencilAttachment.Renderbuffer()->GLName() : 0;
}
mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_DEPTH_ATTACHMENT, rbtarget, depthbuffername);
mContext->gl->fFramebufferRenderbuffer(target, LOCAL_GL_STENCIL_ATTACHMENT, rbtarget, stencilbuffername);
} else {
WebGLuint renderbuffername = parambuffername;
if(!parambuffername && (attachment == LOCAL_GL_DEPTH_ATTACHMENT || attachment == LOCAL_GL_STENCIL_ATTACHMENT)){
renderbuffername = mDepthStencilAttachment.Renderbuffer() ? mDepthStencilAttachment.Renderbuffer()->GLName() : 0;
}
mContext->gl->fFramebufferRenderbuffer(target, attachment, rbtarget, renderbuffername);
}
}
@ -2918,11 +2928,21 @@ public:
}
mContext->MakeContextCurrent();
WebGLuint texturename = wtex ? wtex->GLName() : 0;
WebGLuint paramtexturename = wtex ? wtex->GLName() : 0;
if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) {
mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_DEPTH_ATTACHMENT, textarget, texturename, level);
mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_STENCIL_ATTACHMENT, textarget, texturename, level);
WebGLuint depthtexturename = paramtexturename;
WebGLuint stenciltexturename = paramtexturename;
if(!paramtexturename){
depthtexturename = mDepthAttachment.Texture() ? mDepthAttachment.Texture()->GLName() : 0;
stenciltexturename = mStencilAttachment.Texture() ? mStencilAttachment.Texture()->GLName() : 0;
}
mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_DEPTH_ATTACHMENT, textarget, depthtexturename, level);
mContext->gl->fFramebufferTexture2D(target, LOCAL_GL_STENCIL_ATTACHMENT, textarget, stenciltexturename, level);
} else {
WebGLuint texturename = paramtexturename;
if(!paramtexturename && (attachment == LOCAL_GL_DEPTH_ATTACHMENT || attachment == LOCAL_GL_STENCIL_ATTACHMENT)){
texturename = mDepthStencilAttachment.Texture() ? mDepthStencilAttachment.Texture()->GLName() : 0;
}
mContext->gl->fFramebufferTexture2D(target, attachment, textarget, texturename, level);
}

View File

@ -13,6 +13,7 @@
#include "nsISeekableStream.h"
#include "pratom.h"
#include "nsMediaPluginReader.h"
#include "nsIGfxInfo.h"
#include "MPAPI.h"
@ -87,6 +88,29 @@ static PluginHost sPluginHost = {
void nsMediaPluginHost::TryLoad(const char *name)
{
bool forceEnabled =
Preferences::GetBool("stagefright.force-enabled", false);
bool disabled =
Preferences::GetBool("stagefright.disabled", false);
if (disabled) {
NS_WARNING("XXX stagefright disabled\n");
return;
}
if (!forceEnabled) {
nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
if (gfxInfo) {
int32_t status;
if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_STAGEFRIGHT, &status))) {
if (status != nsIGfxInfo::FEATURE_NO_INFO) {
NS_WARNING("XXX stagefright blacklisted\n");
return;
}
}
}
}
PRLibrary *lib = PR_LoadLibrary(name);
if (lib) {
Manifest *manifest = static_cast<Manifest *>(PR_FindSymbol(lib, "MPAPI_MANIFEST"));

View File

@ -12,6 +12,7 @@
#include "AudioDestinationNode.h"
#include "AudioBufferSourceNode.h"
#include "AudioBuffer.h"
#include "GainNode.h"
namespace mozilla {
namespace dom {
@ -86,6 +87,13 @@ AudioContext::CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels,
return buffer.forget();
}
already_AddRefed<GainNode>
AudioContext::CreateGain()
{
nsRefPtr<GainNode> gainNode = new GainNode(this);
return gainNode.forget();
}
}
}

View File

@ -26,6 +26,7 @@ namespace dom {
class AudioDestinationNode;
class AudioBufferSourceNode;
class AudioBuffer;
class GainNode;
class AudioContext MOZ_FINAL : public nsWrapperCache,
public EnableWebAudioCheck
@ -61,6 +62,9 @@ public:
uint32_t aLength, float aSampleRate,
ErrorResult& aRv);
already_AddRefed<GainNode>
CreateGain();
private:
nsCOMPtr<nsIDOMWindow> mWindow;
nsRefPtr<AudioDestinationNode> mDestination;

View File

@ -153,7 +153,19 @@ public:
void CancelScheduledValues(float aStartTime)
{
// TODO: implement
for (unsigned i = 0; i < mEvents.Length(); ++i) {
if (mEvents[i].mTime >= aStartTime) {
#ifdef DEBUG
// Sanity check: the array should be sorted, so all of the following
// events should have a time greater than aStartTime too.
for (unsigned j = i + 1; j < mEvents.Length(); ++j) {
MOZ_ASSERT(mEvents[j].mTime >= aStartTime);
}
#endif
mEvents.TruncateLength(i);
break;
}
}
}
// This method computes the AudioParam value at a given time based on the event timeline

View File

@ -0,0 +1,42 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GainNode.h"
#include "mozilla/dom/GainNodeBinding.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(GainNode)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(GainNode, AudioNode)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mGain)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(GainNode, AudioNode)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(tmp->mGain, AudioParam, "gain value")
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(GainNode)
NS_INTERFACE_MAP_END_INHERITING(AudioNode)
NS_IMPL_ADDREF_INHERITED(GainNode, AudioNode)
NS_IMPL_RELEASE_INHERITED(GainNode, AudioNode)
GainNode::GainNode(AudioContext* aContext)
: AudioNode(aContext)
, mGain(new AudioParam(aContext, 1.0f, 0.0f, 1.0f))
{
}
JSObject*
GainNode::WrapObject(JSContext* aCx, JSObject* aScope,
bool* aTriedToWrap)
{
return GainNodeBinding::Wrap(aCx, aScope, this, aTriedToWrap);
}
}
}

View File

@ -0,0 +1,51 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GainNode_h_
#define GainNode_h_
#include "AudioNode.h"
#include "AudioParam.h"
namespace mozilla {
namespace dom {
class AudioContext;
class GainNode : public AudioNode
{
public:
explicit GainNode(AudioContext* aContext);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(GainNode, AudioNode)
virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope,
bool* aTriedToWrap);
virtual uint32_t MaxNumberOfInputs() const MOZ_FINAL MOZ_OVERRIDE
{
return 1;
}
virtual uint32_t MaxNumberOfOutputs() const MOZ_FINAL MOZ_OVERRIDE
{
return 1;
}
AudioParam* Gain() const
{
return mGain;
}
private:
nsRefPtr<AudioParam> mGain;
};
}
}
#endif

View File

@ -23,6 +23,7 @@ CPPSRCS := \
AudioParam.cpp \
AudioSourceNode.cpp \
EnableWebAudioCheck.cpp \
GainNode.cpp \
$(NULL)
EXPORTS_NAMESPACES := mozilla/dom
@ -33,6 +34,7 @@ EXPORTS_mozilla/dom := \
AudioNode.h \
AudioParam.h \
AudioSourceNode.h \
GainNode.h \
$(NULL)
PARALLEL_DIRS := test

View File

@ -208,6 +208,25 @@ void TestEventReplacement()
is(timeline.GetValueAtTime(0.1f), 30.0f, "The first event should be overwritten");
}
void TestEventRemoval()
{
Timeline timeline(10.0f, .1f, 20.0f);
ErrorResultMock rv;
timeline.SetValueAtTime(10.0f, 0.1f, rv);
timeline.SetValueAtTime(15.0f, 0.15f, rv);
timeline.SetValueAtTime(20.0f, 0.2f, rv);
timeline.LinearRampToValueAtTime(30.0f, 0.3f, rv);
is(timeline.GetEventCount(), 4, "Should have three events initially");
timeline.CancelScheduledValues(0.4f);
is(timeline.GetEventCount(), 4, "Trying to delete past the end of the array should have no effect");
timeline.CancelScheduledValues(0.3f);
is(timeline.GetEventCount(), 3, "Should successfully delete one event");
timeline.CancelScheduledValues(0.12f);
is(timeline.GetEventCount(), 1, "Should successfully delete two events");
}
void TestBeforeFirstEvent()
{
Timeline timeline(10.0f, .1f, 20.0f);
@ -327,6 +346,7 @@ int main()
TestSpecExample();
TestInvalidEvents();
TestEventReplacement();
TestEventRemoval();
TestBeforeFirstEvent();
TestAfterLastValueEvent();
TestAfterLastTargetValueEvent();

View File

@ -14,6 +14,7 @@ MOCHITEST_FILES := \
test_AudioBuffer.html \
test_AudioContext.html \
test_badConnect.html \
test_gainNode.html \
test_singleSourceDest.html \
$(NULL)

View File

@ -0,0 +1,58 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test GainNode</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
SpecialPowers.setBoolPref("media.webaudio.enabled", true);
var context = new mozAudioContext();
var buffer = context.createBuffer(1, 2048, 44100);
for (var i = 0; i < 2048; ++i) {
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / 44100);
}
var destination = context.destination;
var source = context.createBufferSource();
var gain = context.createGain();
source.buffer = buffer;
source.connect(gain);
gain.connect(destination);
ok(gain.gain, "The audioparam member must exist");
is(gain.gain.value, 1.0, "Correct initial value");
is(gain.gain.defaultValue, 1.0, "Correct default value");
is(gain.gain.minValue, 0, "Correct min value");
is(gain.gain.maxValue, 1.0, "Correct max value");
gain.gain.value = 0.5;
is(gain.gain.value, 0.5, "Correct initial value");
is(gain.gain.defaultValue, 1.0, "Correct default value");
is(gain.gain.minValue, 0, "Correct min value");
is(gain.gain.maxValue, 1.0, "Correct max value");
source.start(0);
SimpleTest.executeSoon(function() {
source.stop(0);
source.disconnect();
gain.disconnect();
SpecialPowers.clearUserPref("media.webaudio.enabled");
SimpleTest.finish();
});
});
</script>
</pre>
</body>
</html>

View File

@ -511,12 +511,11 @@ nsDOMWindowUtils::SendMouseEvent(const nsAString& aType,
int32_t aModifiers,
bool aIgnoreRootScrollFrame,
float aPressure,
unsigned short aInputSourceArg,
bool *aPreventDefault)
unsigned short aInputSourceArg)
{
return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
aIgnoreRootScrollFrame, aPressure,
aInputSourceArg, false, aPreventDefault);
aInputSourceArg, false);
}
NS_IMETHODIMP
@ -533,7 +532,7 @@ nsDOMWindowUtils::SendMouseEventToWindow(const nsAString& aType,
SAMPLE_LABEL("nsDOMWindowUtils", "SendMouseEventToWindow");
return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
aIgnoreRootScrollFrame, aPressure,
aInputSourceArg, true, nullptr);
aInputSourceArg, true);
}
static nsIntPoint
@ -556,8 +555,7 @@ nsDOMWindowUtils::SendMouseEventCommon(const nsAString& aType,
bool aIgnoreRootScrollFrame,
float aPressure,
unsigned short aInputSourceArg,
bool aToWindow,
bool *aPreventDefault)
bool aToWindow)
{
if (!nsContentUtils::IsCallerChrome()) {
return NS_ERROR_DOM_SECURITY_ERR;
@ -584,9 +582,7 @@ nsDOMWindowUtils::SendMouseEventCommon(const nsAString& aType,
else if (aType.EqualsLiteral("contextmenu")) {
msg = NS_CONTEXTMENU;
contextMenuKey = (aButton == 0);
} else if (aType.EqualsLiteral("MozMouseHittest"))
msg = NS_MOUSE_MOZHITTEST;
else
} else
return NS_ERROR_FAILURE;
if (aInputSourceArg == nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN) {
@ -627,10 +623,7 @@ nsDOMWindowUtils::SendMouseEventCommon(const nsAString& aType,
status = nsEventStatus_eIgnore;
return presShell->HandleEvent(view->GetFrame(), &event, false, &status);
}
nsresult rv = widget->DispatchEvent(&event, status);
*aPreventDefault = (status == nsEventStatus_eConsumeNoDefault);
return rv;
return widget->DispatchEvent(&event, status);
}
NS_IMETHODIMP

View File

@ -44,8 +44,7 @@ protected:
bool aIgnoreRootScrollFrame,
float aPressure,
unsigned short aInputSourceArg,
bool aToWindow,
bool *aPreventDefault);
bool aToWindow);
static mozilla::widget::Modifiers GetWidgetModifiers(int32_t aModifiers);
};

View File

@ -1545,6 +1545,7 @@ nsJSContext::CompileScript(const PRUnichar* aText,
nsScriptObjectHolder<JSScript>& aScriptObject,
bool aSaveSource /* = false */)
{
SAMPLE_LABEL_PRINTF("JS", "Compile Script", "%s", aURL ? aURL : "");
NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
NS_ENSURE_ARG_POINTER(aPrincipal);

View File

@ -202,6 +202,11 @@ DOMInterfaces = {
'workers': True,
}],
'GainNode': [
{
'resultNotAddRefed': [ 'gain' ],
}],
'HTMLCollection': [
{
'nativeType': 'nsIHTMLCollection',

View File

@ -2387,45 +2387,6 @@ BluetoothDBusService::Disconnect(const uint16_t aProfileId,
DispatchBluetoothReply(aRunnable, v, replyError);
}
class CreateBluetoothScoSocket : public nsRunnable
{
public:
CreateBluetoothScoSocket(UnixSocketConsumer* aConsumer,
const nsAString& aAddress,
bool aAuth,
bool aEncrypt)
: mConsumer(aConsumer),
mAddress(aAddress),
mAuth(aAuth),
mEncrypt(aEncrypt)
{
}
nsresult
Run()
{
MOZ_ASSERT(!NS_IsMainThread());
nsString replyError;
BluetoothUnixSocketConnector* c =
new BluetoothUnixSocketConnector(BluetoothSocketType::SCO, -1,
mAuth, mEncrypt);
if (!mConsumer->ConnectSocket(c, NS_ConvertUTF16toUTF8(mAddress).get())) {
replyError.AssignLiteral("SocketConnectionError");
return NS_ERROR_FAILURE;
}
return NS_OK;
}
private:
nsRefPtr<UnixSocketConsumer> mConsumer;
nsString mAddress;
bool mAuth;
bool mEncrypt;
};
class ConnectBluetoothSocketRunnable : public nsRunnable
{
public:
@ -2583,12 +2544,13 @@ BluetoothDBusService::GetScoSocket(const nsAString& aAddress,
return NS_ERROR_FAILURE;
}
nsRefPtr<nsRunnable> func(new CreateBluetoothScoSocket(aConsumer,
aAddress,
aAuth,
aEncrypt));
if (NS_FAILED(mBluetoothCommandThread->Dispatch(func, NS_DISPATCH_NORMAL))) {
NS_WARNING("Cannot dispatch firmware loading task!");
nsString replyError;
BluetoothUnixSocketConnector* c =
new BluetoothUnixSocketConnector(BluetoothSocketType::SCO, -1,
aAuth, aEncrypt);
if (!aConsumer->ConnectSocket(c, NS_ConvertUTF16toUTF8(aAddress).get())) {
replyError.AssignLiteral("SocketConnectionError");
return NS_ERROR_FAILURE;
}

View File

@ -40,7 +40,7 @@ interface nsIDOMTouch;
interface nsIDOMClientRect;
interface nsIURI;
[scriptable, uuid(b3a3589d-cc9d-4123-9b21-51c66e88b436)]
[scriptable, uuid(C98B7275-93C4-4EAD-B7CF-573D872C1071)]
interface nsIDOMWindowUtils : nsISupports {
/**
@ -204,8 +204,7 @@ interface nsIDOMWindowUtils : nsISupports {
const long MODIFIER_OS = 0x0400;
/** Synthesize a mouse event. The event types supported are:
* mousedown, mouseup, mousemove, mouseover, mouseout, contextmenu,
* MozMouseHitTest
* mousedown, mouseup, mousemove, mouseover, mouseout, contextmenu
*
* Events are sent in coordinates offset by aX and aY from the window.
*
@ -237,18 +236,16 @@ interface nsIDOMWindowUtils : nsISupports {
* @param aPressure touch input pressure: 0.0 -> 1.0
* @param aInputSourceArg input source, see nsIDOMMouseEvent for values,
* defaults to mouse input.
*
* returns true if the page called prevent default on this event
*/
boolean sendMouseEvent(in AString aType,
in float aX,
in float aY,
in long aButton,
in long aClickCount,
in long aModifiers,
[optional] in boolean aIgnoreRootScrollFrame,
[optional] in float aPressure,
[optional] in unsigned short aInputSourceArg);
void sendMouseEvent(in AString aType,
in float aX,
in float aY,
in long aButton,
in long aClickCount,
in long aModifiers,
[optional] in boolean aIgnoreRootScrollFrame,
[optional] in float aPressure,
[optional] in unsigned short aInputSourceArg);
/** Synthesize a touch event. The event types supported are:
* touchstart, touchend, touchmove, and touchcancel

View File

@ -1276,9 +1276,8 @@ TabChild::RecvMouseEvent(const nsString& aType,
{
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
NS_ENSURE_TRUE(utils, true);
bool ignored = false;
utils->SendMouseEvent(aType, aX, aY, aButton, aClickCount, aModifiers,
aIgnoreRootScrollFrame, 0, 0, &ignored);
aIgnoreRootScrollFrame, 0, 0);
return true;
}

View File

@ -137,7 +137,8 @@ SettingsLock.prototype = {
var lock;
while (lock = this._settingsManager._locks.dequeue()) {
if (!lock._transaction) {
lock._transaction = lock._settingsManager._settingsDB._db.transaction(SETTINGSSTORE_NAME, "readwrite");
let transactionType = this._settingsManager.hasWritePrivileges ? "readwrite" : "readonly";
lock._transaction = lock._settingsManager._settingsDB._db.transaction(SETTINGSSTORE_NAME, transactionType);
}
lock.process();
}

View File

@ -25,6 +25,9 @@ interface mozAudioContext {
[Creator]
AudioBufferSourceNode createBufferSource();
[Creator]
GainNode createGain();
};
typedef mozAudioContext AudioContext;

View File

@ -37,7 +37,7 @@ interface AudioParam {
// void setValueCurveAtTime(Float32Array values, float startTime, float duration);
// Cancels all scheduled parameter changes with times greater than or equal to startTime.
// void cancelScheduledValues(float startTime);
void cancelScheduledValues(float startTime);
};

View File

@ -0,0 +1,19 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* The origin of this IDL file is
* https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
*
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply.
*/
[PrefControlled]
interface GainNode : AudioNode {
readonly attribute AudioParam gain;
};

View File

@ -29,6 +29,7 @@ webidl_files = \
EventTarget.webidl \
FileList.webidl \
FileReaderSync.webidl \
GainNode.webidl \
HTMLCollection.webidl \
HTMLOptionsCollection.webidl \
HTMLPropertiesCollection.webidl \

View File

@ -921,7 +921,7 @@ void ExtractFontsFromJar(nsIFile* aLocalDir)
jarFile->GetLastModifiedTime(&jarModifiedTime);
mozilla::scache::StartupCache* cache = mozilla::scache::StartupCache::GetSingleton();
if (NS_SUCCEEDED(cache->GetBuffer(JAR_LAST_MODIFED_TIME, &cachedModifiedTimeBuf, &longSize))
if (cache && NS_SUCCEEDED(cache->GetBuffer(JAR_LAST_MODIFED_TIME, &cachedModifiedTimeBuf, &longSize))
&& longSize == sizeof(int64_t)) {
if (jarModifiedTime < *((int64_t*) cachedModifiedTimeBuf)) {
return;
@ -979,7 +979,7 @@ void ExtractFontsFromJar(nsIFile* aLocalDir)
}
}
}
if (allFontsExtracted) {
if (allFontsExtracted && cache) {
cache->PutBuffer(JAR_LAST_MODIFED_TIME, (char*)&jarModifiedTime, sizeof(int64_t));
}
}

View File

@ -703,17 +703,21 @@ ifdef relativesrcdir
LOCALE_SRCDIR = $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir))
endif
ifdef LOCALE_SRCDIR
# if LOCALE_MERGEDIR is set, use mergedir first, then the localization,
# and finally en-US
ifdef relativesrcdir
MAKE_JARS_FLAGS += --relativesrcdir=$(relativesrcdir)
ifneq (en-US,$(AB_CD))
ifdef LOCALE_MERGEDIR
MAKE_JARS_FLAGS += -c $(LOCALE_MERGEDIR)/$(subst /locales,,$(relativesrcdir))
MAKE_JARS_FLAGS += --locale-mergedir=$(LOCALE_MERGEDIR)
endif
ifdef IS_LANGUAGE_REPACK
MAKE_JARS_FLAGS += --l10n-base=$(L10NBASEDIR)
endif
else
MAKE_JARS_FLAGS += -c $(LOCALE_SRCDIR)
ifdef LOCALE_MERGEDIR
MAKE_JARS_FLAGS += -c $(topsrcdir)/$(relativesrcdir)/en-US
endif
endif
endif # en-US
else
MAKE_JARS_FLAGS += -c $(LOCALE_SRCDIR)
endif # ! relativesrcdir
ifdef LOCALE_MERGEDIR
MERGE_FILE = $(firstword \

View File

@ -48,6 +48,7 @@ JSCompartment::JSCompartment(JSRuntime *rt)
gcStoreBuffer(&gcNursery),
#endif
needsBarrier_(false),
ionUsingBarriers_(false),
gcScheduled(false),
gcState(NoGC),
gcPreserveCode(false),
@ -131,7 +132,7 @@ JSCompartment::init(JSContext *cx)
}
void
JSCompartment::setNeedsBarrier(bool needs)
JSCompartment::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon)
{
#ifdef JS_METHODJIT
/* ClearAllFrames calls compileBarriers() and needs the old value. */
@ -141,8 +142,10 @@ JSCompartment::setNeedsBarrier(bool needs)
#endif
#ifdef JS_ION
if (needsBarrier_ != needs)
if (updateIon == UpdateIon && needs != ionUsingBarriers_) {
ion::ToggleBarriers(this, needs);
ionUsingBarriers_ = needs;
}
#endif
needsBarrier_ = needs;

View File

@ -155,6 +155,7 @@ struct JSCompartment
private:
bool needsBarrier_;
bool ionUsingBarriers_;
public:
bool needsBarrier() const {
@ -169,7 +170,12 @@ struct JSCompartment
return compileBarriers(needsBarrier());
}
void setNeedsBarrier(bool needs);
enum ShouldUpdateIon {
DontUpdateIon,
UpdateIon
};
void setNeedsBarrier(bool needs, ShouldUpdateIon updateIon);
static size_t OffsetOfNeedsBarrier() {
return offsetof(JSCompartment, needsBarrier_);

View File

@ -4079,7 +4079,7 @@ ResetIncrementalGC(JSRuntime *rt, const char *reason)
AutoCopyFreeListToArenas copy(rt);
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
if (c->isGCMarking()) {
c->setNeedsBarrier(false);
c->setNeedsBarrier(false, JSCompartment::DontUpdateIon);
c->setGCState(JSCompartment::NoGC);
wasMarking = true;
}
@ -4137,10 +4137,15 @@ AutoGCSlice::AutoGCSlice(JSRuntime *rt)
rt->stackSpace.markActiveCompartments();
for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
/* Clear this early so we don't do any write barriers during GC. */
/*
* Clear needsBarrier early so we don't do any write barriers during
* GC. We don't need to update the Ion barriers (which is expensive)
* because Ion code doesn't run during GC. If need be, we'll update the
* Ion barriers in ~AutoGCSlice.
*/
if (c->isGCMarking()) {
JS_ASSERT(c->needsBarrier());
c->setNeedsBarrier(false);
c->setNeedsBarrier(false, JSCompartment::DontUpdateIon);
} else {
JS_ASSERT(!c->needsBarrier());
}
@ -4151,11 +4156,11 @@ AutoGCSlice::~AutoGCSlice()
{
for (GCCompartmentsIter c(runtime); !c.done(); c.next()) {
if (c->isGCMarking()) {
c->setNeedsBarrier(true);
c->setNeedsBarrier(true, JSCompartment::UpdateIon);
c->arenas.prepareForIncrementalGC(runtime);
} else {
JS_ASSERT(c->isGCSweeping());
c->setNeedsBarrier(false);
c->setNeedsBarrier(false, JSCompartment::UpdateIon);
}
}
}
@ -4312,7 +4317,7 @@ IncrementalCollectSlice(JSRuntime *rt,
default:
JS_ASSERT(false);
}
}
}
class IncrementalSafety
@ -5267,7 +5272,7 @@ StartVerifyPreBarriers(JSRuntime *rt)
rt->gcMarker.start(rt);
for (CompartmentsIter c(rt); !c.done(); c.next()) {
PurgeJITCaches(c);
c->setNeedsBarrier(true);
c->setNeedsBarrier(true, JSCompartment::UpdateIon);
c->arenas.purge();
}
@ -5350,7 +5355,7 @@ EndVerifyPreBarriers(JSRuntime *rt)
compartmentCreated = true;
PurgeJITCaches(c);
c->setNeedsBarrier(false);
c->setNeedsBarrier(false, JSCompartment::UpdateIon);
}
/*

View File

@ -7,8 +7,10 @@ package org.mozilla.gecko;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.BrowserDB.URLColumns;
import org.mozilla.gecko.db.BrowserContract.Images;
import org.mozilla.gecko.sync.setup.SyncAccounts;
import org.mozilla.gecko.sync.setup.activities.SetupSyncActivity;
import org.mozilla.gecko.util.GeckoAsyncTask;
import org.json.JSONArray;
import org.json.JSONException;
@ -59,8 +61,10 @@ import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@ -263,9 +267,8 @@ public class AboutHomeContent extends ScrollView
mTopSitesAdapter = new TopSitesCursorAdapter(mActivity,
R.layout.abouthome_topsite_item,
mCursor,
new String[] { URLColumns.TITLE,
URLColumns.THUMBNAIL },
new int[] { R.id.title, R.id.thumbnail });
new String[] { URLColumns.TITLE },
new int[] { R.id.title });
mTopSitesAdapter.setViewBinder(new TopSitesViewBinder());
mTopSitesGrid.setAdapter(mTopSitesAdapter);
@ -273,6 +276,9 @@ public class AboutHomeContent extends ScrollView
mTopSitesAdapter.changeCursor(mCursor);
}
if (mTopSitesAdapter.getCount() > 0)
loadTopSitesThumbnails(resolver);
updateLayout(syncIsSetup);
// Free the old Cursor in the right thread now.
@ -288,6 +294,97 @@ public class AboutHomeContent extends ScrollView
});
}
private List<String> getTopSitesUrls() {
List<String> urls = new ArrayList<String>();
Cursor c = mTopSitesAdapter.getCursor();
if (c == null || !c.moveToFirst())
return urls;
do {
final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
urls.add(url);
} while (c.moveToNext());
return urls;
}
private void displayThumbnail(View view, Bitmap thumbnail) {
ImageView thumbnailView = (ImageView) view.findViewById(R.id.thumbnail);
if (thumbnail == null) {
thumbnailView.setImageResource(R.drawable.abouthome_thumbnail_bg);
thumbnailView.setScaleType(ImageView.ScaleType.FIT_CENTER);
} else {
try {
thumbnailView.setImageBitmap(thumbnail);
thumbnailView.setScaleType(ImageView.ScaleType.CENTER_CROP);
} catch (OutOfMemoryError oom) {
Log.e(LOGTAG, "Unable to load thumbnail bitmap", oom);
thumbnailView.setImageResource(R.drawable.abouthome_thumbnail_bg);
thumbnailView.setScaleType(ImageView.ScaleType.FIT_CENTER);
}
}
}
private void updateTopSitesThumbnails(Map<String, Bitmap> thumbnails) {
for (int i = 0; i < mTopSitesGrid.getChildCount(); i++) {
final View view = mTopSitesGrid.getChildAt(i);
Cursor c = (Cursor) mTopSitesGrid.getItemAtPosition(i);
final String url = c.getString(c.getColumnIndex(URLColumns.URL));
displayThumbnail(view, thumbnails.get(url));
}
mTopSitesGrid.invalidate();
}
public Map<String, Bitmap> getTopSitesThumbnails(Cursor c) {
Map<String, Bitmap> thumbnails = new HashMap<String, Bitmap>();
try {
if (c == null || !c.moveToFirst())
return thumbnails;
do {
final String url = c.getString(c.getColumnIndexOrThrow(Images.URL));
final byte[] b = c.getBlob(c.getColumnIndexOrThrow(Images.THUMBNAIL));
if (b == null)
continue;
Bitmap thumbnail = BitmapFactory.decodeByteArray(b, 0, b.length);
if (thumbnail == null)
continue;
thumbnails.put(url, thumbnail);
} while (c.moveToNext());
} finally {
if (c != null)
c.close();
}
return thumbnails;
}
private void loadTopSitesThumbnails(final ContentResolver cr) {
final List<String> urls = getTopSitesUrls();
if (urls.size() == 0)
return;
(new GeckoAsyncTask<Void, Void, Cursor>(GeckoApp.mAppContext, GeckoAppShell.getHandler()) {
@Override
public Cursor doInBackground(Void... params) {
return BrowserDB.getThumbnailsForUrls(cr, urls);
}
@Override
public void onPostExecute(Cursor c) {
updateTopSitesThumbnails(getTopSitesThumbnails(c));
}
}).execute();
}
void update(final EnumSet<UpdateFlags> flags) {
GeckoAppShell.getHandler().post(new Runnable() {
public void run() {
@ -675,28 +772,6 @@ public class AboutHomeContent extends ScrollView
}
class TopSitesViewBinder implements SimpleCursorAdapter.ViewBinder {
private boolean updateThumbnail(View view, Cursor cursor, int thumbIndex) {
byte[] b = cursor.getBlob(thumbIndex);
ImageView thumbnail = (ImageView) view;
if (b == null) {
thumbnail.setImageResource(R.drawable.abouthome_thumbnail_bg);
thumbnail.setScaleType(ImageView.ScaleType.FIT_CENTER);
} else {
try {
Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
thumbnail.setImageBitmap(bitmap);
thumbnail.setScaleType(ImageView.ScaleType.CENTER_CROP);
} catch (OutOfMemoryError oom) {
Log.e(LOGTAG, "Unable to load thumbnail bitmap", oom);
thumbnail.setImageResource(R.drawable.abouthome_thumbnail_bg);
thumbnail.setScaleType(ImageView.ScaleType.FIT_CENTER);
}
}
return true;
}
private boolean updateTitle(View view, Cursor cursor, int titleIndex) {
String title = cursor.getString(titleIndex);
TextView titleView = (TextView) view;
@ -719,11 +794,6 @@ public class AboutHomeContent extends ScrollView
return updateTitle(view, cursor, titleIndex);
}
int thumbIndex = cursor.getColumnIndexOrThrow(URLColumns.THUMBNAIL);
if (columnIndex == thumbIndex) {
return updateThumbnail(view, cursor, thumbIndex);
}
// Other columns are handled automatically
return false;
}

View File

@ -7,6 +7,7 @@ package org.mozilla.gecko;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.util.GeckoJarReader;
import org.mozilla.gecko.util.LruCache;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.HttpGet;
@ -19,6 +20,7 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.http.AndroidHttpClient;
@ -46,6 +48,7 @@ public class Favicons {
private Map<Long,LoadFaviconTask> mLoadTasks;
private long mNextFaviconLoadId;
private LruCache<String, Drawable> mFaviconsCache;
private static final String USER_AGENT = GeckoApp.mAppContext.getDefaultUAString();
private AndroidHttpClient mHttpClient;
@ -139,6 +142,15 @@ public class Favicons {
mLoadTasks = Collections.synchronizedMap(new HashMap<Long,LoadFaviconTask>());
mNextFaviconLoadId = 0;
// Create a favicon memory cache that have up to 1mb of size
mFaviconsCache = new LruCache<String, Drawable>(1024 * 1024) {
@Override
protected int sizeOf(String url, Drawable image) {
Bitmap bitmap = ((BitmapDrawable) image).getBitmap();
return bitmap.getRowBytes() * bitmap.getHeight();
}
};
}
private synchronized AndroidHttpClient getHttpClient() {
@ -149,6 +161,20 @@ public class Favicons {
return mHttpClient;
}
private void dispatchResult(final String pageUrl, final Drawable image,
final OnFaviconLoadedListener listener) {
if (pageUrl != null && image != null)
putFaviconInMemCache(pageUrl, image);
// We want to always run the listener on UI thread
GeckoAppShell.getMainHandler().post(new Runnable() {
public void run() {
if (listener != null)
listener.onFaviconLoaded(pageUrl, image);
}
});
}
public String getFaviconUrlForPageUrl(String pageUrl) {
return mDbHelper.getFaviconUrlForPageUrl(pageUrl);
}
@ -158,8 +184,15 @@ public class Favicons {
// Handle the case where page url is empty
if (pageUrl == null || pageUrl.length() == 0) {
if (listener != null)
listener.onFaviconLoaded(null, null);
dispatchResult(null, null, listener);
return -1;
}
// Check if favicon is mem cached
Drawable image = getFaviconFromMemCache(pageUrl);
if (image != null) {
dispatchResult(pageUrl, image, listener);
return -1;
}
LoadFaviconTask task = new LoadFaviconTask(pageUrl, faviconUrl, persist, listener);
@ -172,6 +205,18 @@ public class Favicons {
return taskId;
}
public Drawable getFaviconFromMemCache(String pageUrl) {
return mFaviconsCache.get(pageUrl);
}
public void putFaviconInMemCache(String pageUrl, Drawable image) {
mFaviconsCache.put(pageUrl, image);
}
public void clearMemCache() {
mFaviconsCache.evictAll();
}
public boolean cancelFaviconLoad(long taskId) {
Log.d(LOGTAG, "Requesting cancelation of favicon load (" + taskId + ")");
@ -255,7 +300,7 @@ public class Favicons {
// Runs in background thread
private BitmapDrawable downloadFavicon(URL faviconUrl) {
if (mFaviconUrl.startsWith("jar:jar:")) {
return GeckoJarReader.getBitmapDrawable(GeckoApp.mAppContext.getResources(), mFaviconUrl);
return GeckoJarReader.getBitmapDrawable(mContext.getResources(), mFaviconUrl);
}
URI uri;
@ -339,15 +384,7 @@ public class Favicons {
@Override
protected void onPostExecute(final BitmapDrawable image) {
mLoadTasks.remove(mId);
if (mListener != null) {
// We want to always run the listener on UI thread
GeckoApp.mAppContext.runOnUiThread(new Runnable() {
public void run() {
mListener.onFaviconLoaded(mPageUrl, image);
}
});
}
dispatchResult(mPageUrl, image, mListener);
}
@Override

View File

@ -9,6 +9,7 @@ import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.gfx.GeckoLayerClient;
import org.mozilla.gecko.gfx.GfxInfoThread;
import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
import org.mozilla.gecko.gfx.InputConnectionHandler;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.gfx.TouchEventHandler;
import org.mozilla.gecko.util.EventDispatcher;
@ -109,7 +110,7 @@ public class GeckoAppShell
static private boolean gRestartScheduled = false;
static private GeckoInputConnection mInputConnection = null;
static private GeckoEditableListener mEditableListener = null;
static private final HashMap<Integer, AlertNotification>
mAlertNotifications = new HashMap<Integer, AlertNotification>();
@ -534,8 +535,11 @@ public class GeckoAppShell
// Called on the UI thread after Gecko loads.
private static void geckoLoaded() {
LayerView v = GeckoApp.mAppContext.getLayerView();
mInputConnection = GeckoInputConnection.create(v);
v.setInputConnectionHandler(mInputConnection);
GeckoEditable editable = new GeckoEditable();
InputConnectionHandler ich = GeckoInputConnection.create(v, editable);
v.setInputConnectionHandler(ich);
// install the gecko => editable listener
mEditableListener = editable;
}
static void sendPendingEventsToGecko() {
@ -568,21 +572,30 @@ public class GeckoAppShell
* The Gecko-side API: API methods that Gecko calls
*/
public static void notifyIME(int type, int state) {
if (mInputConnection != null)
mInputConnection.notifyIME(type, state);
if (mEditableListener != null) {
mEditableListener.notifyIME(type, state);
}
}
public static void notifyIMEEnabled(int state, String typeHint, String modeHint,
String actionHint, boolean landscapeFS) {
// notifyIMEEnabled() still needs the landscapeFS parameter because it is called from JNI
// code that assumes it has the same signature as XUL Fennec's (which does use landscapeFS).
if (mInputConnection != null)
mInputConnection.notifyIMEEnabled(state, typeHint, modeHint, actionHint);
public static void notifyIMEEnabled(int state, String typeHint,
String modeHint, String actionHint,
boolean landscapeFS) {
// notifyIMEEnabled() still needs the landscapeFS parameter
// because it is called from JNI code that assumes it has the
// same signature as XUL Fennec's (which does use landscapeFS).
// Bug 807124 will eliminate the need for landscapeFS
if (mEditableListener != null) {
mEditableListener.notifyIMEEnabled(state, typeHint,
modeHint, actionHint);
}
}
public static void notifyIMEChange(String text, int start, int end, int newEnd) {
if (mInputConnection != null)
mInputConnection.notifyIMEChange(text, start, end, newEnd);
if (newEnd < 0) { // Selection change
mEditableListener.onSelectionChange(start, end);
} else { // Text change
mEditableListener.onTextChange(text, start, end, newEnd);
}
}
private static CountDownLatch sGeckoPendingAcks = null;
@ -1997,8 +2010,10 @@ public class GeckoAppShell
}
public static void viewSizeChanged() {
if (mInputConnection != null && mInputConnection.isIMEEnabled()) {
sendEventToGecko(GeckoEvent.createBroadcastEvent("ScrollTo:FocusedInput", ""));
LayerView v = GeckoApp.mAppContext.getLayerView();
if (v != null && v.isIMEEnabled()) {
sendEventToGecko(GeckoEvent.createBroadcastEvent(
"ScrollTo:FocusedInput", ""));
}
}

View File

@ -0,0 +1,815 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import android.text.Editable;
import android.text.InputFilter;
import android.text.Spanned;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Selection;
import android.text.style.UnderlineSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.BackgroundColorSpan;
import android.util.Log;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;
// interface for the UI thread
interface GeckoEditableClient {
void sendEvent(GeckoEvent event);
Editable getEditable();
void setUpdateGecko(boolean update);
void setListener(GeckoEditableListener listener);
}
/* interface for the Editable to listen to the Gecko thread
and also for the UI thread to listen to the Editable */
interface GeckoEditableListener {
void notifyIME(int type, int state);
void notifyIMEEnabled(int state, String typeHint,
String modeHint, String actionHint);
void onSelectionChange(int start, int end);
void onTextChange(String text, int start, int oldEnd, int newEnd);
}
/*
GeckoEditable implements only some functions of Editable
The field mText contains the actual underlying
SpannableStringBuilder/Editable that contains our text.
*/
final class GeckoEditable
implements InvocationHandler, Editable,
GeckoEditableClient, GeckoEditableListener {
private static final boolean DEBUG = false;
private static final String LOGTAG = "GeckoEditable";
private static final int NOTIFY_IME_REPLY_EVENT = 1;
// Filters to implement Editable's filtering functionality
private InputFilter[] mFilters;
private final SpannableStringBuilder mText;
private final Editable mProxy;
private GeckoEditableListener mListener;
private final ActionQueue mActionQueue;
private int mSavedSelectionStart;
private volatile int mGeckoUpdateSeqno;
private int mUIUpdateSeqno;
private int mLastUIUpdateSeqno;
private boolean mUpdateGecko;
/* An action that alters the Editable
Each action corresponds to a Gecko event. While the Gecko event is being sent to the Gecko
thread, the action stays on top of mActions queue. After the Gecko event is processed and
replied, the action is removed from the queue
*/
private static final class Action {
// For input events (keypress, etc.); use with IME_SYNCHRONIZE
static final int TYPE_EVENT = 0;
// For Editable.replace() call; use with IME_REPLACE_TEXT
static final int TYPE_REPLACE_TEXT = 1;
/* For Editable.setSpan(Selection...) call; use with IME_SYNCHRONIZE
Note that we don't use this with IME_SET_SELECTION because we don't want to update the
Gecko selection at the point of this action. The Gecko selection is updated only after
UI has updated its selection (during IME_SYNCHRONIZE reply) */
static final int TYPE_SET_SELECTION = 2;
// For Editable.setSpan() call; use with IME_SYNCHRONIZE
static final int TYPE_SET_SPAN = 3;
// For Editable.removeSpan() call; use with IME_SYNCHRONIZE
static final int TYPE_REMOVE_SPAN = 4;
final int mType;
int mStart;
int mEnd;
CharSequence mSequence;
Object mSpanObject;
int mSpanFlags;
boolean mShouldUpdate;
Action(int type) {
mType = type;
}
static Action newReplaceText(CharSequence text, int start, int end) {
if (start < 0 || start > end) {
throw new IllegalArgumentException("invalid replace text offsets");
}
final Action action = new Action(TYPE_REPLACE_TEXT);
action.mSequence = text;
action.mStart = start;
action.mEnd = end;
return action;
}
static Action newSetSelection(int start, int end) {
if (start < 0 || start > end) {
throw new IllegalArgumentException("invalid selection offsets");
}
final Action action = new Action(TYPE_SET_SELECTION);
action.mStart = start;
action.mEnd = end;
return action;
}
static Action newSetSpan(Object object, int start, int end, int flags) {
if (start < 0 || start > end) {
throw new IllegalArgumentException("invalid span offsets");
}
final Action action = new Action(TYPE_SET_SPAN);
action.mSpanObject = object;
action.mStart = start;
action.mEnd = end;
action.mSpanFlags = flags;
return action;
}
}
/* Queue of editing actions sent to Gecko thread that
the Gecko thread has not responded to yet */
private final class ActionQueue {
private final ConcurrentLinkedQueue<Action> mActions;
private final Semaphore mActionsActive;
ActionQueue() {
mActions = new ConcurrentLinkedQueue<Action>();
mActionsActive = new Semaphore(1);
}
void offer(Action action) {
if (DEBUG) {
GeckoApp.assertOnUiThread();
}
/* Events don't need update because they generate text/selection
notifications which will do the updating for us */
if (action.mType != Action.TYPE_EVENT) {
action.mShouldUpdate = mUpdateGecko;
}
if (mActions.isEmpty()) {
mActionsActive.acquireUninterruptibly();
mActions.offer(action);
} else synchronized(this) {
// tryAcquire here in case Gecko thread has just released it
mActionsActive.tryAcquire();
mActions.offer(action);
}
switch (action.mType) {
case Action.TYPE_EVENT:
case Action.TYPE_SET_SELECTION:
case Action.TYPE_SET_SPAN:
case Action.TYPE_REMOVE_SPAN:
GeckoAppShell.sendEventToGecko(
GeckoEvent.createIMEEvent(GeckoEvent.IME_SYNCHRONIZE));
break;
case Action.TYPE_REPLACE_TEXT:
GeckoAppShell.sendEventToGecko(GeckoEvent.createIMEReplaceEvent(
action.mStart, action.mEnd, action.mSequence.toString()));
break;
}
++mUIUpdateSeqno;
}
void poll() {
if (DEBUG) {
GeckoApp.assertOnGeckoThread();
}
if (mActions.isEmpty()) {
throw new IllegalStateException("empty actions queue");
}
mActions.poll();
// Don't bother locking if queue is not empty yet
if (mActions.isEmpty()) {
synchronized(this) {
if (mActions.isEmpty()) {
mActionsActive.release();
}
}
}
}
Action peek() {
if (DEBUG) {
GeckoApp.assertOnGeckoThread();
}
if (mActions.isEmpty()) {
throw new IllegalStateException("empty actions queue");
}
return mActions.peek();
}
void syncWithGecko() {
if (DEBUG) {
GeckoApp.assertOnUiThread();
}
if (!mActions.isEmpty()) {
mActionsActive.acquireUninterruptibly();
mActionsActive.release();
}
}
boolean isEmpty() {
return mActions.isEmpty();
}
}
GeckoEditable() {
mActionQueue = new ActionQueue();
mSavedSelectionStart = -1;
mUpdateGecko = true;
mText = new SpannableStringBuilder();
final Class[] PROXY_INTERFACES = { Editable.class };
mProxy = (Editable)Proxy.newProxyInstance(
Editable.class.getClassLoader(),
PROXY_INTERFACES, this);
}
private static void geckoPostToUI(Runnable runnable) {
GeckoApp.mAppContext.mMainHandler.post(runnable);
}
private void geckoUpdateGecko(final boolean force) {
/* We do not increment the seqno here, but only check it, because geckoUpdateGecko is a
request for update. If we incremented the seqno here, geckoUpdateGecko would have
prevented other updates from occurring */
final int seqnoWhenPosted = mGeckoUpdateSeqno;
geckoPostToUI(new Runnable() {
public void run() {
mActionQueue.syncWithGecko();
if (seqnoWhenPosted == mGeckoUpdateSeqno) {
uiUpdateGecko(force);
}
}
});
}
private void uiUpdateGecko(boolean force) {
if (!force && mUIUpdateSeqno == mLastUIUpdateSeqno) {
if (DEBUG) {
Log.d(LOGTAG, "uiUpdateGecko() skipped");
}
return;
}
mLastUIUpdateSeqno = mUIUpdateSeqno;
mActionQueue.syncWithGecko();
if (DEBUG) {
Log.d(LOGTAG, "uiUpdateGecko()");
}
final int selStart = mText.getSpanStart(Selection.SELECTION_START);
final int selEnd = mText.getSpanEnd(Selection.SELECTION_END);
int composingStart = mText.length();
int composingEnd = 0;
Object[] spans = mText.getSpans(0, composingStart, Object.class);
for (Object span : spans) {
if ((mText.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) {
composingStart = Math.min(composingStart, mText.getSpanStart(span));
composingEnd = Math.max(composingEnd, mText.getSpanEnd(span));
}
}
if (DEBUG) {
Log.d(LOGTAG, " range = " + composingStart + "-" + composingEnd);
Log.d(LOGTAG, " selection = " + selStart + "-" + selEnd);
}
if (composingStart >= composingEnd) {
GeckoAppShell.sendEventToGecko(GeckoEvent.createIMEEvent(
GeckoEvent.IME_REMOVE_COMPOSITION));
if (selStart >= 0 && selEnd >= 0) {
GeckoAppShell.sendEventToGecko(
GeckoEvent.createIMESelectEvent(selStart, selEnd));
}
return;
}
if (selEnd >= composingStart && selEnd <= composingEnd) {
GeckoAppShell.sendEventToGecko(GeckoEvent.createIMERangeEvent(
selEnd - composingStart, selEnd - composingStart,
GeckoEvent.IME_RANGE_CARETPOSITION, 0, 0, 0));
}
int rangeStart = composingStart;
do {
int rangeType, rangeStyles = 0;
int rangeForeColor = 0, rangeBackColor = 0;
int rangeEnd = mText.nextSpanTransition(rangeStart, composingEnd, Object.class);
if (selStart > rangeStart && selStart < rangeEnd) {
rangeEnd = selStart;
} else if (selEnd > rangeStart && selEnd < rangeEnd) {
rangeEnd = selEnd;
}
spans = mText.getSpans(rangeStart, rangeEnd, Object.class);
if (DEBUG) {
Log.d(LOGTAG, " found " + spans.length + " spans @ " +
rangeStart + "-" + rangeEnd);
}
if (spans.length == 0) {
rangeType = (selStart == rangeStart && selEnd == rangeEnd)
? GeckoEvent.IME_RANGE_SELECTEDRAWTEXT
: GeckoEvent.IME_RANGE_RAWINPUT;
} else {
rangeType = (selStart == rangeStart && selEnd == rangeEnd)
? GeckoEvent.IME_RANGE_SELECTEDCONVERTEDTEXT
: GeckoEvent.IME_RANGE_CONVERTEDTEXT;
for (Object span : spans) {
if (span instanceof UnderlineSpan) {
rangeStyles |= GeckoEvent.IME_RANGE_UNDERLINE;
} else if (span instanceof ForegroundColorSpan) {
rangeStyles |= GeckoEvent.IME_RANGE_FORECOLOR;
rangeForeColor =
((ForegroundColorSpan)span).getForegroundColor();
} else if (span instanceof BackgroundColorSpan) {
rangeStyles |= GeckoEvent.IME_RANGE_BACKCOLOR;
rangeBackColor =
((BackgroundColorSpan)span).getBackgroundColor();
}
}
}
GeckoAppShell.sendEventToGecko(GeckoEvent.createIMERangeEvent(
rangeStart - composingStart, rangeEnd - composingStart,
rangeType, rangeStyles, rangeForeColor, rangeBackColor));
rangeStart = rangeEnd;
if (DEBUG) {
Log.d(LOGTAG, " added " + rangeType + " : " + rangeStyles +
" : " + Integer.toHexString(rangeForeColor) +
" : " + Integer.toHexString(rangeBackColor));
}
} while (rangeStart < composingEnd);
GeckoAppShell.sendEventToGecko(GeckoEvent.createIMECompositionEvent(
composingStart, composingEnd));
}
// GeckoEditableClient interface
@Override
public void sendEvent(GeckoEvent event) {
if (DEBUG) {
// GeckoEditableClient methods should all be called from the UI thread
GeckoApp.assertOnUiThread();
}
/*
We are actually sending two events to Gecko here,
1. Event from the event parameter (key event, etc.)
2. Sync event from the mActionQueue.offer call
The first event is a normal GeckoEvent that does not reply back to us,
the second sync event will have a reply, during which we see that there is a pending
event-type action, and update the selection/composition/etc. accordingly.
*/
GeckoAppShell.sendEventToGecko(event);
mActionQueue.offer(new Action(Action.TYPE_EVENT));
}
@Override
public Editable getEditable() {
if (DEBUG) {
// GeckoEditableClient methods should all be called from the UI thread
GeckoApp.assertOnUiThread();
}
return mProxy;
}
@Override
public void setUpdateGecko(boolean update) {
if (DEBUG) {
// GeckoEditableClient methods should all be called from the UI thread
GeckoApp.assertOnUiThread();
}
if (update) {
uiUpdateGecko(false);
}
mUpdateGecko = update;
}
@Override
public void setListener(GeckoEditableListener listener) {
if (DEBUG) {
// GeckoEditableClient methods should all be called from the UI thread
GeckoApp.assertOnUiThread();
}
mListener = listener;
}
// GeckoEditableListener interface
void geckoActionReply() {
if (DEBUG) {
// GeckoEditableListener methods should all be called from the Gecko thread
GeckoApp.assertOnGeckoThread();
}
final Action action = mActionQueue.peek();
switch (action.mType) {
case Action.TYPE_SET_SELECTION:
final int len = mText.length();
final int selStart = Math.min(action.mStart, len);
final int selEnd = Math.min(action.mEnd, len);
if (selStart < action.mStart || selEnd < action.mEnd) {
Log.w(LOGTAG, "IME sync error: selection out of bounds");
}
Selection.setSelection(mText, selStart, selEnd);
geckoPostToUI(new Runnable() {
public void run() {
mActionQueue.syncWithGecko();
final int start = Selection.getSelectionStart(mText);
final int end = Selection.getSelectionEnd(mText);
if (mListener != null &&
selStart == start && selEnd == end) {
// There has not been another new selection in the mean time that
// made this notification out-of-date
mListener.onSelectionChange(start, end);
}
}
});
break;
case Action.TYPE_SET_SPAN:
mText.setSpan(action.mSpanObject, action.mStart, action.mEnd, action.mSpanFlags);
break;
}
if (action.mShouldUpdate) {
geckoUpdateGecko(false);
}
mActionQueue.poll();
}
@Override
public void notifyIME(final int type, final int state) {
if (DEBUG) {
// GeckoEditableListener methods should all be called from the Gecko thread
GeckoApp.assertOnGeckoThread();
}
if (type == NOTIFY_IME_REPLY_EVENT) {
geckoActionReply();
return;
}
geckoPostToUI(new Runnable() {
public void run() {
// Make sure there are no other things going on
mActionQueue.syncWithGecko();
if (mListener != null) {
mListener.notifyIME(type, state);
}
}
});
}
@Override
public void notifyIMEEnabled(final int state, final String typeHint,
final String modeHint, final String actionHint) {
if (DEBUG) {
// GeckoEditableListener methods should all be called from the Gecko thread
GeckoApp.assertOnGeckoThread();
}
geckoPostToUI(new Runnable() {
public void run() {
// Make sure there are no other things going on
mActionQueue.syncWithGecko();
if (mListener != null) {
mListener.notifyIMEEnabled(state, typeHint,
modeHint, actionHint);
}
}
});
}
@Override
public void onSelectionChange(final int start, final int end) {
if (DEBUG) {
// GeckoEditableListener methods should all be called from the Gecko thread
GeckoApp.assertOnGeckoThread();
}
if (start < 0 || start > end || end > mText.length()) {
throw new IllegalArgumentException("invalid selection notification range");
}
final int seqnoWhenPosted = ++mGeckoUpdateSeqno;
geckoPostToUI(new Runnable() {
public void run() {
mActionQueue.syncWithGecko();
/* check to see there has not been another action that potentially changed the
selection. If so, we can skip this update because we know there is another
update right after this one that will replace the effect of this update */
if (mGeckoUpdateSeqno == seqnoWhenPosted) {
/* In this case, Gecko's selection has changed and it's notifying us to change
Java's selection. In the normal case, whenever Java's selection changes,
we go back and set Gecko's selection as well. However, in this case,
since Gecko's selection is already up-to-date, we skip this step. */
boolean oldUpdateGecko = mUpdateGecko;
mUpdateGecko = false;
Selection.setSelection(mProxy, start, end);
mUpdateGecko = oldUpdateGecko;
}
}
});
}
@Override
public void onTextChange(final String text, final int start,
final int unboundedOldEnd, final int unboundedNewEnd) {
if (DEBUG) {
// GeckoEditableListener methods should all be called from the Gecko thread
GeckoApp.assertOnGeckoThread();
}
if (start < 0 || start > unboundedOldEnd) {
throw new IllegalArgumentException("invalid text notification range");
}
/* For the "end" parameters, Gecko can pass in a large
number to denote "end of the text". Fix that here */
final int oldEnd = unboundedOldEnd > mText.length() ? mText.length() : unboundedOldEnd;
// new end should always match text
if (unboundedNewEnd < (start + text.length())) {
throw new IllegalArgumentException("newEnd does not match text");
}
final int newEnd = start + text.length();
if (!mActionQueue.isEmpty()) {
final Action action = mActionQueue.peek();
if (action.mType == Action.TYPE_REPLACE_TEXT &&
action.mStart == start &&
text.equals(action.mSequence.toString())) {
// Replace using saved text to preserve spans
mText.replace(start, oldEnd, action.mSequence,
0, action.mSequence.length());
} else {
mText.replace(start, oldEnd, text, 0, text.length());
}
} else {
mText.replace(start, oldEnd, text, 0, text.length());
geckoPostToUI(new Runnable() {
public void run() {
if (mListener != null) {
mListener.onTextChange(text, start, oldEnd, newEnd);
}
}
});
}
}
// InvocationHandler interface
private static StringBuilder debugAppend(StringBuilder sb, Object obj) {
if (obj == null) {
sb.append("null");
} else if (obj instanceof GeckoEditable) {
sb.append("GeckoEditable");
} else if (Proxy.isProxyClass(obj.getClass())) {
debugAppend(sb, Proxy.getInvocationHandler(obj));
} else if (obj instanceof CharSequence) {
sb.append("\"").append(obj.toString().replace('\n', '\u21b2')).append("\"");
} else if (obj.getClass().isArray()) {
Class cls = obj.getClass();
sb.append(cls.getComponentType().getSimpleName()).append("[")
.append(java.lang.reflect.Array.getLength(obj)).append("]");
} else {
sb.append(obj.toString());
}
return sb;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object target;
final Class methodInterface = method.getDeclaringClass();
if (DEBUG) {
// Editable methods should all be called from the UI thread
GeckoApp.assertOnUiThread();
}
if (methodInterface == Editable.class ||
methodInterface == Appendable.class ||
methodInterface == Spannable.class) {
// Method alters the Editable; route calls to our implementation
target = this;
} else {
// Method queries the Editable; must sync with Gecko first
// then call on the inner Editable itself
mActionQueue.syncWithGecko();
target = mText;
}
Object ret = method.invoke(target, args);
if (DEBUG) {
StringBuilder log = new StringBuilder(method.getName());
log.append("(");
for (Object arg : args) {
debugAppend(log, arg).append(", ");
}
if (args.length > 0) {
log.setLength(log.length() - 2);
}
if (method.getReturnType().equals(Void.TYPE)) {
log.append(")");
} else {
debugAppend(log.append(") = "), ret);
}
Log.d(LOGTAG, log.toString());
}
return ret;
}
// Spannable interface
private static boolean isCompositionSpan(Object what, int flags) {
return (flags & Spanned.SPAN_COMPOSING) != 0 ||
what instanceof UnderlineSpan ||
what instanceof ForegroundColorSpan ||
what instanceof BackgroundColorSpan;
}
@Override
public void removeSpan(Object what) {
if (what == Selection.SELECTION_START ||
what == Selection.SELECTION_END) {
Log.w(LOGTAG, "selection removed with removeSpan()");
}
// Okay to remove immediately
mText.removeSpan(what);
if (mUpdateGecko) {
mActionQueue.offer(new Action(Action.TYPE_REMOVE_SPAN));
}
}
@Override
public void setSpan(Object what, int start, int end, int flags) {
if (what == Selection.SELECTION_START) {
if ((flags & Spanned.SPAN_INTERMEDIATE) != 0) {
// We will get the end offset next, just save the start for now
mSavedSelectionStart = start;
} else {
mActionQueue.offer(Action.newSetSelection(start, -1));
}
} else if (what == Selection.SELECTION_END) {
mActionQueue.offer(Action.newSetSelection(mSavedSelectionStart, end));
mSavedSelectionStart = -1;
} else {
mActionQueue.offer(Action.newSetSpan(what, start, end, flags));
}
}
// Appendable interface
@Override
public Editable append(CharSequence text) {
return replace(length(), length(), text, 0, text.length());
}
@Override
public Editable append(CharSequence text, int start, int end) {
return replace(length(), length(), text, start, end);
}
@Override
public Editable append(char text) {
return replace(length(), length(), String.valueOf(text), 0, 1);
}
// Editable interface
@Override
public InputFilter[] getFilters() {
return mFilters;
}
@Override
public void setFilters(InputFilter[] filters) {
mFilters = filters;
}
@Override
public void clearSpans() {
/* XXX this clears the selection spans too,
but there is no way to clear the corresponding selection in Gecko */
Log.w(LOGTAG, "selection cleared with clearSpans()");
mText.clearSpans();
}
@Override
public Editable replace(int st, int en,
CharSequence source, int start, int end) {
CharSequence text = source;
if (start < 0 || start > end || end > text.length()) {
throw new IllegalArgumentException("invalid replace offsets");
}
if (start != 0 || end != text.length()) {
text = text.subSequence(start, end);
}
if (mFilters != null) {
// Filter text before sending the request to Gecko
for (int i = 0; i < mFilters.length; ++i) {
final CharSequence cs = mFilters[i].filter(
text, 0, text.length(), mProxy, st, en);
if (cs != null) {
text = cs;
}
}
}
if (text == source) {
// Always create a copy
text = new SpannableString(source);
}
mActionQueue.offer(Action.newReplaceText(text,
Math.min(st, en), Math.max(st, en)));
return mProxy;
}
@Override
public void clear() {
replace(0, length(), "", 0, 0);
}
@Override
public Editable delete(int st, int en) {
return replace(st, en, "", 0, 0);
}
@Override
public Editable insert(int where, CharSequence text,
int start, int end) {
return replace(where, where, text, start, end);
}
@Override
public Editable insert(int where, CharSequence text) {
return replace(where, where, text, 0, text.length());
}
@Override
public Editable replace(int st, int en, CharSequence text) {
return replace(st, en, text, 0, text.length());
}
/* GetChars interface */
@Override
public void getChars(int start, int end, char[] dest, int destoff) {
throw new UnsupportedOperationException();
}
/* Spanned interface */
@Override
public int getSpanEnd(Object tag) {
throw new UnsupportedOperationException();
}
@Override
public int getSpanFlags(Object tag) {
throw new UnsupportedOperationException();
}
@Override
public int getSpanStart(Object tag) {
throw new UnsupportedOperationException();
}
@Override
public <T> T[] getSpans(int start, int end, Class<T> type) {
throw new UnsupportedOperationException();
}
@Override
public int nextSpanTransition(int start, int limit, Class type) {
throw new UnsupportedOperationException();
}
/* CharSequence interface */
@Override
public char charAt(int index) {
throw new UnsupportedOperationException();
}
@Override
public int length() {
throw new UnsupportedOperationException();
}
@Override
public CharSequence subSequence(int start, int end) {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
throw new UnsupportedOperationException();
}
}

View File

@ -80,14 +80,12 @@ public class GeckoEvent {
private static final int DOM_KEY_LOCATION_MOBILE = 4;
private static final int DOM_KEY_LOCATION_JOYSTICK = 5;
public static final int IME_COMPOSITION_END = 0;
public static final int IME_COMPOSITION_BEGIN = 1;
public static final int IME_SET_TEXT = 2;
public static final int IME_GET_TEXT = 3;
public static final int IME_DELETE_TEXT = 4;
public static final int IME_SET_SELECTION = 5;
public static final int IME_GET_SELECTION = 6;
public static final int IME_ADD_RANGE = 7;
public static final int IME_SYNCHRONIZE = 0;
public static final int IME_REPLACE_TEXT = 1;
public static final int IME_SET_SELECTION = 2;
public static final int IME_ADD_COMPOSITION_RANGE = 3;
public static final int IME_UPDATE_COMPOSITION = 4;
public static final int IME_REMOVE_COMPOSITION = 5;
public static final int IME_RANGE_CARETPOSITION = 1;
public static final int IME_RANGE_RAWINPUT = 2;
@ -118,7 +116,8 @@ public class GeckoEvent {
public int mMetaState, mFlags;
public int mKeyCode, mUnicodeChar;
public int mRepeatCount;
public int mOffset, mCount;
public int mCount;
public int mStart, mEnd;
public String mCharacters, mCharactersExtra;
public int mRangeType, mRangeStyles;
public int mRangeForeColor, mRangeBackColor;
@ -459,44 +458,51 @@ public class GeckoEvent {
return event;
}
public static GeckoEvent createIMEEvent(int imeAction, int offset, int count) {
public static GeckoEvent createIMEEvent(int action) {
GeckoEvent event = new GeckoEvent(IME_EVENT);
event.mAction = imeAction;
event.mOffset = offset;
event.mCount = count;
event.mAction = action;
return event;
}
private void InitIMERange(int action, int offset, int count,
int rangeType, int rangeStyles,
int rangeForeColor, int rangeBackColor) {
mAction = action;
mOffset = offset;
mCount = count;
mRangeType = rangeType;
mRangeStyles = rangeStyles;
mRangeForeColor = rangeForeColor;
mRangeBackColor = rangeBackColor;
return;
}
public static GeckoEvent createIMERangeEvent(int offset, int count,
int rangeType, int rangeStyles,
int rangeForeColor, int rangeBackColor,
String text) {
public static GeckoEvent createIMEReplaceEvent(int start, int end,
String text) {
GeckoEvent event = new GeckoEvent(IME_EVENT);
event.InitIMERange(IME_SET_TEXT, offset, count, rangeType, rangeStyles,
rangeForeColor, rangeBackColor);
event.mAction = IME_REPLACE_TEXT;
event.mStart = start;
event.mEnd = end;
event.mCharacters = text;
return event;
}
public static GeckoEvent createIMERangeEvent(int offset, int count,
int rangeType, int rangeStyles,
int rangeForeColor, int rangeBackColor) {
public static GeckoEvent createIMESelectEvent(int start, int end) {
GeckoEvent event = new GeckoEvent(IME_EVENT);
event.InitIMERange(IME_ADD_RANGE, offset, count, rangeType, rangeStyles,
rangeForeColor, rangeBackColor);
event.mAction = IME_SET_SELECTION;
event.mStart = start;
event.mEnd = end;
return event;
}
public static GeckoEvent createIMECompositionEvent(int start, int end) {
GeckoEvent event = new GeckoEvent(IME_EVENT);
event.mAction = IME_UPDATE_COMPOSITION;
event.mStart = start;
event.mEnd = end;
return event;
}
public static GeckoEvent createIMERangeEvent(int start,
int end, int rangeType,
int rangeStyles,
int rangeForeColor,
int rangeBackColor) {
GeckoEvent event = new GeckoEvent(IME_EVENT);
event.mAction = IME_ADD_COMPOSITION_RANGE;
event.mStart = start;
event.mEnd = end;
event.mRangeType = rangeType;
event.mRangeStyles = rangeStyles;
event.mRangeForeColor = rangeForeColor;
event.mRangeBackColor = rangeBackColor;
return event;
}

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,7 @@ UTIL_JAVA_FILES := \
INISection.java \
util/EventDispatcher.java \
util/FloatUtils.java \
util/LruCache.java \
$(NULL)
FENNEC_JAVA_FILES = \
@ -76,6 +77,7 @@ FENNEC_JAVA_FILES = \
GeckoAppShell.java \
GeckoBatteryManager.java \
GeckoConnectivityReceiver.java \
GeckoEditable.java \
GeckoEvent.java \
GeckoHalDefines.java \
GeckoInputConnection.java \

View File

@ -156,6 +156,8 @@ class MemoryMonitor extends BroadcastReceiver {
}
ScreenshotHandler.disableScreenshot(false);
GeckoAppShell.geckoEventSync();
GeckoApp.mAppContext.getFavicons().clearMemCache();
}
}

View File

@ -35,7 +35,7 @@ public final class ScreenshotHandler implements Runnable {
private static final int BYTES_FOR_16BPP = 2;
private static final int MAX_PIXELS_PER_SLICE = 100000;
private static boolean sDisableScreenshot;
private static boolean sDisableScreenshot = true;
private static boolean sForceDisabled;
private static ScreenshotHandler sInstance;

View File

@ -7,8 +7,10 @@ package org.mozilla.gecko;
import org.mozilla.gecko.AwesomeBar.ContextMenuSubject;
import org.mozilla.gecko.db.BrowserContract.Combined;
import org.mozilla.gecko.db.BrowserContract.Images;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.BrowserDB.URLColumns;
import org.mozilla.gecko.util.GeckoAsyncTask;
import org.mozilla.gecko.util.GeckoEventListener;
import org.json.JSONArray;
@ -18,8 +20,13 @@ import org.json.JSONObject;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
@ -49,6 +56,7 @@ import android.widget.TextView;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
public static final String LOGTAG = "ALL_PAGES";
@ -68,6 +76,11 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
private LinearLayout mAllPagesView;
private boolean mAnimateSuggestions;
private View mSuggestionsOptInPrompt;
private Handler mHandler;
private static final int MESSAGE_LOAD_FAVICONS = 1;
private static final int MESSAGE_UPDATE_FAVICONS = 2;
private static final int DELAY_SHOW_THUMBNAILS = 550;
private class SearchEntryViewHolder {
public FlowLayout suggestionView;
@ -122,9 +135,14 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
((Activity)mContext).registerForContextMenu(mView);
mView.setTag(TAG);
AwesomeBarCursorAdapter adapter = getCursorAdapter();
((ListView)mView).setAdapter(adapter);
mView.setOnTouchListener(mListListener);
ListView listView = (ListView) mView;
listView.setAdapter(adapter);
listView.setOnTouchListener(mListListener);
mHandler = new AllPagesHandler();
}
return (ListView)mView;
}
@ -138,6 +156,10 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
Cursor cursor = adapter.getCursor();
if (cursor != null)
cursor.close();
mHandler.removeMessages(MESSAGE_UPDATE_FAVICONS);
mHandler.removeMessages(MESSAGE_LOAD_FAVICONS);
mHandler = null;
}
public void filter(String searchTerm) {
@ -196,6 +218,8 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
Cursor c = BrowserDB.filter(getContentResolver(), constraint, MAX_RESULTS);
c.getCount();
postLoadFavicons();
long end = SystemClock.uptimeMillis();
int time = (int)(end - start);
Log.i(LOGTAG, "Got cursor in " + time + "ms");
@ -398,8 +422,8 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
updateTitle(viewHolder.titleView, cursor);
updateUrl(viewHolder.urlView, cursor);
updateFavicon(viewHolder.faviconView, cursor);
updateBookmarkIcon(viewHolder.bookmarkIconView, cursor);
displayFavicon(viewHolder);
}
return convertView;
@ -706,4 +730,129 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
private void unregisterEventListener(String event) {
GeckoAppShell.getEventDispatcher().unregisterEventListener(event, this);
}
private List<String> getUrlsWithoutFavicon() {
List<String> urls = new ArrayList<String>();
Cursor c = mCursorAdapter.getCursor();
if (c == null || !c.moveToFirst())
return urls;
do {
final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
// We only want to load favicons from DB if they are not in the
// memory cache yet.
Favicons favicons = GeckoApp.mAppContext.getFavicons();
if (favicons.getFaviconFromMemCache(url) != null)
continue;
urls.add(url);
} while (c.moveToNext());
return urls;
}
public void storeFaviconsInMemCache(Cursor c) {
try {
if (c == null || !c.moveToFirst())
return;
Favicons favicons = GeckoApp.mAppContext.getFavicons();
do {
final String url = c.getString(c.getColumnIndexOrThrow(Images.URL));
final byte[] b = c.getBlob(c.getColumnIndexOrThrow(Images.FAVICON));
if (b == null)
continue;
Bitmap favicon = BitmapFactory.decodeByteArray(b, 0, b.length);
if (favicon == null)
continue;
Drawable faviconDrawable = new BitmapDrawable(getResources(), favicon);
favicons.putFaviconInMemCache(url, faviconDrawable);
} while (c.moveToNext());
} finally {
if (c != null)
c.close();
}
}
private void loadFaviconsForCurrentResults() {
final List<String> urls = getUrlsWithoutFavicon();
if (urls.size() == 0)
return;
(new GeckoAsyncTask<Void, Void, Cursor>(GeckoApp.mAppContext, GeckoAppShell.getHandler()) {
@Override
public Cursor doInBackground(Void... params) {
return BrowserDB.getFaviconsForUrls(getContentResolver(), urls);
}
@Override
public void onPostExecute(Cursor c) {
storeFaviconsInMemCache(c);
postUpdateFavicons();
}
}).execute();
}
private void displayFavicon(AwesomeEntryViewHolder viewHolder) {
final String url = viewHolder.urlView.getText().toString();
Favicons favicons = GeckoApp.mAppContext.getFavicons();
viewHolder.faviconView.setImageDrawable(favicons.getFaviconFromMemCache(url));
}
private void updateFavicons() {
ListView listView = (ListView) mView;
for (int i = 0; i < listView.getChildCount(); i++) {
final View view = listView.getChildAt(i);
final Object tag = view.getTag();
if (tag == null || !(tag instanceof AwesomeEntryViewHolder))
continue;
final AwesomeEntryViewHolder viewHolder = (AwesomeEntryViewHolder) tag;
displayFavicon(viewHolder);
}
mView.invalidate();
}
private void postUpdateFavicons() {
if (mHandler == null)
return;
Message msg = mHandler.obtainMessage(MESSAGE_UPDATE_FAVICONS,
AllPagesTab.this);
mHandler.removeMessages(MESSAGE_UPDATE_FAVICONS);
mHandler.sendMessage(msg);
}
private void postLoadFavicons() {
if (mHandler == null)
return;
Message msg = mHandler.obtainMessage(MESSAGE_LOAD_FAVICONS,
AllPagesTab.this);
mHandler.removeMessages(MESSAGE_LOAD_FAVICONS);
mHandler.sendMessageDelayed(msg, 200);
}
private class AllPagesHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_LOAD_FAVICONS:
loadFaviconsForCurrentResults();
break;
case MESSAGE_UPDATE_FAVICONS:
updateFavicons();
break;
}
}
}
}

View File

@ -12,6 +12,8 @@ import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.drawable.BitmapDrawable;
import java.util.List;
public class BrowserDB {
public static String ABOUT_PAGES_URL_FILTER = "about:%";
@ -73,12 +75,16 @@ public class BrowserDB {
public BitmapDrawable getFaviconForUrl(ContentResolver cr, String uri);
public Cursor getFaviconsForUrls(ContentResolver cr, List<String> urls);
public void updateFaviconForUrl(ContentResolver cr, String uri, BitmapDrawable favicon);
public void updateThumbnailForUrl(ContentResolver cr, String uri, BitmapDrawable thumbnail);
public byte[] getThumbnailForUrl(ContentResolver cr, String uri);
public Cursor getThumbnailsForUrls(ContentResolver cr, List<String> urls);
public void removeThumbnails(ContentResolver cr);
public void registerBookmarkObserver(ContentResolver cr, ContentObserver observer);
@ -186,6 +192,10 @@ public class BrowserDB {
return sDb.getFaviconForUrl(cr, uri);
}
public static Cursor getFaviconsForUrls(ContentResolver cr, List<String> urls) {
return sDb.getFaviconsForUrls(cr, urls);
}
public static void updateFaviconForUrl(ContentResolver cr, String uri, BitmapDrawable favicon) {
sDb.updateFaviconForUrl(cr, uri, favicon);
}
@ -198,6 +208,10 @@ public class BrowserDB {
return sDb.getThumbnailForUrl(cr, uri);
}
public static Cursor getThumbnailsForUrls(ContentResolver cr, List<String> urls) {
return sDb.getThumbnailsForUrls(cr, urls);
}
public static void removeThumbnails(ContentResolver cr) {
sDb.removeThumbnails(cr);
}

View File

@ -69,7 +69,7 @@ public class BrowserProvider extends ContentProvider {
static final String DATABASE_NAME = "browser.db";
static final int DATABASE_VERSION = 11;
static final int DATABASE_VERSION = 12;
// Maximum age of deleted records to be cleaned up (20 days in ms)
static final long MAX_AGE_OF_DELETED_RECORDS = 86400000 * 20;
@ -100,6 +100,8 @@ public class BrowserProvider extends ContentProvider {
static final String VIEW_BOOKMARKS_WITH_IMAGES = "bookmarks_with_images";
static final String VIEW_HISTORY_WITH_IMAGES = "history_with_images";
static final String VIEW_COMBINED = "combined";
static final String VIEW_COMBINED_WITH_IMAGES = "combined_with_images";
// Bookmark matches
@ -644,6 +646,74 @@ public class BrowserProvider extends ContentProvider {
" ON " + Combined.URL + " = " + qualifyColumn(TABLE_IMAGES, Images.URL));
}
private void createCombinedViewOn12(SQLiteDatabase db) {
debug("Creating " + VIEW_COMBINED + " view");
db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED + " AS" +
" SELECT " + Combined.BOOKMARK_ID + ", " +
Combined.HISTORY_ID + ", " +
// We need to return an _id column because CursorAdapter requires it for its
// default implementation for the getItemId() method. However, since
// we're not using this feature in the parts of the UI using this view,
// we can just use 0 for all rows.
"0 AS " + Combined._ID + ", " +
Combined.URL + ", " +
Combined.TITLE + ", " +
Combined.VISITS + ", " +
Combined.DISPLAY + ", " +
Combined.DATE_LAST_VISITED +
" FROM (" +
// Bookmarks without history.
" SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " +
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " +
Bookmarks.FIXED_READING_LIST_ID + " THEN " + Combined.DISPLAY_READER + " ELSE " +
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
"-1 AS " + Combined.HISTORY_ID + ", " +
"-1 AS " + Combined.VISITS + ", " +
"-1 AS " + Combined.DATE_LAST_VISITED +
" FROM " + TABLE_BOOKMARKS +
" WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + " AND " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " = 0 AND " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) +
" NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" +
" UNION ALL" +
// History with and without bookmark.
" SELECT " + "CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " ELSE NULL END AS " + Combined.BOOKMARK_ID + ", " +
qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " +
// Prioritze bookmark titles over history titles, since the user may have
// customized the title for a bookmark.
"COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " +
qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " +
// Only use DISPLAY_READER if the matching bookmark entry inside reading
// list folder is not marked as deleted.
"CASE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.IS_DELETED) + " WHEN 0 THEN CASE " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.PARENT) + " WHEN " + Bookmarks.FIXED_READING_LIST_ID +
" THEN " + Combined.DISPLAY_READER + " ELSE " + Combined.DISPLAY_NORMAL + " END ELSE " +
Combined.DISPLAY_NORMAL + " END AS " + Combined.DISPLAY + ", " +
qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " +
qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " +
qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED +
" FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS +
" ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) +
" WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND " +
qualifyColumn(TABLE_HISTORY, History.IS_DELETED) + " = 0 AND (" +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " +
qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " = " + Bookmarks.TYPE_BOOKMARK + ") " +
")");
debug("Creating " + VIEW_COMBINED_WITH_IMAGES + " view");
db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED_WITH_IMAGES + " AS" +
" SELECT *, " +
qualifyColumn(TABLE_IMAGES, Images.FAVICON) + " AS " + Combined.FAVICON + ", " +
qualifyColumn(TABLE_IMAGES, Images.THUMBNAIL) + " AS " + Combined.THUMBNAIL +
" FROM " + VIEW_COMBINED + " LEFT OUTER JOIN " + TABLE_IMAGES +
" ON " + Combined.URL + " = " + qualifyColumn(TABLE_IMAGES, Images.URL));
}
@Override
public void onCreate(SQLiteDatabase db) {
debug("Creating browser.db: " + db.getPath());
@ -651,10 +721,10 @@ public class BrowserProvider extends ContentProvider {
createBookmarksTable(db);
createHistoryTable(db);
createImagesTable(db);
createCombinedViewOn12(db);
createBookmarksWithImagesView(db);
createHistoryWithImagesView(db);
createCombinedWithImagesViewOn11(db);
createOrUpdateSpecialFolder(db, Bookmarks.PLACES_FOLDER_GUID,
R.string.bookmarks_folder_places, 0);
@ -1091,6 +1161,13 @@ public class BrowserProvider extends ContentProvider {
createCombinedWithImagesViewOn11(db);
}
private void upgradeDatabaseFrom11to12(SQLiteDatabase db) {
debug("Dropping view: " + VIEW_COMBINED_WITH_IMAGES);
db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED_WITH_IMAGES);
createCombinedViewOn12(db);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
debug("Upgrading browser.db: " + db.getPath() + " from " +
@ -1141,6 +1218,10 @@ public class BrowserProvider extends ContentProvider {
case 11:
upgradeDatabaseFrom10to11(db);
break;
case 12:
upgradeDatabaseFrom11to12(db);
break;
}
}
@ -1931,7 +2012,11 @@ public class BrowserProvider extends ContentProvider {
groupBy = Combined.URL;
qb.setProjectionMap(COMBINED_PROJECTION_MAP);
qb.setTables(VIEW_COMBINED_WITH_IMAGES);
if (hasImagesInProjection(projection))
qb.setTables(VIEW_COMBINED_WITH_IMAGES);
else
qb.setTables(VIEW_COMBINED);
break;
}

View File

@ -31,6 +31,7 @@ import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
// Calculate these once, at initialization. isLoggable is too expensive to
@ -181,7 +182,6 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
new String[] { Combined._ID,
Combined.URL,
Combined.TITLE,
Combined.FAVICON,
Combined.DISPLAY,
Combined.BOOKMARK_ID,
Combined.HISTORY_ID },
@ -194,8 +194,7 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
return filterAllSites(cr,
new String[] { Combined._ID,
Combined.URL,
Combined.TITLE,
Combined.THUMBNAIL },
Combined.TITLE },
"",
limit,
BrowserDB.ABOUT_PAGES_URL_FILTER);
@ -619,6 +618,27 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
return new BitmapDrawable(bitmap);
}
public Cursor getFaviconsForUrls(ContentResolver cr, List<String> urls) {
StringBuffer selection = new StringBuffer();
String[] selectionArgs = new String[urls.size()];
for (int i = 0; i < urls.size(); i++) {
final String url = urls.get(i);
if (i > 0)
selection.append(" OR ");
selection.append(Images.URL + " = ?");
selectionArgs[i] = url;
}
return cr.query(mImagesUriWithProfile,
new String[] { Images.URL, Images.FAVICON },
selection.toString(),
selectionArgs,
null);
}
public void updateFaviconForUrl(ContentResolver cr, String uri,
BitmapDrawable favicon) {
Bitmap bitmap = favicon.getBitmap();
@ -687,6 +707,27 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
return b;
}
public Cursor getThumbnailsForUrls(ContentResolver cr, List<String> urls) {
StringBuffer selection = new StringBuffer();
String[] selectionArgs = new String[urls.size()];
for (int i = 0; i < urls.size(); i++) {
final String url = urls.get(i);
if (i > 0)
selection.append(" OR ");
selection.append(Images.URL + " = ?");
selectionArgs[i] = url;
}
return cr.query(mImagesUriWithProfile,
new String[] { Images.URL, Images.THUMBNAIL },
selection.toString(),
selectionArgs,
null);
}
public void removeThumbnails(ContentResolver cr) {
ContentValues values = new ContentValues();
values.putNull(Images.THUMBNAIL);

View File

@ -16,4 +16,5 @@ public interface InputConnectionHandler
boolean onKeyLongPress(int keyCode, KeyEvent event);
boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event);
boolean onKeyUp(int keyCode, KeyEvent event);
boolean isIMEEnabled();
}

View File

@ -261,6 +261,13 @@ public class LayerView extends FrameLayout {
return false;
}
public boolean isIMEEnabled() {
if (mInputConnectionHandler != null) {
return mInputConnectionHandler.isIMEEnabled();
}
return false;
}
public void requestRender() {
if (mListener != null) {
mListener.renderRequested();

View File

@ -218,7 +218,7 @@
<item name="android:paddingBottom">@dimen/abouthome_icon_radius</item>
<item name="android:paddingLeft">0dip</item>
<item name="android:paddingRight">0dip</item>
<item name="android:scaleType">centerCrop</item>
<item name="android:scaleType">fitCenter</item>
</style>
<style name="AboutHome.Thumbnail.Label">

View File

@ -0,0 +1,323 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mozilla.gecko.util;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Static library version of {@link android.util.LruCache}. Used to write apps
* that run on API levels prior to 12. When running on API level 12 or above,
* this implementation is still used; it does not try to switch to the
* framework's implementation. See the framework SDK documentation for a class
* overview.
*/
public class LruCache<K, V> {
private final LinkedHashMap<K, V> map;
/** Size of this cache in units. Not necessarily the number of elements. */
private int size;
private int maxSize;
private int putCount;
private int createCount;
private int evictionCount;
private int hitCount;
private int missCount;
/**
* @param maxSize for caches that do not override {@link #sizeOf}, this is
* the maximum number of entries in the cache. For all other caches,
* this is the maximum sum of the sizes of the entries in this cache.
*/
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
/**
* Returns the value for {@code key} if it exists in the cache or can be
* created by {@code #create}. If a value was returned, it is moved to the
* head of the queue. This returns null if a value is not cached and cannot
* be created.
*/
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
}
/*
* Attempt to create a value. This may take a long time, and the map
* may be different when create() returns. If a conflicting value was
* added to the map while create() was working, we leave that value in
* the map and release the created value.
*/
V createdValue = create(key);
if (createdValue == null) {
return null;
}
synchronized (this) {
createCount++;
mapValue = map.put(key, createdValue);
if (mapValue != null) {
// There was a conflict so undo that last put
map.put(key, mapValue);
} else {
size += safeSizeOf(key, createdValue);
}
}
if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
trimToSize(maxSize);
return createdValue;
}
}
/**
* Caches {@code value} for {@code key}. The value is moved to the head of
* the queue.
*
* @return the previous value mapped by {@code key}.
*/
public final V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}
V previous;
synchronized (this) {
putCount++;
size += safeSizeOf(key, value);
previous = map.put(key, value);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, value);
}
trimToSize(maxSize);
return previous;
}
/**
* @param maxSize the maximum size of the cache before returning. May be -1
* to evict even 0-sized elements.
*/
private void trimToSize(int maxSize) {
while (true) {
K key;
V value;
synchronized (this) {
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}
if (size <= maxSize || map.isEmpty()) {
break;
}
Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
/**
* Removes the entry for {@code key} if it exists.
*
* @return the previous value mapped by {@code key}.
*/
public final V remove(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V previous;
synchronized (this) {
previous = map.remove(key);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, null);
}
return previous;
}
/**
* Called for entries that have been evicted or removed. This method is
* invoked when a value is evicted to make space, removed by a call to
* {@link #remove}, or replaced by a call to {@link #put}. The default
* implementation does nothing.
*
* <p>The method is called without synchronization: other threads may
* access the cache while this method is executing.
*
* @param evicted true if the entry is being removed to make space, false
* if the removal was caused by a {@link #put} or {@link #remove}.
* @param newValue the new value for {@code key}, if it exists. If non-null,
* this removal was caused by a {@link #put}. Otherwise it was caused by
* an eviction or a {@link #remove}.
*/
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
/**
* Called after a cache miss to compute a value for the corresponding key.
* Returns the computed value or null if no value can be computed. The
* default implementation returns null.
*
* <p>The method is called without synchronization: other threads may
* access the cache while this method is executing.
*
* <p>If a value for {@code key} exists in the cache when this method
* returns, the created value will be released with {@link #entryRemoved}
* and discarded. This can occur when multiple threads request the same key
* at the same time (causing multiple values to be created), or when one
* thread calls {@link #put} while another is creating a value for the same
* key.
*/
protected V create(K key) {
return null;
}
private int safeSizeOf(K key, V value) {
int result = sizeOf(key, value);
if (result < 0) {
throw new IllegalStateException("Negative size: " + key + "=" + value);
}
return result;
}
/**
* Returns the size of the entry for {@code key} and {@code value} in
* user-defined units. The default implementation returns 1 so that size
* is the number of entries and max size is the maximum number of entries.
*
* <p>An entry's size must not change while it is in the cache.
*/
protected int sizeOf(K key, V value) {
return 1;
}
/**
* Clear the cache, calling {@link #entryRemoved} on each removed entry.
*/
public final void evictAll() {
trimToSize(-1); // -1 will evict 0-sized elements
}
/**
* For caches that do not override {@link #sizeOf}, this returns the number
* of entries in the cache. For all other caches, this returns the sum of
* the sizes of the entries in this cache.
*/
public synchronized final int size() {
return size;
}
/**
* For caches that do not override {@link #sizeOf}, this returns the maximum
* number of entries in the cache. For all other caches, this returns the
* maximum sum of the sizes of the entries in this cache.
*/
public synchronized final int maxSize() {
return maxSize;
}
/**
* Returns the number of times {@link #get} returned a value.
*/
public synchronized final int hitCount() {
return hitCount;
}
/**
* Returns the number of times {@link #get} returned null or required a new
* value to be created.
*/
public synchronized final int missCount() {
return missCount;
}
/**
* Returns the number of times {@link #create(Object)} returned a value.
*/
public synchronized final int createCount() {
return createCount;
}
/**
* Returns the number of times {@link #put} was called.
*/
public synchronized final int putCount() {
return putCount;
}
/**
* Returns the number of values that have been evicted.
*/
public synchronized final int evictionCount() {
return evictionCount;
}
/**
* Returns a copy of the current contents of the cache, ordered from least
* recently accessed to most recently accessed.
*/
public synchronized final Map<K, V> snapshot() {
return new LinkedHashMap<K, V>(map);
}
@Override public synchronized final String toString() {
int accesses = hitCount + missCount;
int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
maxSize, hitCount, missCount, hitPercent);
}
}

View File

@ -6586,6 +6586,10 @@ var ActivityObserver = {
let isForeground = false
switch (aTopic) {
case "application-background" :
let doc = BrowserApp.selectedTab.browser.contentDocument;
if (doc.mozFullScreen) {
doc.mozCancelFullScreen();
}
isForeground = false;
break;
case "application-foreground" :

View File

@ -52,20 +52,11 @@ SessionStore.prototype = {
// Get file references
this._sessionFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
this._sessionFileBackup = this._sessionFile.clone();
this._sessionCache = this._sessionFile.clone();
this._sessionFile.append("sessionstore.js");
this._sessionFileBackup.append("sessionstore.bak");
this._sessionCache.append("sessionstoreCache");
this._loadState = STATE_STOPPED;
try {
if (!this._sessionCache.exists() || !this._sessionCache.isDirectory())
this._sessionCache.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
} catch (ex) {
Cu.reportError(ex); // file was write-locked?
}
this._interval = Services.prefs.getIntPref("browser.sessionstore.interval");
this._maxTabsUndo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo");
@ -88,36 +79,6 @@ SessionStore.prototype = {
} catch (ex) { dump(ex + '\n'); } // couldn't remove the file - what now?
}
this._clearCache();
},
_clearCache: function ss_clearCache() {
// First, let's get a list of files we think should be active
let activeFiles = [];
this._forEachBrowserWindow(function(aWindow) {
let tabs = aWindow.BrowserApp.tabs;
for (let i = 0; i < tabs.length; i++) {
let browser = tabs[i].browser;
if (browser.__SS_extdata && "thumbnail" in browser.__SS_extdata)
activeFiles.push(browser.__SS_extdata.thumbnail);
}
});
// Now, let's find the stale files in the cache folder
let staleFiles = [];
let cacheFiles = this._sessionCache.directoryEntries;
while (cacheFiles.hasMoreElements()) {
let file = cacheFiles.getNext().QueryInterface(Ci.nsILocalFile);
let fileURI = Services.io.newFileURI(file);
if (activeFiles.indexOf(fileURI) == -1)
staleFiles.push(file);
}
// Remove the stale files in a separate step to keep the enumerator from
// messing up if we remove the files as we collect them.
staleFiles.forEach(function(aFile) {
aFile.remove(false);
})
},
_sendMessageToJava: function (aMsg) {
@ -928,26 +889,6 @@ SessionStore.prototype = {
setTabValue: function ss_setTabValue(aTab, aKey, aStringValue) {
let browser = aTab.browser;
// Thumbnails are actually stored in the cache, so do the save and update the URI
if (aKey == "thumbnail") {
let file = this._sessionCache.clone();
file.append("thumbnail-" + browser.contentWindowId);
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
let source = Services.io.newURI(aStringValue, "UTF8", null);
let target = Services.io.newFileURI(file)
let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Ci.nsIWebBrowserPersist);
persist.persistFlags = Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES | Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
let privacyContext = browser.contentWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsILoadContext);
persist.saveURI(source, null, null, null, null, file, privacyContext);
aStringValue = target.spec;
}
if (!browser.__SS_extdata)
browser.__SS_extdata = {};
browser.__SS_extdata[aKey] = aStringValue;
@ -978,7 +919,6 @@ SessionStore.prototype = {
}
function notifyObservers(aMessage) {
self._clearCache();
Services.obs.notifyObservers(null, "sessionstore-windows-restored", aMessage || "");
}

View File

@ -3629,6 +3629,10 @@ pref("webgl.prefer-16bpp", false);
pref("webgl.default-no-alpha", false);
pref("webgl.force-layers-readback", false);
// Stagefright prefs
pref("stagefright.force-enabled", false);
pref("stagefright.disabled", false);
#ifdef XP_WIN
// The default TCP send window on Windows is too small, and autotuning only occurs on receive
pref("network.tcp.sendbuffer", 131072);

View File

@ -405,6 +405,11 @@ nsHttpHandler::IsAcceptableEncoding(const char *enc)
if (!PL_strncasecmp(enc, "x-", 2))
enc += 2;
// gzip and deflate are inherently acceptable in modern HTTP - always
// process them if a stream converter can also be found.
if (!PL_strcasecmp(enc, "gzip") || !PL_strcasecmp(enc, "deflate"))
return true;
return nsHttp::FindToken(mAcceptEncodings.get(), enc, HTTP_LWS ",") != nullptr;
}

View File

@ -689,7 +689,9 @@ MarionetteDriverActor.prototype = {
}
let curWindow = this.getCurrentWindow();
let marionette = new Marionette(this, curWindow, "chrome", this.marionetteLog, this.marionettePerf);
let marionette = new Marionette(this, curWindow, "chrome",
this.marionetteLog, this.marionettePerf,
this.scriptTimeout);
let _chromeSandbox = this.createExecuteSandbox(curWindow, marionette, aRequest.args, aRequest.specialPowers);
if (!_chromeSandbox)
return;
@ -799,7 +801,8 @@ MarionetteDriverActor.prototype = {
let original_onerror = curWindow.onerror;
let that = this;
let marionette = new Marionette(this, curWindow, "chrome",
this.marionetteLog, this.marionettePerf);
this.marionetteLog, this.marionettePerf,
this.scriptTimeout);
marionette.command_id = this.command_id;
function chromeAsyncReturnFunc(value, status) {

View File

@ -264,7 +264,9 @@ function createExecuteContentSandbox(aWindow) {
sandbox.__proto__ = sandbox.window;
sandbox.testUtils = utils;
let marionette = new Marionette(this, aWindow, "content", marionetteLogObj, marionettePerf);
let marionette = new Marionette(this, aWindow, "content",
marionetteLogObj, marionettePerf,
marionetteTimeout);
sandbox.marionette = marionette;
marionette.exports.forEach(function(fn) {
try {
@ -445,7 +447,6 @@ function executeWithCallback(msg, timeout) {
asyncTestTimeoutId = curWindow.setTimeout(function() {
sandbox.asyncComplete('timed out', 28);
}, marionetteTimeout);
sandbox.marionette.timeout = marionetteTimeout;
curWindow.addEventListener('error', function win__onerror(evt) {
curWindow.removeEventListener('error', win__onerror, true);

View File

@ -5,14 +5,14 @@
* The Marionette object, passed to the script context.
*/
function Marionette(scope, window, context, logObj, perfData) {
function Marionette(scope, window, context, logObj, perfData, timeout) {
this.scope = scope;
this.window = window;
this.tests = [];
this.logObj = logObj;
this.perfData = perfData;
this.context = context;
this.timeout = 0;
this.timeout = timeout;
this.TEST_UNEXPECTED_FAIL = "TEST-UNEXPECTED-FAIL";
this.TEST_PASS = "TEST-PASS";
this.TEST_KNOWN_FAIL = "TEST-KNOWN-FAIL";
@ -144,14 +144,15 @@ Marionette.prototype = {
callback();
return;
}
timeout = timeout || Date.now();
if (Date.now() - timeout > this.timeout) {
var now = Date.now();
var deadline = now + (typeof(timeout) == "undefined" ? this.timeout : timeout);
if (deadline <= now) {
dump("waitFor timeout: " + test.toString() + "\n");
// the script will timeout here, so no need to raise a separate
// timeout exception
return;
}
this.window.setTimeout(this.waitFor.bind(this), 100, callback, test, timeout);
this.window.setTimeout(this.waitFor.bind(this), 100, callback, test, deadline - now);
},
runEmulatorCmd: function runEmulatorCmd(cmd, callback) {

View File

@ -206,13 +206,11 @@ function _parseModifiers(aEvent)
* a mousedown followed by a mouse up is performed.
*
* aWindow is optional, and defaults to the current window object.
*
* Returns whether the event had preventDefault() called on it.
*/
function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
{
var rect = aTarget.getBoundingClientRect();
return synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
aEvent, aWindow);
}
function synthesizeTouch(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
@ -236,7 +234,6 @@ function synthesizeTouch(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
var defaultPrevented = false;
if (utils) {
var button = aEvent.button || 0;
@ -244,15 +241,13 @@ function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
var modifiers = _parseModifiers(aEvent);
if (("type" in aEvent) && aEvent.type) {
defaultPrevented = utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers);
utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers);
}
else {
utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers);
utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers);
}
}
return defaultPrevented;
}
function synthesizeTouchAtPoint(left, top, aEvent, aWindow)
{

View File

@ -264,7 +264,6 @@ REMOTE_XPCSHELL = \
rm -f ./$@.log && \
$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
-I$(topsrcdir)/build \
-I$(topsrcdir)/build/mobile \
-I$(topsrcdir)/testing/mozbase/mozdevice/mozdevice \
$(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \
--manifest=$(DEPTH)/_tests/xpcshell/xpcshell.ini \
@ -276,6 +275,37 @@ REMOTE_XPCSHELL = \
$(SYMBOLS_PATH) \
$(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
B2G_XPCSHELL = \
rm -f ./@.log && \
$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
-I$(topsrcdir)/build \
$(topsrcdir)/testing/xpcshell/runtestsb2g.py \
--manifest=$(DEPTH)/_tests/xpcshell/xpcshell.ini \
--build-info-json=$(DEPTH)/mozinfo.json \
--no-logfiles \
--use-device-libs \
--no-clean \
--objdir=$(DEPTH) \
$$EXTRA_XPCSHELL_ARGS \
--b2gpath=${B2G_HOME} \
$(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
xpcshell-tests-b2g: ADB_PATH?=$(shell which adb)
xpcshell-tests-b2g:
@if [ "${B2G_HOME}" = "" ]; then \
echo "Please set the B2G_HOME variable"; exit 1; \
elif [ ! -f "${ADB_PATH}" ]; then \
echo "Please set the ADB_PATH variable"; exit 1; \
elif [ "${EMULATOR}" != "" ]; then \
EXTRA_XPCSHELL_ARGS=--emulator=${EMULATOR}; \
$(call B2G_XPCSHELL); \
exit 0; \
else \
EXTRA_XPCSHELL_ARGS=--address=localhost:2828; \
$(call B2G_XPCSHELL); \
exit 0; \
fi
xpcshell-tests-remote: DM_TRANS?=adb
xpcshell-tests-remote:
@if [ "${TEST_DEVICE}" != "" -o "$(DM_TRANS)" = "adb" ]; \

View File

@ -8,7 +8,7 @@ import re, sys, os
import subprocess
import runxpcshelltests as xpcshell
from automationutils import *
import devicemanager, devicemanagerADB, devicemanagerSUT
from mozdevice import devicemanager, devicemanagerADB, devicemanagerSUT
# A specialization of XPCShellTests that runs tests on an Android device
# via devicemanager.

View File

@ -9,7 +9,7 @@ import os
import traceback
from remotexpcshelltests import XPCShellRemote, RemoteXPCShellOptions
from automationutils import *
import devicemanagerADB
from mozdevice import devicemanagerADB
DEVICE_TEST_ROOT = '/data/local/tests'

View File

@ -2,18 +2,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/. */
#ifdef XP_WIN
#define USE_HITTEST
#elifdef MOZ_WIDGET_COCOA
#define USE_HITTEST
#endif
this.EXPORTED_SYMBOLS = [ "WindowDraggingElement" ];
this.WindowDraggingElement = function WindowDraggingElement(elem) {
this._elem = elem;
this._window = elem.ownerDocument.defaultView;
#ifdef USE_HITTEST
#ifdef XP_WIN
if (!this.isPanel())
this._elem.addEventListener("MozMouseHittest", this, false);
else
@ -60,7 +54,7 @@ WindowDraggingElement.prototype = {
},
handleEvent: function(aEvent) {
let isPanel = this.isPanel();
#ifdef USE_HITTEST
#ifdef XP_WIN
if (!isPanel) {
if (this.shouldDrag(aEvent))
aEvent.preventDefault();

View File

@ -52,6 +52,7 @@
<statusbar id="statusbar">
<statusbarpanel>
<label id="statuslabel" value="Status"/>
<label id="statuslabelnodrag" value="No Drag" onmousedown="event.preventDefault()"/>
</statusbarpanel>
</statusbar>
@ -105,18 +106,26 @@ function test_titlebar()
var titlebar = document.getElementById("titlebar");
var label = document.getElementById("label");
// On Mac, the window can also be moved with the statusbar, but this works
// via the MozMouseHittest event, not via mouse events.
// on Mac, the window can also be moved with the statusbar
if (navigator.platform.indexOf("Mac") >= 0) {
var preventDefaulted;
var statuslabel = document.getElementById("statuslabel");
preventDefaulted = synthesizeMouse(statuslabel, 2, 2, { type: "MozMouseHittest" });
SimpleTest.ok(preventDefaulted, "MozMouseHittest should have been defaultPrevented over statusbar");
var statuslabelnodrag = document.getElementById("statuslabelnodrag");
var button = document.getElementById("button");
preventDefaulted = synthesizeMouse(button, 2, 2, { type: "MozMouseHittest" });
SimpleTest.ok(!preventDefaulted, "MozMouseHittest should NOT have been defaultPrevented over button");
origoldx = window.screenX;
origoldy = window.screenY;
synthesizeMouse(statuslabel, 2, 2, { type: "mousedown" });
synthesizeMouse(statuslabel, 22, 22, { type: "mousemove" });
SimpleTest.is(window.screenX, origoldx + 20, "move window with statusbar horizontal");
SimpleTest.is(window.screenY, origoldy + 20, "move window with statusbar vertical");
synthesizeMouse(statuslabel, 22, 22, { type: "mouseup" });
// event was cancelled so the drag should not have occurred
synthesizeMouse(statuslabelnodrag, 2, 2, { type: "mousedown" });
synthesizeMouse(statuslabelnodrag, 22, 22, { type: "mousemove" });
SimpleTest.is(window.screenX, origoldx + 20, "move window with statusbar cancelled mousedown horizontal");
SimpleTest.is(window.screenY, origoldy + 20, "move window with statusbar cancelled mousedown vertical");
synthesizeMouse(statuslabelnodrag, 22, 22, { type: "mouseup" });
}
origoldx = window.screenX;

View File

@ -107,7 +107,6 @@ AndroidBridge::Init(JNIEnv *jEnv,
jEnableLocationHighAccuracy = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableLocationHighAccuracy", "(Z)V");
jEnableSensor = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableSensor", "(I)V");
jDisableSensor = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "disableSensor", "(I)V");
jReturnIMEQueryResult = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "returnIMEQueryResult", "(Ljava/lang/String;II)V");
jScheduleRestart = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "scheduleRestart", "()V");
jNotifyXreExit = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "onXreExit", "()V");
jGetHandlersForMimeType = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getHandlersForMimeType", "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;");
@ -400,25 +399,6 @@ AndroidBridge::DisableSensor(int aSensorType)
env->CallStaticVoidMethod(mGeckoAppShellClass, jDisableSensor, aSensorType);
}
void
AndroidBridge::ReturnIMEQueryResult(const PRUnichar *aResult, uint32_t aLen,
int aSelStart, int aSelLen)
{
ALOG_BRIDGE("AndroidBridge::ReturnIMEQueryResult");
JNIEnv *env = GetJNIEnv();
if (!env)
return;
AutoLocalJNIFrame jniFrame(env);
jvalue args[3];
args[0].l = NewJavaString(&jniFrame, aResult, aLen);
args[1].i = aSelStart;
args[2].i = aSelLen;
env->CallStaticVoidMethodA(mGeckoAppShellClass,
jReturnIMEQueryResult, args);
}
void
AndroidBridge::ScheduleRestart()
{

View File

@ -100,7 +100,7 @@ class AndroidBridge
public:
enum {
NOTIFY_IME_RESETINPUTSTATE = 0,
NOTIFY_IME_SETOPENSTATE = 1,
NOTIFY_IME_REPLY_EVENT = 1,
NOTIFY_IME_CANCELCOMPOSITION = 2,
NOTIFY_IME_FOCUSCHANGE = 3
};
@ -171,8 +171,6 @@ public:
void DisableSensor(int aSensorType);
void ReturnIMEQueryResult(const PRUnichar *aResult, uint32_t aLen, int aSelStart, int aSelLen);
void NotifyXreExit();
void ScheduleRestart();
@ -408,7 +406,6 @@ protected:
jmethodID jEnableLocationHighAccuracy;
jmethodID jEnableSensor;
jmethodID jDisableSensor;
jmethodID jReturnIMEQueryResult;
jmethodID jNotifyAppShellReady;
jmethodID jNotifyXreExit;
jmethodID jScheduleRestart;

View File

@ -33,8 +33,9 @@ jfieldID AndroidGeckoEvent::jDomKeyLocationField = 0;
jfieldID AndroidGeckoEvent::jFlagsField = 0;
jfieldID AndroidGeckoEvent::jUnicodeCharField = 0;
jfieldID AndroidGeckoEvent::jRepeatCountField = 0;
jfieldID AndroidGeckoEvent::jOffsetField = 0;
jfieldID AndroidGeckoEvent::jCountField = 0;
jfieldID AndroidGeckoEvent::jStartField = 0;
jfieldID AndroidGeckoEvent::jEndField = 0;
jfieldID AndroidGeckoEvent::jPointerIndexField = 0;
jfieldID AndroidGeckoEvent::jRangeTypeField = 0;
jfieldID AndroidGeckoEvent::jRangeStylesField = 0;
@ -227,8 +228,9 @@ AndroidGeckoEvent::InitGeckoEventClass(JNIEnv *jEnv)
jFlagsField = getField("mFlags", "I");
jUnicodeCharField = getField("mUnicodeChar", "I");
jRepeatCountField = getField("mRepeatCount", "I");
jOffsetField = getField("mOffset", "I");
jCountField = getField("mCount", "I");
jStartField = getField("mStart", "I");
jEndField = getField("mEnd", "I");
jPointerIndexField = getField("mPointerIndex", "I");
jRangeTypeField = getField("mRangeType", "I");
jRangeStylesField = getField("mRangeStyles", "I");
@ -564,14 +566,13 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
break;
case IME_EVENT:
if (mAction == IME_GET_TEXT || mAction == IME_SET_SELECTION) {
mOffset = jenv->GetIntField(jobj, jOffsetField);
mCount = jenv->GetIntField(jobj, jCountField);
} else if (mAction == IME_SET_TEXT || mAction == IME_ADD_RANGE) {
if (mAction == IME_SET_TEXT)
ReadCharactersField(jenv);
mOffset = jenv->GetIntField(jobj, jOffsetField);
mCount = jenv->GetIntField(jobj, jCountField);
mStart = jenv->GetIntField(jobj, jStartField);
mEnd = jenv->GetIntField(jobj, jEndField);
if (mAction == IME_REPLACE_TEXT) {
ReadCharactersField(jenv);
} else if (mAction == IME_UPDATE_COMPOSITION ||
mAction == IME_ADD_COMPOSITION_RANGE) {
mRangeType = jenv->GetIntField(jobj, jRangeTypeField);
mRangeStyles = jenv->GetIntField(jobj, jRangeStylesField);
mRangeForeColor =

View File

@ -676,8 +676,9 @@ public:
int Flags() { return mFlags; }
int UnicodeChar() { return mUnicodeChar; }
int RepeatCount() const { return mRepeatCount; }
int Offset() { return mOffset; }
int Count() { return mCount; }
int Start() { return mStart; }
int End() { return mEnd; }
int PointerIndex() { return mPointerIndex; }
int RangeType() { return mRangeType; }
int RangeStyles() { return mRangeStyles; }
@ -703,7 +704,8 @@ protected:
int mDomKeyLocation;
int mKeyCode, mUnicodeChar;
int mRepeatCount;
int mOffset, mCount;
int mCount;
int mStart, mEnd;
int mRangeType, mRangeStyles;
int mRangeForeColor, mRangeBackColor;
double mX, mY, mZ;
@ -753,8 +755,9 @@ protected:
static jfieldID jMetaStateField;
static jfieldID jDomKeyLocationField;
static jfieldID jFlagsField;
static jfieldID jOffsetField;
static jfieldID jCountField;
static jfieldID jStartField;
static jfieldID jEndField;
static jfieldID jPointerIndexField;
static jfieldID jUnicodeCharField;
static jfieldID jRepeatCountField;
@ -807,14 +810,12 @@ public:
};
enum {
IME_COMPOSITION_END = 0,
IME_COMPOSITION_BEGIN = 1,
IME_SET_TEXT = 2,
IME_GET_TEXT = 3,
IME_DELETE_TEXT = 4,
IME_SET_SELECTION = 5,
IME_GET_SELECTION = 6,
IME_ADD_RANGE = 7
IME_SYNCHRONIZE = 0,
IME_REPLACE_TEXT = 1,
IME_SET_SELECTION = 2,
IME_ADD_COMPOSITION_RANGE = 3,
IME_UPDATE_COMPOSITION = 4,
IME_REMOVE_COMPOSITION = 5
};
};

View File

@ -122,27 +122,26 @@ GfxInfo::EnsureInitializedFromGfxInfoData()
// as it's useful information that isn't given anywhere else in about:support of in crash reports.
// But we should really move this out of GfxInfo.
if (mozilla::AndroidBridge::Bridge()) {
nsAutoString str;
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "MODEL", str)) {
mAdapterDescription.AppendPrintf(" -- Model: %s", NS_LossyConvertUTF16toASCII(str).get());
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "MODEL", mModel)) {
mAdapterDescription.AppendPrintf(" -- Model: %s", NS_LossyConvertUTF16toASCII(mModel).get());
}
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "PRODUCT", str)) {
mAdapterDescription.AppendPrintf(", Product: %s", NS_LossyConvertUTF16toASCII(str).get());
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "PRODUCT", mProduct)) {
mAdapterDescription.AppendPrintf(", Product: %s", NS_LossyConvertUTF16toASCII(mProduct).get());
}
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "MANUFACTURER", str)) {
mAdapterDescription.AppendPrintf(", Manufacturer: %s", NS_LossyConvertUTF16toASCII(str).get());
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "MANUFACTURER", mManufacturer)) {
mAdapterDescription.AppendPrintf(", Manufacturer: %s", NS_LossyConvertUTF16toASCII(mManufacturer).get());
}
int32_t version; // the HARDWARE field isn't available on Android SDK < 8
if (!mozilla::AndroidBridge::Bridge()->GetStaticIntField("android/os/Build$VERSION", "SDK_INT", &version))
version = 0;
int32_t signedVersion;
if (!mozilla::AndroidBridge::Bridge()->GetStaticIntField("android/os/Build$VERSION", "SDK_INT", &signedVersion))
signedVersion = 0;
mOSVersion = signedVersion;
if (version >= 8 && mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "HARDWARE", str)) {
if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "HARDWARE", str)) {
mAdapterDescription.AppendPrintf(", Hardware: %s", NS_LossyConvertUTF16toASCII(str).get());
}
// the HARDWARE field isn't available on Android SDK < 8
if (mOSVersion >= 8 && mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "HARDWARE", mHardware)) {
mAdapterDescription.AppendPrintf(", Hardware: %s", NS_LossyConvertUTF16toASCII(mHardware).get());
}
}
@ -309,7 +308,7 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
NS_ENSURE_ARG_POINTER(aStatus);
aSuggestedDriverVersion.SetIsVoid(true);
*aStatus = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
OperatingSystem os = DRIVER_OS_ANDROID;
OperatingSystem os = mOS;
if (aOS)
*aOS = os;
@ -330,6 +329,27 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature,
return NS_OK;
}
}
if (aFeature == FEATURE_STAGEFRIGHT) {
NS_LossyConvertUTF16toASCII cManufacturer(mManufacturer);
NS_LossyConvertUTF16toASCII cModel(mModel);
if (mOSVersion < 14 /* Before version 4.0 */ )
{
*aStatus = nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION;
return NS_OK;
}
else if (mOSVersion < 16 /* Before version 4.1 */ )
{
bool isWhitelisted =
cManufacturer.Equals("samsung", nsCaseInsensitiveCStringComparator()) ||
cModel.Equals("galaxy nexus", nsCaseInsensitiveCStringComparator()); // some Galaxy Nexus have manufacturer=amazon
if (!isWhitelisted) {
*aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
return NS_OK;
}
}
}
}
return GfxInfoBase::GetFeatureStatusImpl(aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, &os);
@ -366,7 +386,29 @@ NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString & aDriverVersion)
/* void spoofOSVersion (in unsigned long aVersion); */
NS_IMETHODIMP GfxInfo::SpoofOSVersion(uint32_t aVersion)
{
EnsureInitializedFromGfxInfoData(); // initialization from GfxInfo data overwrites mOSVersion
mOSVersion = aVersion;
return NS_OK;
}
#endif
const nsAString& GfxInfo::Model() const
{
return mModel;
}
const nsAString& GfxInfo::Hardware() const
{
return mHardware;
}
const nsAString& GfxInfo::Product() const
{
return mProduct;
}
const nsAString& GfxInfo::Manufacturer() const
{
return mManufacturer;
}

View File

@ -48,11 +48,18 @@ public:
void EnsureInitializedFromGfxInfoData();
virtual const nsAString& Model() const;
virtual const nsAString& Hardware() const;
virtual const nsAString& Product() const;
virtual const nsAString& Manufacturer() const;
#ifdef DEBUG
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIGFXINFODEBUG
#endif
virtual uint32_t OperatingSystemVersion() const { return mOSVersion; }
protected:
virtual nsresult GetFeatureStatusImpl(int32_t aFeature,
@ -76,6 +83,11 @@ private:
nsCString mError;
nsCString mAdapterDescription;
OperatingSystem mOS;
uint32_t mOSVersion;
nsString mModel, mHardware, mManufacturer, mProduct;
};
} // namespace widget

View File

@ -161,7 +161,9 @@ nsWindow::nsWindow() :
mIsVisible(false),
mParent(nullptr),
mFocus(nullptr),
mIMEComposing(false)
mIMEComposing(false),
mIMEMaskSelectionUpdate(false),
mIMEMaskTextUpdate(false)
{
}
@ -639,20 +641,18 @@ nsWindow::DispatchEvent(nsGUIEvent *aEvent)
switch (aEvent->message) {
case NS_COMPOSITION_START:
MOZ_ASSERT(!mIMEComposing);
mIMEComposing = true;
break;
case NS_COMPOSITION_END:
MOZ_ASSERT(mIMEComposing);
mIMEComposing = false;
mIMEComposingText.Truncate();
break;
case NS_TEXT_TEXT:
MOZ_ASSERT(mIMEComposing);
mIMEComposingText = static_cast<nsTextEvent*>(aEvent)->theText;
break;
case NS_KEY_PRESS:
// Sometimes the text changes after a key press do not generate notifications (see Bug 723810)
// Call the corresponding methods explicitly to send those changes back to Java
OnIMETextChange(0, 0, 0);
OnIMESelectionChange();
break;
}
return status;
}
@ -1751,6 +1751,7 @@ void
nsWindow::OnKeyEvent(AndroidGeckoEvent *ae)
{
nsRefPtr<nsWindow> kungFuDeathGrip(this);
RemoveIMEComposition();
uint32_t msg;
switch (ae->Action()) {
case AndroidKeyEvent::ACTION_DOWN:
@ -1816,89 +1817,216 @@ nsWindow::OnKeyEvent(AndroidGeckoEvent *ae)
#define ALOGIME(args...)
#endif
void
nsWindow::OnIMEAddRange(AndroidGeckoEvent *ae)
static nscolor
ConvertAndroidColor(uint32_t c)
{
//ALOGIME("IME: IME_ADD_RANGE");
nsTextRange range;
range.mStartOffset = ae->Offset();
range.mEndOffset = range.mStartOffset + ae->Count();
range.mRangeType = ae->RangeType();
range.mRangeStyle.mDefinedStyles = ae->RangeStyles();
range.mRangeStyle.mLineStyle = nsTextRangeStyle::LINESTYLE_SOLID;
range.mRangeStyle.mForegroundColor = NS_RGBA(
((ae->RangeForeColor() >> 16) & 0xff),
((ae->RangeForeColor() >> 8) & 0xff),
(ae->RangeForeColor() & 0xff),
((ae->RangeForeColor() >> 24) & 0xff));
range.mRangeStyle.mBackgroundColor = NS_RGBA(
((ae->RangeBackColor() >> 16) & 0xff),
((ae->RangeBackColor() >> 8) & 0xff),
(ae->RangeBackColor() & 0xff),
((ae->RangeBackColor() >> 24) & 0xff));
mIMERanges.AppendElement(range);
return;
return NS_RGBA((c & 0x000000ff),
(c & 0x0000ff00) >> 8,
(c & 0x00ff0000) >> 16,
(c & 0xff000000) >> 24);
}
class AutoIMEMask {
private:
bool mOldMask, *mMask;
public:
AutoIMEMask(bool &mask) : mOldMask(mask), mMask(&mask) {
mask = true;
}
~AutoIMEMask() {
*mMask = mOldMask;
}
};
/*
Remove the composition but leave the text content as-is
*/
void
nsWindow::RemoveIMEComposition()
{
// Remove composition on Gecko side
if (!mIMEComposing)
return;
nsRefPtr<nsWindow> kungFuDeathGrip(this);
AutoIMEMask selMask(mIMEMaskSelectionUpdate);
AutoIMEMask textMask(mIMEMaskTextUpdate);
nsTextEvent textEvent(true, NS_TEXT_TEXT, this);
InitEvent(textEvent, nullptr);
textEvent.theText = mIMEComposingText;
DispatchEvent(&textEvent);
nsCompositionEvent event(true, NS_COMPOSITION_END, this);
InitEvent(event, nullptr);
DispatchEvent(&event);
}
void
nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
{
MOZ_ASSERT(!mIMEMaskTextUpdate);
MOZ_ASSERT(!mIMEMaskSelectionUpdate);
/*
Rules for managing IME between Gecko and Java:
* Gecko controls the text content, and Java shadows the Gecko text
through text updates
* Java controls the selection, and Gecko shadows the Java selection
through set selection events
* Java controls the composition, and Gecko shadows the Java
composition through update composition events
*/
nsRefPtr<nsWindow> kungFuDeathGrip(this);
switch (ae->Action()) {
case AndroidGeckoEvent::IME_COMPOSITION_END:
case AndroidGeckoEvent::IME_SYNCHRONIZE:
{
ALOGIME("IME: IME_COMPOSITION_END");
MOZ_ASSERT(mIMEComposing,
"IME_COMPOSITION_END when we are not composing?!");
nsCompositionEvent event(true, NS_COMPOSITION_END, this);
InitEvent(event, nullptr);
event.data = mIMELastDispatchedComposingText;
mIMELastDispatchedComposingText.Truncate();
DispatchEvent(&event);
AndroidBridge::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT, 0);
}
return;
case AndroidGeckoEvent::IME_COMPOSITION_BEGIN:
break;
case AndroidGeckoEvent::IME_REPLACE_TEXT:
{
ALOGIME("IME: IME_COMPOSITION_BEGIN");
MOZ_ASSERT(!mIMEComposing,
"IME_COMPOSITION_BEGIN when we are already composing?!");
/*
Replace text in Gecko thread from ae->Start() to ae->End()
with the string ae->Characters()
mIMELastDispatchedComposingText.Truncate();
nsCompositionEvent event(true, NS_COMPOSITION_START, this);
InitEvent(event, nullptr);
DispatchEvent(&event);
}
return;
case AndroidGeckoEvent::IME_ADD_RANGE:
{
NS_ASSERTION(mIMEComposing,
"IME_ADD_RANGE when we are not composing?!");
OnIMEAddRange(ae);
}
return;
case AndroidGeckoEvent::IME_SET_TEXT:
{
NS_ASSERTION(mIMEComposing,
"IME_SET_TEXT when we are not composing?!");
Selection updates are masked so the result of our temporary
selection event is not passed on to Java
OnIMEAddRange(ae);
Text updates are passed on, so the Java text can shadow the
Gecko text
*/
AutoIMEMask selMask(mIMEMaskSelectionUpdate);
RemoveIMEComposition();
{
nsSelectionEvent event(true, NS_SELECTION_SET, this);
InitEvent(event, nullptr);
event.mOffset = uint32_t(ae->Start());
event.mLength = uint32_t(ae->End() - ae->Start());
event.mExpandToClusterBoundary = false;
DispatchEvent(&event);
}
{
nsCompositionEvent event(true, NS_COMPOSITION_START, this);
InitEvent(event, nullptr);
DispatchEvent(&event);
}
{
nsTextEvent event(true, NS_TEXT_TEXT, this);
InitEvent(event, nullptr);
event.theText = ae->Characters();
DispatchEvent(&event);
}
{
nsCompositionEvent event(true, NS_COMPOSITION_END, this);
InitEvent(event, nullptr);
event.data = ae->Characters();
DispatchEvent(&event);
}
AndroidBridge::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT, 0);
}
break;
case AndroidGeckoEvent::IME_SET_SELECTION:
{
/*
Set Gecko selection to ae->Start() to ae->End()
Selection updates are masked to prevent Java from being
notified of the new selection
*/
AutoIMEMask selMask(mIMEMaskSelectionUpdate);
nsSelectionEvent selEvent(true, NS_SELECTION_SET, this);
InitEvent(selEvent, nullptr);
int32_t start = ae->Start(), end = ae->End();
if (start < 0 || end < 0) {
nsQueryContentEvent event(true, NS_QUERY_SELECTED_TEXT, this);
InitEvent(event, nullptr);
DispatchEvent(&event);
MOZ_ASSERT(event.mSucceeded && !event.mWasAsync);
if (start < 0)
start = int32_t(event.GetSelectionStart());
if (end < 0)
end = int32_t(event.GetSelectionEnd());
}
selEvent.mOffset = std::min(start, end);
selEvent.mLength = std::max(start, end) - selEvent.mOffset;
selEvent.mReversed = start > end;
selEvent.mExpandToClusterBoundary = false;
DispatchEvent(&selEvent);
}
break;
case AndroidGeckoEvent::IME_ADD_COMPOSITION_RANGE:
{
nsTextRange range;
range.mStartOffset = ae->Start();
range.mEndOffset = ae->End();
range.mRangeType = ae->RangeType();
range.mRangeStyle.mDefinedStyles = ae->RangeStyles();
range.mRangeStyle.mLineStyle = nsTextRangeStyle::LINESTYLE_SOLID;
range.mRangeStyle.mForegroundColor =
ConvertAndroidColor(uint32_t(ae->RangeForeColor()));
range.mRangeStyle.mBackgroundColor =
ConvertAndroidColor(uint32_t(ae->RangeBackColor()));
mIMERanges.AppendElement(range);
}
break;
case AndroidGeckoEvent::IME_UPDATE_COMPOSITION:
{
/*
Update the composition from ae->Start() to ae->End() using
information from added ranges. This is only used for
visual indication and does not affect the text content.
Only the offsets are specified and not the text content
to eliminate the possibility of this event altering the
text content unintentionally.
Selection and text updates are masked so the result of
temporary events are not passed on to Java
*/
AutoIMEMask selMask(mIMEMaskSelectionUpdate);
AutoIMEMask textMask(mIMEMaskTextUpdate);
RemoveIMEComposition();
nsTextEvent event(true, NS_TEXT_TEXT, this);
InitEvent(event, nullptr);
event.theText.Assign(ae->Characters());
event.rangeArray = mIMERanges.Elements();
event.rangeCount = mIMERanges.Length();
{
nsSelectionEvent event(true, NS_SELECTION_SET, this);
InitEvent(event, nullptr);
event.mOffset = uint32_t(ae->Start());
event.mLength = uint32_t(ae->End() - ae->Start());
event.mExpandToClusterBoundary = false;
DispatchEvent(&event);
}
{
nsQueryContentEvent queryEvent(true,
NS_QUERY_SELECTED_TEXT, this);
InitEvent(queryEvent, nullptr);
DispatchEvent(&queryEvent);
MOZ_ASSERT(queryEvent.mSucceeded && !queryEvent.mWasAsync);
event.theText = queryEvent.mReply.mString;
}
{
nsCompositionEvent event(true, NS_COMPOSITION_START, this);
InitEvent(event, nullptr);
DispatchEvent(&event);
}
if (mIMEComposing &&
event.theText != mIMELastDispatchedComposingText) {
event.theText != mIMEComposingText) {
nsCompositionEvent compositionUpdate(true,
NS_COMPOSITION_UPDATE,
this);
InitEvent(compositionUpdate, nullptr);
compositionUpdate.data = event.theText;
mIMELastDispatchedComposingText = event.theText;
DispatchEvent(&compositionUpdate);
if (Destroyed())
return;
@ -1914,83 +2042,13 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
DispatchEvent(&event);
mIMERanges.Clear();
}
return;
case AndroidGeckoEvent::IME_GET_TEXT:
break;
case AndroidGeckoEvent::IME_REMOVE_COMPOSITION:
{
ALOGIME("IME: IME_GET_TEXT: o=%u, l=%u", ae->Offset(), ae->Count());
nsQueryContentEvent event(true, NS_QUERY_TEXT_CONTENT, this);
InitEvent(event, nullptr);
event.InitForQueryTextContent(ae->Offset(), ae->Count());
DispatchEvent(&event);
if (!event.mSucceeded) {
ALOGIME("IME: -> failed");
AndroidBridge::Bridge()->ReturnIMEQueryResult(
nullptr, 0, 0, 0);
return;
} else if (!event.mWasAsync) {
AndroidBridge::Bridge()->ReturnIMEQueryResult(
event.mReply.mString.get(),
event.mReply.mString.Length(), 0, 0);
}
RemoveIMEComposition();
mIMERanges.Clear();
}
return;
case AndroidGeckoEvent::IME_DELETE_TEXT:
{
ALOGIME("IME: IME_DELETE_TEXT");
NS_ASSERTION(mIMEComposing,
"IME_DELETE_TEXT when we are not composing?!");
nsKeyEvent event(true, NS_KEY_PRESS, this);
ANPEvent pluginEvent;
InitKeyEvent(event, *ae, &pluginEvent);
event.keyCode = NS_VK_BACK;
DispatchEvent(&event);
}
return;
case AndroidGeckoEvent::IME_SET_SELECTION:
{
ALOGIME("IME: IME_SET_SELECTION: o=%u, l=%d", ae->Offset(), ae->Count());
nsSelectionEvent selEvent(true, NS_SELECTION_SET, this);
InitEvent(selEvent, nullptr);
selEvent.mOffset = uint32_t(ae->Count() >= 0 ?
ae->Offset() :
ae->Offset() + ae->Count());
selEvent.mLength = uint32_t(NS_ABS(ae->Count()));
selEvent.mReversed = ae->Count() >= 0 ? false : true;
selEvent.mExpandToClusterBoundary = false;
DispatchEvent(&selEvent);
}
return;
case AndroidGeckoEvent::IME_GET_SELECTION:
{
ALOGIME("IME: IME_GET_SELECTION");
nsQueryContentEvent event(true, NS_QUERY_SELECTED_TEXT, this);
InitEvent(event, nullptr);
DispatchEvent(&event);
if (!event.mSucceeded) {
ALOGIME("IME: -> failed");
AndroidBridge::Bridge()->ReturnIMEQueryResult(
nullptr, 0, 0, 0);
return;
} else if (!event.mWasAsync) {
AndroidBridge::Bridge()->ReturnIMEQueryResult(
event.mReply.mString.get(),
event.mReply.mString.Length(),
event.GetSelectionStart(),
event.GetSelectionEnd() - event.GetSelectionStart());
}
//ALOGIME("IME: -> o=%u, l=%u", event.mReply.mOffset, event.mReply.mString.Length());
}
return;
break;
}
}
@ -2027,28 +2085,8 @@ NS_IMETHODIMP
nsWindow::ResetInputState()
{
//ALOGIME("IME: ResetInputState: s=%d", aState);
// Cancel composition on Gecko side
if (mIMEComposing) {
nsRefPtr<nsWindow> kungFuDeathGrip(this);
nsTextEvent textEvent(true, NS_TEXT_TEXT, this);
InitEvent(textEvent, nullptr);
textEvent.theText = mIMEComposingText;
DispatchEvent(&textEvent);
mIMEComposingText.Truncate(0);
nsCompositionEvent event(true, NS_COMPOSITION_END, this);
InitEvent(event, nullptr);
DispatchEvent(&event);
}
RemoveIMEComposition();
AndroidBridge::NotifyIME(AndroidBridge::NOTIFY_IME_RESETINPUTSTATE, 0);
// Send IME text/selection change notifications
OnIMETextChange(0, 0, 0);
OnIMESelectionChange();
return NS_OK;
}
@ -2108,7 +2146,6 @@ nsWindow::CancelIMEComposition()
nsTextEvent textEvent(true, NS_TEXT_TEXT, this);
InitEvent(textEvent, nullptr);
DispatchEvent(&textEvent);
mIMEComposingText.Truncate(0);
nsCompositionEvent compEvent(true, NS_COMPOSITION_END, this);
InitEvent(compEvent, nullptr);
@ -2128,7 +2165,7 @@ nsWindow::OnIMEFocusChange(bool aFocus)
int(aFocus));
if (aFocus) {
OnIMETextChange(0, 0, 0);
OnIMETextChange(0, INT32_MAX, INT32_MAX);
OnIMESelectionChange();
}
@ -2138,22 +2175,16 @@ nsWindow::OnIMEFocusChange(bool aFocus)
NS_IMETHODIMP
nsWindow::OnIMETextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd)
{
if (mIMEMaskTextUpdate)
return NS_OK;
ALOGIME("IME: OnIMETextChange: s=%d, oe=%d, ne=%d",
aStart, aOldEnd, aNewEnd);
if (!mInputContext.mIMEState.mEnabled) {
AndroidBridge::NotifyIMEChange(nullptr, 0, 0, 0, 0);
return NS_OK;
}
// A quirk in Android makes it necessary to pass the whole text.
// The more efficient way would have been passing the substring from index
// aStart to index aNewEnd
nsRefPtr<nsWindow> kungFuDeathGrip(this);
nsQueryContentEvent event(true, NS_QUERY_TEXT_CONTENT, this);
InitEvent(event, nullptr);
event.InitForQueryTextContent(0, UINT32_MAX);
event.InitForQueryTextContent(aStart, aNewEnd - aStart);
DispatchEvent(&event);
if (!event.mSucceeded)
@ -2163,18 +2194,18 @@ nsWindow::OnIMETextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd)
event.mReply.mString.Length(),
aStart, aOldEnd, aNewEnd);
/* Make sure Java's selection is up-to-date */
OnIMESelectionChange();
return NS_OK;
}
NS_IMETHODIMP
nsWindow::OnIMESelectionChange(void)
{
ALOGIME("IME: OnIMESelectionChange");
if (!mInputContext.mIMEState.mEnabled) {
AndroidBridge::NotifyIMEChange(nullptr, 0, 0, 0, -1);
if (mIMEMaskSelectionUpdate)
return NS_OK;
}
ALOGIME("IME: OnIMESelectionChange");
nsRefPtr<nsWindow> kungFuDeathGrip(this);
nsQueryContentEvent event(true, NS_QUERY_SELECTED_TEXT, this);
@ -2184,9 +2215,8 @@ nsWindow::OnIMESelectionChange(void)
if (!event.mSucceeded)
return NS_OK;
AndroidBridge::NotifyIMEChange(nullptr, 0, int(event.mReply.mOffset),
int(event.mReply.mOffset +
event.mReply.mString.Length()), -1);
AndroidBridge::NotifyIMEChange(nullptr, 0, int(event.GetSelectionStart()),
int(event.GetSelectionEnd()), -1);
return NS_OK;
}

View File

@ -166,7 +166,7 @@ protected:
bool DrawTo(gfxASurface *targetSurface);
bool DrawTo(gfxASurface *targetSurface, const nsIntRect &aRect);
bool IsTopLevel();
void OnIMEAddRange(mozilla::AndroidGeckoEvent *ae);
void RemoveIMEComposition();
// Call this function when the users activity is the direct cause of an
// event (like a keypress or mouse click).
@ -183,8 +183,8 @@ protected:
nsCOMPtr<nsIIdleServiceInternal> mIdleService;
bool mIMEComposing;
bool mIMEMaskSelectionUpdate, mIMEMaskTextUpdate;
nsString mIMEComposingText;
nsString mIMELastDispatchedComposingText;
nsAutoTArray<nsTextRange, 4> mIMERanges;
InputContext mInputContext;

View File

@ -53,6 +53,8 @@ public:
NS_DECL_NSIGFXINFODEBUG
#endif
virtual uint32_t OperatingSystemVersion() const { return mOSXVersion; }
protected:
virtual nsresult GetFeatureStatusImpl(int32_t aFeature,

View File

@ -183,10 +183,6 @@ typedef NSInteger NSEventGestureAxis;
- (NSEventPhase)momentumPhase;
@end
@protocol EventRedirection
- (NSView*)targetView;
@end
@interface ChildView : NSView<
#ifdef ACCESSIBILITY
mozAccessible,
@ -271,8 +267,6 @@ typedef NSInteger NSEventGestureAxis;
// class initialization
+ (void)initialize;
+ (void)registerViewForDraggedTypes:(NSView*)aView;
// these are sent to the first responder when the window key status changes
- (void)viewsWindowDidBecomeKey;
- (void)viewsWindowDidResignKey;
@ -282,8 +276,6 @@ typedef NSInteger NSEventGestureAxis;
- (void)sendFocusEvent:(uint32_t)eventType;
- (void)updateWindowDraggableStateOnMouseMove:(NSEvent*)theEvent;
- (void)handleMouseMoved:(NSEvent*)aEvent;
- (void)drawRect:(NSRect)aRect inTitlebarContext:(CGContextRef)aContext;

View File

@ -1912,20 +1912,6 @@ NSEvent* gLastDragMouseDownEvent = nil;
}
}
+ (void)registerViewForDraggedTypes:(NSView*)aView
{
[aView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
NSStringPboardType,
NSHTMLPboardType,
NSURLPboardType,
NSFilesPromisePboardType,
kWildcardPboardType,
kCorePboardType_url,
kCorePboardType_urld,
kCorePboardType_urln,
nil]];
}
// initWithFrame:geckoChild:
- (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild
{
@ -1973,8 +1959,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
}
// register for things we'll take from other applications
[ChildView registerViewForDraggedTypes:self];
PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView initWithFrame: registering drag types\n"));
[self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
NSStringPboardType,
NSHTMLPboardType,
NSURLPboardType,
NSFilesPromisePboardType,
kWildcardPboardType,
kCorePboardType_url,
kCorePboardType_urld,
kCorePboardType_urln,
nil]];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowBecameMain:)
name:NSWindowDidBecomeMainNotification
@ -2344,7 +2339,7 @@ NSEvent* gLastDragMouseDownEvent = nil;
- (BOOL)mouseDownCanMoveWindow
{
return [[self window] isMovableByWindowBackground];
return NO;
}
- (void)lockFocus
@ -3290,25 +3285,6 @@ NSEvent* gLastDragMouseDownEvent = nil;
mGeckoChild->DispatchEvent(&event, status);
}
- (void)updateWindowDraggableStateOnMouseMove:(NSEvent*)theEvent
{
if (!theEvent || !mGeckoChild) {
return;
}
nsCocoaWindow* windowWidget = mGeckoChild->GetXULWindowWidget();
if (!windowWidget) {
return;
}
// We assume later on that sending a hit test event won't cause widget destruction.
nsMouseEvent hitTestEvent(true, NS_MOUSE_MOZHITTEST, mGeckoChild, nsMouseEvent::eReal);
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&hitTestEvent];
bool result = mGeckoChild->DispatchWindowEvent(hitTestEvent);
[windowWidget->GetCocoaWindow() setMovableByWindowBackground:result];
}
- (void)handleMouseMoved:(NSEvent*)theEvent
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
@ -4833,11 +4809,6 @@ ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent)
NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, window);
NSView* view = [[[window contentView] superview] hitTest:windowEventLocation];
while([view conformsToProtocol:@protocol(EventRedirection)]) {
view = [(id<EventRedirection>)view targetView];
}
if (![view isKindOfClass:[ChildView class]])
return nil;
@ -4936,7 +4907,7 @@ ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent,
NSWindow *ourWindow = [self window];
NSView *contentView = [ourWindow contentView];
if ([ourWindow isKindOfClass:[ToolbarWindow class]] && (self == contentView))
return [ourWindow isMovableByWindowBackground];
return NO;
return [self nsChildView_NSView_mouseDownCanMoveWindow];
}

View File

@ -18,7 +18,6 @@
class nsCocoaWindow;
class nsChildView;
class nsMenuBarX;
@class ChildView;
// Value copied from BITMAP_MAX_AREA, used in nsNativeThemeCocoa.mm
#define CUIDRAW_MAX_AREA 500000
@ -177,7 +176,6 @@ typedef struct _nsCocoaWindowList {
TitlebarAndBackgroundColor *mColor;
float mUnifiedToolbarHeight;
NSColor *mBackgroundColor;
NSView *mTitlebarView; // strong
}
// Pass nil here to get the default appearance.
- (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive;
@ -188,7 +186,6 @@ typedef struct _nsCocoaWindowList {
- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect sync:(BOOL)aSync;
- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect;
- (void)setDrawsContentsIntoWindowFrame:(BOOL)aState;
- (ChildView*)mainChildView;
@end
class nsCocoaWindow : public nsBaseWidget, public nsPIWidgetCocoa

View File

@ -447,10 +447,6 @@ nsresult nsCocoaWindow::CreateNativeWindow(const NSRect &aRect,
[mWindow setContentMinSize:NSMakeSize(60, 60)];
[mWindow disableCursorRects];
// Make sure the window starts out not draggable by the background.
// We will turn it on as necessary.
[mWindow setMovableByWindowBackground:NO];
[[WindowDataMap sharedWindowDataMap] ensureDataForWindow:mWindow];
mWindowMadeHere = true;
@ -2587,94 +2583,7 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
@end
@interface TitlebarMouseHandlingView : NSView<EventRedirection>
{
ToolbarWindow* mWindow; // weak
BOOL mProcessingRightMouseDown;
}
- (id)initWithWindow:(ToolbarWindow*)aWindow;
@end
@implementation TitlebarMouseHandlingView
- (id)initWithWindow:(ToolbarWindow*)aWindow
{
if ((self = [super initWithFrame:[aWindow titlebarRect]])) {
mWindow = aWindow;
[self setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
[ChildView registerViewForDraggedTypes:self];
mProcessingRightMouseDown = NO;
}
return self;
}
- (NSView*)targetView
{
return [mWindow mainChildView];
}
- (BOOL)mouseDownCanMoveWindow
{
return [mWindow isMovableByWindowBackground];
}
// We redirect many types of events to the window's mainChildView simply by
// passing the event object to the respective handler method. We don't need any
// coordinate transformations because event coordinates are relative to the
// window.
// We only need to handle event types whose target NSView is determined by the
// event's position. We don't need to handle key events and NSMouseMoved events
// because those are only sent to the window's first responder. This view
// doesn't override acceptsFirstResponder, so it will never receive those kinds
// of events.
- (void)mouseMoved:(NSEvent*)aEvent { [[self targetView] mouseMoved:aEvent]; }
- (void)mouseDown:(NSEvent*)aEvent { [[self targetView] mouseDown:aEvent]; }
- (void)mouseUp:(NSEvent*)aEvent { [[self targetView] mouseUp:aEvent]; }
- (void)mouseDragged:(NSEvent*)aEvent { [[self targetView] mouseDragged:aEvent]; }
- (void)rightMouseDown:(NSEvent*)aEvent
{
// To avoid recursion...
if (mProcessingRightMouseDown)
return;
mProcessingRightMouseDown = YES;
[[self targetView] rightMouseDown:aEvent];
mProcessingRightMouseDown = NO;
}
- (void)rightMouseUp:(NSEvent*)aEvent { [[self targetView] rightMouseUp:aEvent]; }
- (void)rightMouseDragged:(NSEvent*)aEvent { [[self targetView] rightMouseDragged:aEvent]; }
- (void)otherMouseDown:(NSEvent*)aEvent { [[self targetView] otherMouseDown:aEvent]; }
- (void)otherMouseUp:(NSEvent*)aEvent { [[self targetView] otherMouseUp:aEvent]; }
- (void)otherMouseDragged:(NSEvent*)aEvent { [[self targetView] otherMouseDragged:aEvent]; }
- (void)scrollWheel:(NSEvent*)aEvent { [[self targetView] scrollWheel:aEvent]; }
- (void)swipeWithEvent:(NSEvent*)aEvent { [[self targetView] swipeWithEvent:aEvent]; }
- (void)beginGestureWithEvent:(NSEvent*)aEvent { [[self targetView] beginGestureWithEvent:aEvent]; }
- (void)magnifyWithEvent:(NSEvent*)aEvent { [[self targetView] magnifyWithEvent:aEvent]; }
- (void)rotateWithEvent:(NSEvent*)aEvent { [[self targetView] rotateWithEvent:aEvent]; }
- (void)endGestureWithEvent:(NSEvent*)aEvent { [[self targetView] endGestureWithEvent:aEvent]; }
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
{ return [[self targetView] draggingEntered:sender]; }
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
{ return [[self targetView] draggingUpdated:sender]; }
- (void)draggingExited:(id <NSDraggingInfo>)sender
{ [[self targetView] draggingExited:sender]; }
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
{ return [[self targetView] performDragOperation:sender]; }
- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
{ [[self targetView] draggedImage:anImage endedAt:aPoint operation:operation]; }
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
{ return [[self targetView] draggingSourceOperationMaskForLocal:isLocal]; }
- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDestination
{ return [[self targetView] namesOfPromisedFilesDroppedAtDestination:dropDestination]; }
- (NSMenu*)menuForEvent:(NSEvent*)aEvent
{ return [[self targetView] menuForEvent:aEvent]; }
@end
// This class allows us to exercise control over the window's title bar. This
// allows for a "unified toolbar" look, and for extending the content area into
// the title bar. It works like this:
// This class allows us to have a "unified toolbar" style window. It works like this:
// 1) We set the window's style to textured.
// 2) Because of this, the background color applies to the entire window, including
// the titlebar area. For normal textured windows, the default pattern is a
@ -2708,11 +2617,6 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
// to the containing window - the other direction doesn't work. That's why the
// toolbar height is cached in the ToolbarWindow but nsNativeThemeCocoa can simply
// query the window for its titlebar height when drawing the toolbar.
@interface ToolbarWindow(Private)
- (void)installTitlebarMouseHandlingView;
- (void)uninstallTitlebarMouseHandlingView;
@end;
@implementation ToolbarWindow
- (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag
@ -2747,7 +2651,6 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
[mColor release];
[mBackgroundColor release];
[mTitlebarView release];
[super dealloc];
NS_OBJC_END_TRY_ABORT_BLOCK;
@ -2827,48 +2730,11 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
[self setTitlebarNeedsDisplayInRect:[self titlebarRect] sync:needSyncRedraw];
}
// Extending the content area into the title bar works by redirection of both
// drawing and mouse events.
// The window's NSView hierarchy looks like this:
// - border view ([[window contentView] superview])
// - transparent title bar event redirection view
// - window controls (traffic light buttons)
// - content view ([window contentView], default NSView provided by the window)
// - our main Gecko ChildView ([window mainChildView]), which has an
// OpenGL context attached to it when accelerated
// - possibly more ChildViews for plugins
//
// When the window is in title bar extension mode, the mainChildView covers the
// whole window but is only visible in the content area of the window, because
// it's a subview of the window's contentView and thus clipped to its dimensions.
// This clipping is a good thing because it avoids a few problems. For example,
// if the mainChildView weren't clipped and thus visible in the titlebar, we'd
// have have to do the rounded corner masking and the drawing of the highlight
// line ourselves.
// This would be especially hard in combination with OpenGL acceleration since
// rounded corners would require making the OpenGL context transparent, which
// would bring another set of challenges with it. Having the window controls
// draw on top of an OpenGL context could be hard, too.
//
// So title bar drawing happens in the border view. The border view's drawRect
// method is not under our control, but we can get it to call into our code
// using some tricks, see the TitlebarAndBackgroundColor class below.
// Specifically, we have it call the TitlebarDrawCallback function, which
// draws the contents of mainChildView into the provided CGContext.
// (Even if the ChildView uses OpenGL for rendering, drawing in the title bar
// will happen non-accelerated in that CGContext.)
//
// Mouse event redirection happens via a TitlebarMouseHandlingView which we
// install below.
- (void)setDrawsContentsIntoWindowFrame:(BOOL)aState
{
BOOL stateChanged = ([self drawsContentsIntoWindowFrame] != aState);
[super setDrawsContentsIntoWindowFrame:aState];
if (stateChanged && [[self delegate] isKindOfClass:[WindowDelegate class]]) {
// Here we extend / shrink our mainChildView. We do that by firing a resize
// event which will cause the ChildView to be resized to the rect returned
// by nsCocoaWindow::GetClientBounds. GetClientBounds bases its return
// value on what we return from drawsContentsIntoWindowFrame.
WindowDelegate *windowDelegate = (WindowDelegate *)[self delegate];
nsCocoaWindow *geckoWindow = [windowDelegate geckoWidget];
if (geckoWindow) {
@ -2883,35 +2749,10 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
// we'll send a mouse move event with the correct new position.
ChildViewMouseTracker::ResendLastMouseMoveEvent();
if (aState) {
[self installTitlebarMouseHandlingView];
} else {
[self uninstallTitlebarMouseHandlingView];
}
[self setTitlebarNeedsDisplayInRect:[self titlebarRect]];
}
}
- (void)installTitlebarMouseHandlingView
{
mTitlebarView = [[TitlebarMouseHandlingView alloc] initWithWindow:self];
[[[self contentView] superview] addSubview:mTitlebarView positioned:NSWindowBelow relativeTo:nil];
}
- (void)uninstallTitlebarMouseHandlingView
{
[mTitlebarView removeFromSuperview];
[mTitlebarView release];
mTitlebarView = nil;
}
- (ChildView*)mainChildView
{
NSView* view = [[[self contentView] subviews] lastObject];
if (view && [view isKindOfClass:[ChildView class]])
return (ChildView*)view;
return nil;
}
// Returning YES here makes the setShowsToolbarButton method work even though
// the window doesn't contain an NSToolbar.
- (BOOL)_hasToolbar
@ -2980,9 +2821,6 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) {
nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget];
if (widget) {
if (type == NSMouseMoved) {
[[self mainChildView] updateWindowDraggableStateOnMouseMove:anEvent];
}
if (gGeckoAppModalWindowList && (widget != gGeckoAppModalWindowList->window))
return;
if (widget->HasModalDescendents())
@ -3048,8 +2886,8 @@ TitlebarDrawCallback(void* aInfo, CGContextRef aContext)
NSRect titlebarRect = [window titlebarRect];
if ([window drawsContentsIntoWindowFrame]) {
ChildView* view = [window mainChildView];
if (!view)
NSView* view = [[[window contentView] subviews] lastObject];
if (!view || ![view isKindOfClass:[ChildView class]])
return;
// Gecko drawing assumes flippedness, but the current context isn't flipped
@ -3060,7 +2898,7 @@ TitlebarDrawCallback(void* aInfo, CGContextRef aContext)
CGContextTranslateCTM(aContext, 0.0f, -[window frame].size.height);
NSRect flippedTitlebarRect = { NSZeroPoint, titlebarRect.size };
[view drawRect:flippedTitlebarRect inTitlebarContext:aContext];
[(ChildView*)view drawRect:flippedTitlebarRect inTitlebarContext:aContext];
} else {
BOOL isMain = [window isMainWindow];
NSColor *titlebarColor = [window titlebarColorForActiveWindow:isMain];

View File

@ -8,7 +8,7 @@
/* NOTE: this interface is completely undesigned, not stable and likely to change */
[scriptable, uuid(a67c77af-2952-4028-93ab-e7bc3b43cf81)]
[scriptable, uuid(8a9797ae-22d4-431d-a628-18fd5900c53c)]
interface nsIGfxInfo : nsISupports
{
/*
@ -77,6 +77,8 @@ interface nsIGfxInfo : nsISupports
const long FEATURE_WEBGL_ANGLE = 7;
/* Whether WebGL antialiasing is supported. */
const long FEATURE_WEBGL_MSAA = 8;
/* Whether Stagefright is supported */
const long FEATURE_STAGEFRIGHT = 9;
/*
* A set of return values from GetFeatureStatus

View File

@ -45,6 +45,8 @@ public:
virtual nsresult Init();
virtual uint32_t OperatingSystemVersion() const { return mWindowsVersion; }
#ifdef DEBUG
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIGFXINFODEBUG

View File

@ -17,6 +17,7 @@ nsAString* GfxDriverInfo::mDeviceVendors[DeviceVendorMax];
GfxDriverInfo::GfxDriverInfo()
: mOperatingSystem(DRIVER_OS_UNKNOWN),
mOperatingSystemVersion(0),
mAdapterVendor(GfxDriverInfo::GetDeviceVendor(VendorAll)),
mDevices(allDevices),
mDeleteDevices(false),
@ -36,6 +37,7 @@ GfxDriverInfo::GfxDriverInfo(OperatingSystem os, nsAString& vendor,
const char *suggestedVersion /* = nullptr */,
bool ownDevices /* = false */)
: mOperatingSystem(os),
mOperatingSystemVersion(0),
mAdapterVendor(vendor),
mDevices(devices),
mDeleteDevices(ownDevices),
@ -49,6 +51,7 @@ GfxDriverInfo::GfxDriverInfo(OperatingSystem os, nsAString& vendor,
GfxDriverInfo::GfxDriverInfo(const GfxDriverInfo& aOrig)
: mOperatingSystem(aOrig.mOperatingSystem),
mOperatingSystemVersion(aOrig.mOperatingSystemVersion),
mAdapterVendor(aOrig.mAdapterVendor),
mFeature(aOrig.mFeature),
mFeatureStatus(aOrig.mFeatureStatus),

View File

@ -86,6 +86,7 @@ struct GfxDriverInfo
~GfxDriverInfo();
OperatingSystem mOperatingSystem;
uint32_t mOperatingSystemVersion;
nsString mAdapterVendor;
@ -117,6 +118,8 @@ struct GfxDriverInfo
static const nsAString& GetDeviceVendor(DeviceVendor id);
static nsAString* mDeviceVendors[DeviceVendorMax];
nsString mModel, mHardware, mProduct, mManufacturer;
};
#define GFX_DRIVER_VERSION(a,b,c,d) \

View File

@ -121,6 +121,9 @@ GetPrefNameForFeature(int32_t aFeature)
case nsIGfxInfo::FEATURE_WEBGL_MSAA:
name = BLACKLIST_PREF_BRANCH "webgl.msaa";
break;
case nsIGfxInfo::FEATURE_STAGEFRIGHT:
name = BLACKLIST_PREF_BRANCH "stagefright";
break;
default:
break;
};
@ -270,7 +273,8 @@ BlacklistFeatureToGfxFeature(const nsAString& aFeature)
return nsIGfxInfo::FEATURE_WEBGL_ANGLE;
else if (aFeature == NS_LITERAL_STRING("WEBGL_MSAA"))
return nsIGfxInfo::FEATURE_WEBGL_MSAA;
else if (aFeature == NS_LITERAL_STRING("STAGEFRIGHT"))
return nsIGfxInfo::FEATURE_STAGEFRIGHT;
return 0;
}
@ -379,6 +383,13 @@ BlacklistEntryToDriverInfo(nsIDOMNode* aBlacklistEntry,
aDriverInfo.mOperatingSystem = BlacklistOSToOperatingSystem(dataValue);
}
// <osversion>14</osversion> currently only used for Android
if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("osversion"),
getter_AddRefs(dataNode))) {
BlacklistNodeToTextValue(dataNode, dataValue);
aDriverInfo.mOperatingSystemVersion = strtoul(NS_LossyConvertUTF16toASCII(dataValue).get(), NULL, 10);
}
// <vendor>0x8086</vendor>
if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("vendor"),
getter_AddRefs(dataNode))) {
@ -440,6 +451,31 @@ BlacklistEntryToDriverInfo(nsIDOMNode* aBlacklistEntry,
aDriverInfo.mComparisonOp = BlacklistComparatorToComparisonOp(dataValue);
}
// <model>foo</model>
if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("model"),
getter_AddRefs(dataNode))) {
BlacklistNodeToTextValue(dataNode, dataValue);
aDriverInfo.mModel = dataValue;
}
// <product>foo</product>
if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("product"),
getter_AddRefs(dataNode))) {
BlacklistNodeToTextValue(dataNode, dataValue);
aDriverInfo.mProduct = dataValue;
}
// <manufacturer>foo</manufacturer>
if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("manufacturer"),
getter_AddRefs(dataNode))) {
BlacklistNodeToTextValue(dataNode, dataValue);
aDriverInfo.mManufacturer = dataValue;
}
// <hardware>foo</hardware>
if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("hardware"),
getter_AddRefs(dataNode))) {
BlacklistNodeToTextValue(dataNode, dataValue);
aDriverInfo.mHardware = dataValue;
}
// We explicitly ignore unknown elements.
return true;
@ -555,6 +591,10 @@ GfxInfoBase::FindBlocklistedDeviceInList(const nsTArray<GfxDriverInfo>& info,
continue;
}
if (info[i].mOperatingSystemVersion && info[i].mOperatingSystemVersion != OperatingSystemVersion()) {
continue;
}
if (!info[i].mAdapterVendor.Equals(GfxDriverInfo::GetDeviceVendor(VendorAll), nsCaseInsensitiveStringComparator()) &&
!info[i].mAdapterVendor.Equals(adapterVendorID, nsCaseInsensitiveStringComparator())) {
continue;
@ -576,6 +616,19 @@ GfxInfoBase::FindBlocklistedDeviceInList(const nsTArray<GfxDriverInfo>& info,
bool match = false;
if (!info[i].mHardware.IsEmpty() && !info[i].mHardware.Equals(Hardware())) {
continue;
}
if (!info[i].mModel.IsEmpty() && !info[i].mModel.Equals(Model())) {
continue;
}
if (!info[i].mProduct.IsEmpty() && !info[i].mProduct.Equals(Product())) {
continue;
}
if (!info[i].mManufacturer.IsEmpty() && !info[i].mManufacturer.Equals(Manufacturer())) {
continue;
}
#if defined(XP_WIN) || defined(ANDROID)
switch (info[i].mComparisonOp) {
case DRIVER_LESS_THAN:
@ -741,6 +794,7 @@ GfxInfoBase::EvaluateDownloadedBlacklist(nsTArray<GfxDriverInfo>& aDriverInfo)
nsIGfxInfo::FEATURE_WEBGL_OPENGL,
nsIGfxInfo::FEATURE_WEBGL_ANGLE,
nsIGfxInfo::FEATURE_WEBGL_MSAA,
nsIGfxInfo::FEATURE_STAGEFRIGHT,
0
};

View File

@ -68,6 +68,12 @@ public:
static nsTArray<GfxDriverInfo>* mDriverInfo;
static bool mDriverInfoObserverInitialized;
virtual const nsAString& Model() const { return nsString(); }
virtual const nsAString& Hardware() const { return nsString(); }
virtual const nsAString& Product() const { return nsString(); }
virtual const nsAString& Manufacturer() const { return nsString(); }
virtual uint32_t OperatingSystemVersion() const { return 0; }
protected:
virtual nsresult GetFeatureStatusImpl(int32_t aFeature, int32_t* aStatus,