Merge mozilla-central to mozilla-inbound

This commit is contained in:
Ed Morley 2011-11-15 19:19:24 +00:00
commit 97ff754433
27 changed files with 1084 additions and 80 deletions

View File

@ -140,6 +140,7 @@ MOZ_UPDATE_PACKAGING = @MOZ_UPDATE_PACKAGING@
MOZ_DISABLE_PARENTAL_CONTROLS = @MOZ_DISABLE_PARENTAL_CONTROLS@
NS_ENABLE_TSF = @NS_ENABLE_TSF@
MOZ_SPELLCHECK = @MOZ_SPELLCHECK@
MOZ_JAVA_COMPOSITOR = @MOZ_JAVA_COMPOSITOR@
MOZ_PROFILELOCKING = @MOZ_PROFILELOCKING@
MOZ_FEEDS = @MOZ_FEEDS@
MOZ_TOOLKIT_SEARCH = @MOZ_TOOLKIT_SEARCH@

View File

@ -4662,6 +4662,7 @@ MOZ_REFLOW_PERF=
MOZ_SAFE_BROWSING=
MOZ_HELP_VIEWER=
MOZ_SPELLCHECK=1
MOZ_JAVA_COMPOSITOR=
MOZ_SVG_DLISTS=
MOZ_TOOLKIT_SEARCH=1
MOZ_UI_LOCALE=en-US
@ -8431,6 +8432,7 @@ AC_SUBST(IBMBIDI)
AC_SUBST(MOZ_UNIVERSALCHARDET)
AC_SUBST(ACCESSIBILITY)
AC_SUBST(MOZ_SPELLCHECK)
AC_SUBST(MOZ_JAVA_COMPOSITOR)
AC_SUBST(MOZ_USER_DIR)
AC_SUBST(MOZ_CRASHREPORTER)
AC_SUBST(MOZ_UPDATER)

View File

@ -1955,6 +1955,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
tmp->mAnimationController->Unlink();
}
tmp->mPendingTitleChangeEvent.Revoke();
tmp->mInUnlinkOrDeletion = false;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

View File

@ -62,6 +62,8 @@ import android.webkit.MimeTypeMap;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.provider.Settings;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityEvent;
import android.util.*;
import android.net.Uri;
@ -411,8 +413,6 @@ public class GeckoAppShell
GeckoAppShell.nativeRun(combinedArgs);
}
private static GeckoEvent mLastDrawEvent;
private static void sendPendingEventsToGecko() {
try {
while (!gPendingEvents.isEmpty()) {
@ -1359,6 +1359,13 @@ public class GeckoAppShell
return true;
}
}
public static boolean getAccessibilityEnabled() {
AccessibilityManager accessibilityManager =
(AccessibilityManager) GeckoApp.mAppContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
return accessibilityManager.isEnabled();
}
public static void addPluginView(final View view,
final double x, final double y,
final double w, final double h) {
@ -1620,6 +1627,16 @@ public class GeckoAppShell
}
}
// unused
public static String handleGeckoMessage(String message) {
return "";
}
// unused
static void checkUriVisited(String uri) {}
// unused
static void markUriVisited(final String uri) {}
public static void enableBatteryNotifications() {
GeckoBatteryManager.enableNotifications();
}

View File

@ -73,6 +73,8 @@ public class GeckoEvent {
public static final int SURFACE_DESTROYED = 14;
public static final int GECKO_EVENT_SYNC = 15;
public static final int ACTIVITY_START = 17;
public static final int SAVE_STATE = 18;
public static final int BROADCAST = 19;
public static final int IME_COMPOSITION_END = 0;
public static final int IME_COMPOSITION_BEGIN = 1;
@ -104,7 +106,7 @@ public class GeckoEvent {
public int mMetaState, mFlags;
public int mKeyCode, mUnicodeChar;
public int mOffset, mCount;
public String mCharacters;
public String mCharacters, mCharactersExtra;
public int mRangeType, mRangeStyles;
public int mRangeForeColor, mRangeBackColor;
public Location mLocation;
@ -223,6 +225,12 @@ public class GeckoEvent {
mP1 = new Point(screenw, screenh);
}
public GeckoEvent(String subject, String data) {
mType = BROADCAST;
mCharacters = subject;
mCharactersExtra = data;
}
public GeckoEvent(String uri) {
mType = LOAD_URI;
mCharacters = uri;

View File

@ -21,8 +21,7 @@
"test_privbrw_tabs.js",
"test_bookmarks_in_same_named_folder.js",
"test_client_wipe.js",
"test_special_tabs.js",
"test_mozmill_sanity.js"
"test_special_tabs.js"
]
}

View File

@ -0,0 +1,50 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/*
* The list of phases mapped to their corresponding profiles. The object
* here must be in strict JSON format, as it will get parsed by the Python
* testrunner (no single quotes, extra comma's, etc).
*/
var phases = { "phase1": "profile1",
"phase2": "profile1",
"phase3": "profile1",
"phase4": "profile1",
"phase5": "profile1" };
/*
* Test phases
*/
Phase('phase1', [
[Addons.install, ['unsigned-1.0.xml']],
[Addons.verify, ['unsigned-xpi@tests.mozilla.org'], STATE_DISABLED],
[Sync, SYNC_WIPE_SERVER],
]);
Phase('phase2', [
[Sync],
[Addons.verify, ['unsigned-xpi@tests.mozilla.org'], STATE_ENABLED],
[Addons.setState, ['unsigned-xpi@tests.mozilla.org'], STATE_DISABLED],
[Sync],
]);
Phase('phase3', [
[Sync],
[Addons.verify, ['unsigned-xpi@tests.mozilla.org'], STATE_DISABLED],
[Addons.setState, ['unsigned-xpi@tests.mozilla.org'], STATE_ENABLED],
[Sync],
]);
Phase('phase4', [
[Sync],
[Addons.verify, ['unsigned-xpi@tests.mozilla.org'], STATE_ENABLED],
[Addons.uninstall, ['unsigned-xpi@tests.mozilla.org']],
[Sync],
]);
Phase('phase5', [
[Sync],
[Addons.verifyNot, ['unsigned-xpi@tests.mozilla.org']],
]);

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<searchresults total_results="1">
<addon id="5612">
<name>Unsigned Test XPI</name>
<type id="1">Extension</type>
<guid>unsigned-xpi@tests.mozilla.org</guid>
<slug>unsigned-xpi</slug>
<version>1.0</version>
<compatible_applications><application>
<name>Firefox</name>
<application_id>1</application_id>
<min_version>3.6</min_version>
<max_version>*</max_version>
<appID>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</appID>
</application></compatible_applications>
<all_compatible_os><os>ALL</os></all_compatible_os>
<install os="ALL" size="452">http://127.0.0.1:4567/unsigned-1.0.xpi</install>
<created epoch="1252903662">
2009-09-14T04:47:42Z
</created>
<last_updated epoch="1315255329">
2011-09-05T20:42:09Z
</last_updated>
</addon>
</searchresults>

Binary file not shown.

View File

@ -30,9 +30,8 @@ tail =
# Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
skip-if = os == "android"
[test_errorhandler_sync_checkServerError.js]
# Bug 604565: this test intermittently hangs on OS X debug builds.
# Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
skip-if = (os == "mac" && debug) || os == "android"
skip-if = os == "android"
[test_forms_store.js]
[test_forms_tracker.js]
[test_history_engine.js]
@ -80,13 +79,11 @@ skip-if = os == "win" || os == "android"
[test_service_sync_401.js]
[test_service_sync_locked.js]
[test_service_sync_remoteSetup.js]
# Bug 604565: this test intermittently hangs on OS X debug builds.
# Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
skip-if = (os == "mac" && debug) || os == "android"
skip-if = os == "android"
[test_service_sync_updateEnabledEngines.js]
# Bug 604565: this test intermittently hangs on OS X debug builds.
# Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
skip-if = (os == "mac" && debug) || os == "android"
skip-if = os == "android"
[test_service_verifyLogin.js]
[test_service_wipeClient.js]
[test_service_wipeServer.js]
@ -94,9 +91,8 @@ skip-if = (os == "mac" && debug) || os == "android"
[test_status_checkSetup.js]
[test_syncengine.js]
[test_syncengine_sync.js]
# Bug 604565: this test intermittently hangs on OS X debug builds.
# Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
skip-if = (os == "mac" && debug) || os == "android"
skip-if = os == "android"
[test_syncscheduler.js]
[test_syncstoragerequest.js]
[test_tab_engine.js]

View File

@ -0,0 +1,252 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Crossweave.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jonathan Griffin <jgriffin@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var EXPORTED_SYMBOLS = ["Addon", "STATE_ENABLED", "STATE_DISABLED"];
const CC = Components.classes;
const CI = Components.interfaces;
const CU = Components.utils;
CU.import("resource://gre/modules/AddonManager.jsm");
CU.import("resource://gre/modules/AddonRepository.jsm");
CU.import("resource://gre/modules/Services.jsm");
CU.import("resource://services-sync/async.js");
CU.import("resource://services-sync/util.js");
CU.import("resource://tps/logger.jsm");
var XPIProvider = CU.import("resource://gre/modules/XPIProvider.jsm")
.XPIProvider;
const ADDONSGETURL = 'http://127.0.0.1:4567/';
const STATE_ENABLED = 1;
const STATE_DISABLED = 2;
function GetFileAsText(file)
{
let channel = Services.io.newChannel(file, null, null);
let inputStream = channel.open();
if (channel instanceof CI.nsIHttpChannel &&
channel.responseStatus != 200) {
return "";
}
let streamBuf = "";
let sis = CC["@mozilla.org/scriptableinputstream;1"]
.createInstance(CI.nsIScriptableInputStream);
sis.init(inputStream);
let available;
while ((available = sis.available()) != 0) {
streamBuf += sis.read(available);
}
inputStream.close();
return streamBuf;
}
function Addon(TPS, id) {
this.TPS = TPS;
this.id = id;
}
Addon.prototype = {
_addons_requiring_restart: [],
_addons_pending_install: [],
Delete: function() {
// find our addon locally
let cb = Async.makeSyncCallback();
XPIProvider.getAddonsByTypes(null, cb);
let results = Async.waitForSyncCallback(cb);
var addon;
var id = this.id;
results.forEach(function(result) {
if (result.id == id) {
addon = result;
}
});
Logger.AssertTrue(!!addon, 'could not find addon ' + this.id + ' to uninstall');
addon.uninstall();
},
Find: function(state) {
let cb = Async.makeSyncCallback();
let addon_found = false;
var that = this;
var log_addon = function(addon) {
that.addon = addon;
Logger.logInfo('addon ' + addon.id + ' found, isActive: ' + addon.isActive);
if (state == STATE_ENABLED || state == STATE_DISABLED) {
Logger.AssertEqual(addon.isActive,
state == STATE_ENABLED ? true : false,
"addon " + that.id + " has an incorrect enabled state");
}
};
// first look in the list of all addons
XPIProvider.getAddonsByTypes(null, cb);
let addonlist = Async.waitForSyncCallback(cb);
addonlist.forEach(function(addon) {
if (addon.id == that.id) {
addon_found = true;
log_addon.call(that, addon);
}
});
if (!addon_found) {
// then look in the list of recent installs
cb = Async.makeSyncCallback();
XPIProvider.getInstallsByTypes(null, cb);
addonlist = Async.waitForSyncCallback(cb);
for (var i in addonlist) {
if (addonlist[i].addon && addonlist[i].addon.id == that.id &&
addonlist[i].state == AddonManager.STATE_INSTALLED) {
addon_found = true;
log_addon.call(that, addonlist[i].addon);
}
}
}
return addon_found;
},
Install: function() {
// For Install, the id parameter initially passed is really the filename
// for the addon's install .xml; we'll read the actual id from the .xml.
let url = this.id;
// set the url used by getAddonsByIDs
var prefs = CC["@mozilla.org/preferences-service;1"]
.getService(CI.nsIPrefBranch);
prefs.setCharPref('extensions.getAddons.get.url', ADDONSGETURL + url);
// read the XML and find the addon id
xml = GetFileAsText(ADDONSGETURL + url);
Logger.AssertTrue(xml.indexOf("<guid>") > -1, 'guid not found in ' + url);
this.id = xml.substring(xml.indexOf("<guid>") + 6, xml.indexOf("</guid"));
Logger.logInfo('addon XML = ' + this.id);
// find our addon on 'AMO'
let cb = Async.makeSyncCallback();
AddonRepository.getAddonsByIDs([this.id], {
searchSucceeded: cb,
searchFailed: cb
}, false);
// Result will be array of addons on searchSucceeded or undefined on
// searchFailed.
let install_addons = Async.waitForSyncCallback(cb);
Logger.AssertTrue(install_addons,
"no addons found for id " + this.id);
Logger.AssertEqual(install_addons.length,
1,
"multiple addons found for id " + this.id);
let addon = install_addons[0];
Logger.logInfo(JSON.stringify(addon), null, ' ');
if (XPIProvider.installRequiresRestart(addon)) {
this._addons_requiring_restart.push(addon.id);
}
// Start installing the addon asynchronously; finish up in
// onInstallEnded(), onInstallFailed(), or onDownloadFailed().
this._addons_pending_install.push(addon.id);
this.TPS.StartAsyncOperation();
Utils.nextTick(function() {
let callback = function(aInstall) {
addon.install = aInstall;
Logger.logInfo("addon install: " + addon.install);
Logger.AssertTrue(addon.install,
"could not get install object for id " + this.id);
addon.install.addListener(this);
addon.install.install();
};
AddonManager.getInstallForURL(addon.sourceURI.spec,
callback.bind(this),
"application/x-xpinstall");
}, this);
},
SetState: function(state) {
if (!this.Find())
return false;
this.addon.userDisabled = state == STATE_ENABLED ? false : true;
return true;
},
// addon installation callbacks
onInstallEnded: function(addon) {
try {
Logger.logInfo('--------- event observed: addon onInstallEnded');
Logger.AssertTrue(addon.addon,
"No addon object in addon instance passed to onInstallEnded");
Logger.AssertTrue(this._addons_pending_install.indexOf(addon.addon.id) > -1,
"onInstallEnded received for unexpected addon " + addon.addon.id);
this._addons_pending_install.splice(
this._addons_pending_install.indexOf(addon.addon.id),
1);
}
catch(e) {
// We can't throw during a callback, as it will just get eaten by
// the callback's caller.
Utils.nextTick(function() {
this.DumpError(e);
}, this);
return;
}
this.TPS.FinishAsyncOperation();
},
onInstallFailed: function(addon) {
Logger.logInfo('--------- event observed: addon onInstallFailed');
Utils.nextTick(function() {
this.DumpError('Installation failed for addon ' +
(addon.addon && addon.addon.id ? addon.addon.id : 'unknown'));
}, this);
},
onDownloadFailed: function(addon) {
Logger.logInfo('--------- event observed: addon onDownloadFailed');
Utils.nextTick(function() {
this.DumpError('Download failed for addon ' +
(addon.addon && addon.addon.id ? addon.addon.id : 'unknown'));
}, this);
},
};

View File

@ -48,9 +48,11 @@ const CU = Components.utils;
CU.import("resource://services-sync/service.js");
CU.import("resource://services-sync/constants.js");
CU.import("resource://services-sync/async.js");
CU.import("resource://services-sync/util.js");
CU.import("resource://gre/modules/XPCOMUtils.jsm");
CU.import("resource://gre/modules/Services.jsm");
CU.import("resource://tps/addons.jsm");
CU.import("resource://tps/bookmarks.jsm");
CU.import("resource://tps/logger.jsm");
CU.import("resource://tps/passwords.jsm");
@ -61,6 +63,8 @@ CU.import("resource://tps/tabs.jsm");
var hh = CC["@mozilla.org/network/protocol;1?name=http"]
.getService(CI.nsIHttpProtocolHandler);
var prefs = CC["@mozilla.org/preferences-service;1"]
.getService(CI.nsIPrefBranch);
var mozmillInit = {};
CU.import('resource://mozmill/modules/init.js', mozmillInit);
@ -73,37 +77,16 @@ const ACTION_SYNC = "sync";
const ACTION_DELETE = "delete";
const ACTION_PRIVATE_BROWSING = "private-browsing";
const ACTION_WIPE_SERVER = "wipe-server";
const ACTION_SETSTATE = "set-state";
const ACTIONS = [ACTION_ADD, ACTION_VERIFY, ACTION_VERIFY_NOT,
ACTION_MODIFY, ACTION_SYNC, ACTION_DELETE,
ACTION_PRIVATE_BROWSING, ACTION_WIPE_SERVER];
ACTION_PRIVATE_BROWSING, ACTION_WIPE_SERVER,
ACTION_SETSTATE];
const SYNC_WIPE_SERVER = "wipe-server";
const SYNC_RESET_CLIENT = "reset-client";
const SYNC_WIPE_CLIENT = "wipe-client";
function GetFileAsText(file)
{
let channel = Services.io.newChannel(file, null, null);
let inputStream = channel.open();
if (channel instanceof CI.nsIHttpChannel &&
channel.responseStatus != 200) {
return "";
}
let streamBuf = "";
let sis = CC["@mozilla.org/scriptableinputstream;1"]
.createInstance(CI.nsIScriptableInputStream);
sis.init(inputStream);
let available;
while ((available = sis.available()) != 0) {
streamBuf += sis.read(available);
}
inputStream.close();
return streamBuf;
}
var TPS =
{
_waitingForSync: false,
@ -351,6 +334,33 @@ var TPS =
}
},
HandleAddons: function (addons, action, state) {
for (var i in addons) {
Logger.logInfo("executing action " + action.toUpperCase() +
" on addon " + JSON.stringify(addons[i]));
var addon = new Addon(this, addons[i]);
switch(action) {
case ACTION_ADD:
addon.Install();
break;
case ACTION_DELETE:
addon.Delete();
break;
case ACTION_VERIFY:
Logger.AssertTrue(addon.Find(state), 'addon ' + addon.id + ' not found');
break;
case ACTION_VERIFY_NOT:
Logger.AssertTrue(!addon.Find(state), 'addon ' + addon.id + " is present, but it shouldn't be");
break;
case ACTION_SETSTATE:
Logger.AssertTrue(addon.SetState(state), 'addon ' + addon.id + ' not found');
break;
}
}
Logger.logPass("executing action " + action.toUpperCase() +
" on addons");
},
HandleBookmarks: function (bookmarks, action) {
try {
let items = [];
@ -460,7 +470,7 @@ var TPS =
let phase = this._phaselist["phase" + this._currentPhase];
let action = phase[this._currentAction];
Logger.logInfo("starting action: " + JSON.stringify(action));
action[0].call(this, action[1]);
action[0].apply(this, action.slice(1));
// if we're in an async operation, don't continue on to the next action
if (this._operations_pending)
@ -517,8 +527,6 @@ var TPS =
// Store account details as prefs so they're accessible to the mozmill
// framework.
let prefs = CC["@mozilla.org/preferences-service;1"]
.getService(CI.nsIPrefBranch);
prefs.setCharPref('tps.account.username', this.config.account.username);
prefs.setCharPref('tps.account.password', this.config.account.password);
prefs.setCharPref('tps.account.passphrase', this.config.account.passphrase);
@ -634,6 +642,24 @@ var TPS =
},
};
var Addons = {
install: function Addons__install(addons) {
TPS.HandleAddons(addons, ACTION_ADD);
},
setState: function Addons__setState(addons, state) {
TPS.HandleAddons(addons, ACTION_SETSTATE, state);
},
uninstall: function Addons__uninstall(addons) {
TPS.HandleAddons(addons, ACTION_DELETE);
},
verify: function Addons__verify(addons, state) {
TPS.HandleAddons(addons, ACTION_VERIFY, state);
},
verifyNot: function Addons__verifyNot(addons) {
TPS.HandleAddons(addons, ACTION_VERIFY_NOT);
},
};
var Bookmarks = {
add: function Bookmarks__add(bookmarks) {
TPS.HandleBookmarks(bookmarks, ACTION_ADD);

View File

@ -38,4 +38,5 @@
from firefoxrunner import TPSFirefoxRunner
from pulse import TPSPulseMonitor
from testrunner import TPSTestRunner
from mozhttpd import MozHttpd

111
testing/tps/tps/mozhttpd.py Normal file
View File

@ -0,0 +1,111 @@
#!/usr/bin/python
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is
# the Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Joel Maher <joel.maher@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
import BaseHTTPServer
import SimpleHTTPServer
import threading
import sys
import os
import urllib
import re
from urlparse import urlparse
from SocketServer import ThreadingMixIn
DOCROOT = '.'
class EasyServer(ThreadingMixIn, BaseHTTPServer.HTTPServer):
allow_reuse_address = True
class MozRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def translate_path(self, path):
# It appears that the default path is '/' and os.path.join makes the '/'
o = urlparse(path)
return "%s%s" % ('' if sys.platform == 'win32' else '/', '/'.join([i.strip('/') for i in (DOCROOT, o.path)]))
# I found on my local network that calls to this were timing out
# I believe all of these calls are from log_message
def address_string(self):
return "a.b.c.d"
# This produces a LOT of noise
def log_message(self, format, *args):
pass
class MozHttpd(object):
def __init__(self, host="127.0.0.1", port=8888, docroot='.'):
global DOCROOT
self.host = host
self.port = int(port)
DOCROOT = docroot
def start(self):
self.httpd = EasyServer((self.host, self.port), MozRequestHandler)
self.server = threading.Thread(target=self.httpd.serve_forever)
self.server.setDaemon(True) # don't hang on exit
self.server.start()
#self.testServer()
#TODO: figure this out
def testServer(self):
fileList = os.listdir(DOCROOT)
filehandle = urllib.urlopen('http://%s:%s' % (self.host, self.port))
data = filehandle.readlines();
filehandle.close()
for line in data:
found = False
# '@' denotes a symlink and we need to ignore it.
webline = re.sub('\<[a-zA-Z0-9\-\_\.\=\"\'\/\\\%\!\@\#\$\^\&\*\(\) ]*\>', '', line.strip('\n')).strip('/').strip().strip('@')
if webline != "":
if webline == "Directory listing for":
found = True
else:
for fileName in fileList:
if fileName == webline:
found = True
if (found == False):
print "NOT FOUND: " + webline.strip()
def stop(self):
if self.httpd:
self.httpd.shutdown()
__del__ = stop

View File

@ -53,7 +53,7 @@ from mozprofile import Profile
from tps.firefoxrunner import TPSFirefoxRunner
from tps.phase import TPSTestPhase
from tps.mozhttpd import MozHttpd
class TempFile(object):
"""Class for temporary files that delete themselves when garbage-collected.
@ -397,6 +397,9 @@ class TPSTestRunner(object):
testlist = [os.path.basename(self.testfile)]
testdir = os.path.dirname(self.testfile)
self.mozhttpd = MozHttpd(port=4567, docroot=testdir)
self.mozhttpd.start()
# run each test, and save the results
for test in testlist:
result = self.run_single_test(testdir, test)
@ -415,6 +418,8 @@ class TPSTestRunner(object):
else:
self.numfailed += 1
self.mozhttpd.stop()
# generate the postdata we'll use to post the results to the db
self.postdata = { 'tests': self.results,
'os':os_string,
@ -440,7 +445,7 @@ class TPSTestRunner(object):
self.numpassed,
self.numfailed,
self.config['account']['serverURL'],
self.buildUrl)
buildUrl)
subj = "TPS Report: "
if self.numfailed == 0 and self.numpassed > 0:

View File

@ -161,6 +161,11 @@ AndroidBridge::Init(JNIEnv *jEnv,
jDisableBatteryNotifications = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "disableBatteryNotifications", "()V");
jGetCurrentBatteryInformation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getCurrentBatteryInformation", "()[D");
jGetAccessibilityEnabled = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getAccessibilityEnabled", "()Z");
jHandleGeckoMessage = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "handleGeckoMessage", "(Ljava/lang/String;)Ljava/lang/String;");
jCheckUriVisited = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "checkUriVisited", "(Ljava/lang/String;)V");
jMarkUriVisited = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "markUriVisited", "(Ljava/lang/String;)V");
jEGLContextClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("javax/microedition/khronos/egl/EGLContext"));
jEGL10Class = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("javax/microedition/khronos/egl/EGL10"));
jEGLSurfaceImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLSurfaceImpl"));
@ -670,6 +675,13 @@ AndroidBridge::HideProgressDialogOnce()
}
}
bool
AndroidBridge::GetAccessibilityEnabled()
{
ALOG_BRIDGE("AndroidBridge::GetAccessibilityEnabled");
return mJNIEnv->CallStaticBooleanMethod(mGeckoAppShellClass, jGetAccessibilityEnabled);
}
void
AndroidBridge::PerformHapticFeedback(bool aIsLongPress)
{
@ -839,6 +851,12 @@ AndroidBridge::SetSurfaceView(jobject obj)
mSurfaceView.Init(obj);
}
void
AndroidBridge::SetSoftwareLayerClient(jobject obj)
{
mSoftwareLayerClient.Init(obj);
}
void
AndroidBridge::ShowInputMethodPicker()
{
@ -1002,10 +1020,8 @@ AndroidBridge::CreateShortcut(const nsAString& aTitle, const nsAString& aURI, co
void
AndroidBridge::PostToJavaThread(nsIRunnable* aRunnable, bool aMainThread)
{
__android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "%s", __PRETTY_FUNCTION__);
JNIEnv* env = AndroidBridge::AttachThread(false);
if (!env) {
__android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "no jni env in %s!!", __PRETTY_FUNCTION__);
return;
}
mRunnableQueue.AppendObject(aRunnable);
@ -1016,27 +1032,21 @@ AndroidBridge::PostToJavaThread(nsIRunnable* aRunnable, bool aMainThread)
env->ExceptionDescribe();
env->ExceptionClear();
}
__android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "leaving %s", __PRETTY_FUNCTION__);
}
void
AndroidBridge::ExecuteNextRunnable()
{
__android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "%s", __PRETTY_FUNCTION__);
JNIEnv* env = AndroidBridge::AttachThread(false);
if (!env) {
__android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "no jni env in %s!!", __PRETTY_FUNCTION__);
return;
}
if (mRunnableQueue.Count() > 0) {
nsIRunnable* r = mRunnableQueue[0];
__android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "going to run %p", r);
r->Run();
mRunnableQueue.RemoveObjectAt(0);
}
__android_log_print(ANDROID_LOG_INFO, "GeckoBridge", "leaving %s", __PRETTY_FUNCTION__);
}
void
@ -1269,6 +1279,54 @@ AndroidBridge::GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInf
mJNIEnv->ReleaseDoubleArrayElements(arr, info, 0);
}
void
AndroidBridge::HandleGeckoMessage(const nsAString &aMessage, nsAString &aRet)
{
ALOG_BRIDGE("%s", __PRETTY_FUNCTION__);
JNIEnv* env = AndroidBridge::AttachThread(false);
if (!env) {
ALOG_BRIDGE("no jni env in %s!!", __PRETTY_FUNCTION__);
return;
}
AutoLocalJNIFrame jniFrame(1);
jstring jMessage = mJNIEnv->NewString(nsPromiseFlatString(aMessage).get(), aMessage.Length());
jstring returnMessage = static_cast<jstring>(env->CallStaticObjectMethod(mGeckoAppShellClass, jHandleGeckoMessage, jMessage));
jthrowable ex = env->ExceptionOccurred();
if (ex) {
env->ExceptionDescribe();
env->ExceptionClear();
}
nsJNIString jniStr(returnMessage);
aRet.Assign(jniStr);
ALOG_BRIDGE("leaving %s", __PRETTY_FUNCTION__);
}
void
AndroidBridge::CheckURIVisited(const nsAString& aURI)
{
AutoLocalJNIFrame jniFrame(1);
jstring jstrURI = mJNIEnv->NewString(nsPromiseFlatString(aURI).get(), aURI.Length());
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jCheckUriVisited, jstrURI);
}
void
AndroidBridge::MarkURIVisited(const nsAString& aURI)
{
AutoLocalJNIFrame jniFrame(1);
jstring jstrURI = mJNIEnv->NewString(nsPromiseFlatString(aURI).get(), aURI.Length());
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jMarkUriVisited, jstrURI);
}
void AndroidBridge::EmitGeckoAccessibilityEvent (PRInt32 eventType, const nsAString& role, const nsAString& text, const nsAString& description, bool enabled, bool checked, bool password) {
AutoLocalJNIFrame jniFrame;
jstring jstrRole = mJNIEnv->NewString(nsPromiseFlatString(role).get(), role.Length());
jstring jstrText = mJNIEnv->NewString(nsPromiseFlatString(text).get(), text.Length());
jstring jstrDescription = mJNIEnv->NewString(nsPromiseFlatString(description).get(), description.Length());
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jEmitGeckoAccessibilityEvent, eventType, jstrRole, jstrText, jstrDescription, enabled, checked, password);
}
void *
AndroidBridge::LockBitmap(jobject bitmap)
{
@ -1379,3 +1437,21 @@ AndroidBridge::UnlockWindow(void* window)
return true;
}
/* Implementation file */
NS_IMPL_ISUPPORTS1(nsAndroidBridge, nsIAndroidBridge)
nsAndroidBridge::nsAndroidBridge()
{
}
nsAndroidBridge::~nsAndroidBridge()
{
}
/* void handleGeckoEvent (in AString message); */
NS_IMETHODIMP nsAndroidBridge::HandleGeckoMessage(const nsAString & message, nsAString &aRet NS_OUTPARAM)
{
AndroidBridge::Bridge()->HandleGeckoMessage(message, aRet);
return NS_OK;
}

View File

@ -40,6 +40,7 @@
#include <jni.h>
#include <android/log.h>
#include <cstdlib>
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
@ -52,6 +53,8 @@
#include "nsIMIMEInfo.h"
#include "nsColor.h"
#include "nsIAndroidBridge.h"
// Some debug #defines
// #define DEBUG_ANDROID_EVENTS
// #define DEBUG_ANDROID_WIDGET
@ -147,6 +150,9 @@ public:
void ScheduleRestart();
void SetSoftwareLayerClient(jobject jobj);
AndroidGeckoSoftwareLayerClient &GetSoftwareLayerClient() { return mSoftwareLayerClient; }
void SetSurfaceView(jobject jobj);
AndroidGeckoSurfaceView& SurfaceView() { return mSurfaceView; }
@ -222,6 +228,8 @@ public:
void FireAndWaitForTracerEvent();
bool GetAccessibilityEnabled();
struct AutoLocalJNIFrame {
AutoLocalJNIFrame(int nEntries = 128) : mEntries(nEntries) {
// Make sure there is enough space to store a local ref to the
@ -290,6 +298,13 @@ public:
bool LockWindow(void *window, unsigned char **bits, int *width, int *height, int *format, int *stride);
bool UnlockWindow(void *window);
void HandleGeckoMessage(const nsAString& message, nsAString &aRet);
void EmitGeckoAccessibilityEvent (PRInt32 eventType, const nsAString& role, const nsAString& text, const nsAString& description, bool enabled, bool checked, bool password);
void CheckURIVisited(const nsAString& uri);
void MarkURIVisited(const nsAString& uri);
bool InitCamera(const nsCString& contentType, PRUint32 camera, PRUint32 *width, PRUint32 *height, PRUint32 *fps);
void CloseCamera();
@ -310,6 +325,7 @@ protected:
// the GeckoSurfaceView
AndroidGeckoSurfaceView mSurfaceView;
AndroidGeckoSoftwareLayerClient mSoftwareLayerClient;
// the GeckoAppShell java class
jclass mGeckoAppShellClass;
@ -375,6 +391,11 @@ protected:
jmethodID jEnableBatteryNotifications;
jmethodID jDisableBatteryNotifications;
jmethodID jGetCurrentBatteryInformation;
jmethodID jGetAccessibilityEnabled;
jmethodID jHandleGeckoMessage;
jmethodID jCheckUriVisited;
jmethodID jMarkUriVisited;
jmethodID jEmitGeckoAccessibilityEvent;
// stuff we need for CallEglCreateWindowSurface
jclass jEGLSurfaceImplClass;
@ -399,6 +420,24 @@ protected:
}
#define NS_ANDROIDBRIDGE_CID \
{ 0x0FE2321D, 0xEBD9, 0x467D, \
{ 0xA7, 0x43, 0x03, 0xA6, 0x8D, 0x40, 0x59, 0x9E } }
class nsAndroidBridge : public nsIAndroidBridge
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIANDROIDBRIDGE
nsAndroidBridge();
private:
~nsAndroidBridge();
protected:
};
extern "C" JNIEnv * GetJNIForThread();
extern bool mozilla_AndroidBridge_SetMainThread(void *);
extern jclass GetGeckoAppShellClass();

View File

@ -44,6 +44,7 @@
#include <jni.h>
#include <pthread.h>
#include <dlfcn.h>
#include <stdio.h>
#include "nsAppShell.h"
#include "nsWindow.h"
@ -52,6 +53,10 @@
#include "mozilla/Services.h"
#include "nsINetworkLinkService.h"
#ifdef MOZ_ANDROID_HISTORY
#include "nsAndroidHistory.h"
#endif
#ifdef MOZ_CRASHREPORTER
#include "nsICrashReporter.h"
#include "nsExceptionHandler.h"
@ -66,6 +71,7 @@ extern "C" {
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_nativeInit(JNIEnv *, jclass);
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyGeckoOfEvent(JNIEnv *, jclass, jobject event);
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_processNextNativeEvent(JNIEnv *, jclass);
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_setSoftwareLayerClient(JNIEnv *jenv, jclass, jobject sv);
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_setSurfaceView(JNIEnv *jenv, jclass, jobject sv);
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onResume(JNIEnv *, jclass);
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onLowMemory(JNIEnv *, jclass);
@ -74,6 +80,7 @@ extern "C" {
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus(JNIEnv *, jclass, jstring status);
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_reportJavaCrash(JNIEnv *, jclass, jstring stack);
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_executeNextRunnable(JNIEnv *, jclass);
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyUriVisited(JNIEnv *, jclass, jstring uri);
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange(JNIEnv* jenv, jclass, jdouble, jboolean, jdouble);
}
@ -110,6 +117,12 @@ Java_org_mozilla_gecko_GeckoAppShell_setSurfaceView(JNIEnv *jenv, jclass, jobjec
AndroidBridge::Bridge()->SetSurfaceView(jenv->NewGlobalRef(obj));
}
NS_EXPORT void JNICALL
Java_org_mozilla_gecko_GeckoAppShell_setSoftwareLayerClient(JNIEnv *jenv, jclass, jobject obj)
{
AndroidBridge::Bridge()->SetSoftwareLayerClient(jenv->NewGlobalRef(obj));
}
NS_EXPORT void JNICALL
Java_org_mozilla_gecko_GeckoAppShell_onLowMemory(JNIEnv *jenv, jclass jc)
{
@ -189,6 +202,14 @@ Java_org_mozilla_gecko_GeckoAppShell_executeNextRunnable(JNIEnv *, jclass)
__android_log_print(ANDROID_LOG_INFO, "GeckoJNI", "leaving %s", __PRETTY_FUNCTION__);
}
NS_EXPORT void JNICALL
Java_org_mozilla_gecko_GeckoAppShell_notifyUriVisited(JNIEnv *jenv, jclass, jstring uri)
{
#ifdef MOZ_ANDROID_HISTORY
nsAndroidHistory::NotifyURIVisited(nsJNIString(uri, jenv));
#endif
}
NS_EXPORT void JNICALL
Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange(JNIEnv* jenv, jclass,
jdouble aLevel,

View File

@ -56,6 +56,7 @@ jfieldID AndroidGeckoEvent::jRectField = 0;
jfieldID AndroidGeckoEvent::jNativeWindowField = 0;
jfieldID AndroidGeckoEvent::jCharactersField = 0;
jfieldID AndroidGeckoEvent::jCharactersExtraField = 0;
jfieldID AndroidGeckoEvent::jKeyCodeField = 0;
jfieldID AndroidGeckoEvent::jMetaStateField = 0;
jfieldID AndroidGeckoEvent::jFlagsField = 0;
@ -102,6 +103,11 @@ jmethodID AndroidAddress::jGetSubLocalityMethod;
jmethodID AndroidAddress::jGetSubThoroughfareMethod;
jmethodID AndroidAddress::jGetThoroughfareMethod;
jclass AndroidGeckoSoftwareLayerClient::jGeckoSoftwareLayerClientClass = 0;
jmethodID AndroidGeckoSoftwareLayerClient::jLockBufferMethod = 0;
jmethodID AndroidGeckoSoftwareLayerClient::jUnlockBufferMethod = 0;
jmethodID AndroidGeckoSoftwareLayerClient::jBeginDrawingMethod = 0;
jmethodID AndroidGeckoSoftwareLayerClient::jEndDrawingMethod = 0;
jclass AndroidGeckoSurfaceView::jGeckoSurfaceViewClass = 0;
jmethodID AndroidGeckoSurfaceView::jBeginDrawingMethod = 0;
jmethodID AndroidGeckoSurfaceView::jEndDrawingMethod = 0;
@ -130,10 +136,12 @@ void
mozilla::InitAndroidJavaWrappers(JNIEnv *jEnv)
{
AndroidGeckoEvent::InitGeckoEventClass(jEnv);
AndroidGeckoSurfaceView::InitGeckoSurfaceViewClass(jEnv);
AndroidPoint::InitPointClass(jEnv);
AndroidLocation::InitLocationClass(jEnv);
AndroidAddress::InitAddressClass(jEnv);
AndroidRect::InitRectClass(jEnv);
AndroidGeckoSoftwareLayerClient::InitGeckoSoftwareLayerClientClass(jEnv);
AndroidGeckoSurfaceView::InitGeckoSurfaceViewClass(jEnv);
}
void
@ -157,6 +165,7 @@ AndroidGeckoEvent::InitGeckoEventClass(JNIEnv *jEnv)
jRectField = getField("mRect", "Landroid/graphics/Rect;");
jCharactersField = getField("mCharacters", "Ljava/lang/String;");
jCharactersExtraField = getField("mCharactersExtra", "Ljava/lang/String;");
jKeyCodeField = getField("mKeyCode", "I");
jMetaStateField = getField("mMetaState", "I");
jFlagsField = getField("mFlags", "I");
@ -174,6 +183,7 @@ AndroidGeckoEvent::InitGeckoEventClass(JNIEnv *jEnv)
void
AndroidGeckoSurfaceView::InitGeckoSurfaceViewClass(JNIEnv *jEnv)
{
#ifndef MOZ_JAVA_COMPOSITOR
initInit();
jGeckoSurfaceViewClass = getClassGlobalRef("org/mozilla/gecko/GeckoSurfaceView");
@ -186,6 +196,7 @@ AndroidGeckoSurfaceView::InitGeckoSurfaceViewClass(JNIEnv *jEnv)
jDraw2DBufferMethod = getMethod("draw2D", "(Ljava/nio/ByteBuffer;I)V");
jGetSurfaceMethod = getMethod("getSurface", "()Landroid/view/Surface;");
jGetHolderMethod = getMethod("getHolder", "()Landroid/view/SurfaceHolder;");
#endif
}
void
@ -302,6 +313,22 @@ AndroidRect::InitRectClass(JNIEnv *jEnv)
jRightField = getField("right", "I");
}
void
AndroidGeckoSoftwareLayerClient::InitGeckoSoftwareLayerClientClass(JNIEnv *jEnv)
{
#ifdef MOZ_JAVA_COMPOSITOR
initInit();
jGeckoSoftwareLayerClientClass =
getClassGlobalRef("org/mozilla/gecko/gfx/GeckoSoftwareLayerClient");
jLockBufferMethod = getMethod("lockBuffer", "()Ljava/nio/ByteBuffer;");
jUnlockBufferMethod = getMethod("unlockBuffer", "()V");
jBeginDrawingMethod = getMethod("beginDrawing", "()V");
jEndDrawingMethod = getMethod("endDrawing", "(IIII)V");
#endif
}
#undef initInit
#undef initClassGlobalRef
#undef getField
@ -351,6 +378,27 @@ AndroidGeckoEvent::ReadCharactersField(JNIEnv *jenv)
jenv->GetStringRegion(s, 0, len, mCharacters.BeginWriting());
}
void
AndroidGeckoEvent::ReadCharactersExtraField(JNIEnv *jenv)
{
jstring s = (jstring) jenv->GetObjectField(wrapped_obj, jCharactersExtraField);
if (!s) {
mCharactersExtra.SetIsVoid(PR_TRUE);
return;
}
int len = jenv->GetStringLength(s);
mCharactersExtra.SetLength(len);
jenv->GetStringRegion(s, 0, len, mCharactersExtra.BeginWriting());
}
void
AndroidGeckoEvent::Init(int aType, nsIntRect const& aRect)
{
mType = aType;
mRect = aRect;
}
void
AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
{
@ -437,6 +485,17 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
break;
}
case BROADCAST: {
ReadCharactersField(jenv);
ReadCharactersExtraField(jenv);
break;
}
case SAVE_STATE: {
ReadCharactersField(jenv);
break;
}
default:
break;
}
@ -472,6 +531,25 @@ AndroidGeckoEvent::Init(AndroidGeckoEvent *aResizeEvent)
mP1.y = aResizeEvent->mP1.y;
}
void
AndroidPoint::Init(JNIEnv *jenv, jobject jobj)
{
if (jobj) {
mX = jenv->GetIntField(jobj, jXField);
mY = jenv->GetIntField(jobj, jYField);
} else {
mX = 0;
mY = 0;
}
}
void
AndroidGeckoSoftwareLayerClient::Init(jobject jobj)
{
NS_ASSERTION(wrapped_obj == nsnull, "Init called on non-null wrapped_obj!");
wrapped_obj = jobj;
}
void
AndroidGeckoSurfaceView::Init(jobject jobj)
{
@ -506,6 +584,46 @@ AndroidGeckoSurfaceView::Draw2D(jobject buffer, int stride)
JNI()->CallVoidMethod(wrapped_obj, jDraw2DBufferMethod, buffer, stride);
}
jobject
AndroidGeckoSoftwareLayerClient::LockBuffer()
{
NS_ASSERTION(!isNull(), "LockBuffer() called on null software layer client!");
AndroidBridge::AutoLocalJNIFrame(1);
return JNI()->CallObjectMethod(wrapped_obj, jLockBufferMethod);
}
unsigned char *
AndroidGeckoSoftwareLayerClient::LockBufferBits()
{
AndroidBridge::AutoLocalJNIFrame(1);
return reinterpret_cast<unsigned char *>(JNI()->GetDirectBufferAddress(LockBuffer()));
}
void
AndroidGeckoSoftwareLayerClient::UnlockBuffer()
{
NS_ASSERTION(!isNull(), "UnlockBuffer() called on null software layer client!");
AndroidBridge::AutoLocalJNIFrame(1);
JNI()->CallVoidMethod(wrapped_obj, jUnlockBufferMethod);
}
void
AndroidGeckoSoftwareLayerClient::BeginDrawing()
{
NS_ASSERTION(!isNull(), "BeginDrawing() called on null software layer client!");
AndroidBridge::AutoLocalJNIFrame(1);
return JNI()->CallVoidMethod(wrapped_obj, jBeginDrawingMethod);
}
void
AndroidGeckoSoftwareLayerClient::EndDrawing(const nsIntRect &aRect)
{
NS_ASSERTION(!isNull(), "EndDrawing() called on null software layer client!");
AndroidBridge::AutoLocalJNIFrame(1);
return JNI()->CallVoidMethod(wrapped_obj, jEndDrawingMethod, aRect.x, aRect.y, aRect.width,
aRect.height);
}
jobject
AndroidGeckoSurfaceView::GetSoftwareDrawBitmap()
{
@ -530,22 +648,6 @@ AndroidGeckoSurfaceView::GetSurfaceHolder()
return JNI()->CallObjectMethod(wrapped_obj, jGetHolderMethod);
}
void
AndroidPoint::Init(JNIEnv *jenv, jobject jobj)
{
NS_ASSERTION(wrapped_obj == nsnull, "Init called on non-null wrapped_obj!");
wrapped_obj = jobj;
if (jobj) {
mX = jenv->GetIntField(jobj, jXField);
mY = jenv->GetIntField(jobj, jYField);
} else {
mX = 0;
mY = 0;
}
}
void
AndroidRect::Init(JNIEnv *jenv, jobject jobj)
{

View File

@ -149,6 +149,32 @@ protected:
static jfieldID jTopField;
};
class AndroidGeckoSoftwareLayerClient : public WrappedJavaObject {
public:
static void InitGeckoSoftwareLayerClientClass(JNIEnv *jEnv);
void Init(jobject jobj);
AndroidGeckoSoftwareLayerClient() {}
AndroidGeckoSoftwareLayerClient(jobject jobj) { Init(jobj); }
jobject LockBuffer();
unsigned char *LockBufferBits();
void UnlockBuffer();
void BeginDrawing();
void EndDrawing(const nsIntRect &aRect);
private:
static jclass jGeckoSoftwareLayerClientClass;
static jmethodID jLockBufferMethod;
static jmethodID jUnlockBufferMethod;
protected:
static jmethodID jBeginDrawingMethod;
static jmethodID jEndDrawingMethod;
};
class AndroidGeckoSurfaceView : public WrappedJavaObject
{
public:
@ -388,6 +414,9 @@ public:
AndroidGeckoEvent(int x1, int y1, int x2, int y2) {
Init(x1, y1, x2, y2);
}
AndroidGeckoEvent(int aType, const nsIntRect &aRect) {
Init(aType, aRect);
}
AndroidGeckoEvent(JNIEnv *jenv, jobject jobj) {
Init(jenv, jobj);
}
@ -398,6 +427,7 @@ public:
void Init(JNIEnv *jenv, jobject jobj);
void Init(int aType);
void Init(int x1, int y1, int x2, int y2);
void Init(int aType, const nsIntRect &aRect);
void Init(AndroidGeckoEvent *aResizeEvent);
int Action() { return mAction; }
@ -413,6 +443,7 @@ public:
double Z() { return mZ; }
const nsIntRect& Rect() { return mRect; }
nsAString& Characters() { return mCharacters; }
nsAString& CharactersExtra() { return mCharactersExtra; }
int KeyCode() { return mKeyCode; }
int MetaState() { return mMetaState; }
int Flags() { return mFlags; }
@ -440,7 +471,7 @@ protected:
int mRangeForeColor, mRangeBackColor;
double mAlpha, mBeta, mGamma;
double mX, mY, mZ;
nsString mCharacters;
nsString mCharacters, mCharactersExtra;
nsRefPtr<nsGeoPosition> mGeoPosition;
nsRefPtr<nsGeoPositionAddress> mGeoAddress;
@ -448,6 +479,7 @@ protected:
void ReadP1Field(JNIEnv *jenv);
void ReadRectField(JNIEnv *jenv);
void ReadCharactersField(JNIEnv *jenv);
void ReadCharactersExtraField(JNIEnv *jenv);
static jclass jGeckoEventClass;
static jfieldID jActionField;
@ -465,6 +497,7 @@ protected:
static jfieldID jNativeWindowField;
static jfieldID jCharactersField;
static jfieldID jCharactersExtraField;
static jfieldID jKeyCodeField;
static jfieldID jMetaStateField;
static jfieldID jFlagsField;
@ -498,6 +531,8 @@ public:
GECKO_EVENT_SYNC = 15,
FORCED_RESIZE = 16,
ACTIVITY_START = 17,
SAVE_STATE = 18,
BROADCAST = 19,
dummy_java_enum_list_end
};

View File

@ -44,12 +44,16 @@ include $(DEPTH)/config/autoconf.mk
MODULE = widget
LIBRARY_NAME = widget_android
XPIDL_MODULE = widget_android
EXPORT_LIBRARY = 1
IS_COMPONENT = 1
MODULE_NAME = nsWidgetAndroidModule
GRE_MODULE = 1
LIBXUL_LIBRARY = 1
ifdef MOZ_JAVA_COMPOSITOR
DEFINES += -DMOZ_JAVA_COMPOSITOR
endif
CPPSRCS = \
GfxInfo.cpp \
@ -78,6 +82,10 @@ NOT_THERE_YET_CPPSRCS = \
nsSound.cpp \
$(NULL)
XPIDLSRCS = \
nsIAndroidBridge.idl \
$(NULL)
SHARED_LIBRARY_LIBS = ../xpwidgets/libxpwidgets_s.a
EXPORTS = AndroidBridge.h AndroidJavaWrappers.h
@ -91,6 +99,8 @@ LOCAL_INCLUDES += \
-I$(topsrcdir)/widget/src/xpwidgets \
-I$(topsrcdir)/widget/src/shared \
-I$(topsrcdir)/dom/system/android \
-I$(topsrcdir)/toolkit/components/places \
-I$(topsrcdir)/docshell/base \
-I$(srcdir) \
$(NULL)

View File

@ -368,12 +368,30 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
break;
}
case AndroidGeckoEvent::BROADCAST: {
if (curEvent->Characters().Length() == 0)
break;
nsCOMPtr<nsIObserverService> obsServ =
mozilla::services::GetObserverService();
const NS_ConvertUTF16toUTF8 topic(curEvent->Characters());
const nsPromiseFlatString& data = PromiseFlatString(curEvent->CharactersExtra());
obsServ->NotifyObservers(nsnull, topic.get(), data.get());
break;
}
case AndroidGeckoEvent::LOAD_URI: {
nsCOMPtr<nsICommandLineRunner> cmdline
(do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
if (!cmdline)
break;
if (curEvent->Characters().Length() == 0)
break;
char *uri = ToNewUTF8String(curEvent->Characters());
if (!uri)
break;

View File

@ -0,0 +1,7 @@
#include "nsISupports.idl"
[scriptable, uuid(32c345d4-9f45-446a-8a93-8939f3453e87)]
interface nsIAndroidBridge : nsISupports
{
AString handleGeckoMessage(in AString message);
};

View File

@ -41,6 +41,7 @@
#include "nsCOMPtr.h"
#include "nsWidgetsCID.h"
#include "nsAppShell.h"
#include "AndroidBridge.h"
#include "nsWindow.h"
#include "nsLookAndFeel.h"
@ -71,6 +72,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSession, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceContextSpecAndroid)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsIMEPicker)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsAndroidBridge)
#include "GfxInfo.h"
namespace mozilla {
@ -113,6 +115,7 @@ NS_DEFINE_NAMED_CID(NS_FILEPICKER_CID);
NS_DEFINE_NAMED_CID(NS_HTMLFORMATCONVERTER_CID);
NS_DEFINE_NAMED_CID(NS_IMEPICKER_CID);
NS_DEFINE_NAMED_CID(NS_GFXINFO_CID);
NS_DEFINE_NAMED_CID(NS_ANDROIDBRIDGE_CID);
static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
{ &kNS_WINDOW_CID, false, NULL, nsWindowConstructor },
@ -130,6 +133,7 @@ static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
{ &kNS_HTMLFORMATCONVERTER_CID, false, NULL, nsHTMLFormatConverterConstructor },
{ &kNS_IMEPICKER_CID, false, NULL, nsIMEPickerConstructor },
{ &kNS_GFXINFO_CID, false, NULL, mozilla::widget::GfxInfoConstructor },
{ &kNS_ANDROIDBRIDGE_CID, false, NULL, nsAndroidBridgeConstructor },
{ NULL }
};
@ -149,6 +153,7 @@ static const mozilla::Module::ContractIDEntry kWidgetContracts[] = {
{ "@mozilla.org/widget/htmlformatconverter;1", &kNS_HTMLFORMATCONVERTER_CID },
{ "@mozilla.org/imepicker;1", &kNS_IMEPICKER_CID },
{ "@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID },
{ "@mozilla.org/android/bridge;1", &kNS_ANDROIDBRIDGE_CID },
{ NULL }
};

View File

@ -75,6 +75,14 @@ using mozilla::unused;
#include "AndroidBridge.h"
#include "imgIEncoder.h"
#include "nsStringGlue.h"
// NB: Keep these in sync with LayerController.java in embedding/android/.
#define TILE_WIDTH 1024
#define TILE_HEIGHT 2048
using namespace mozilla;
NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget)
@ -83,6 +91,10 @@ NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget)
static gfxIntSize gAndroidBounds;
static gfxIntSize gAndroidScreenBounds;
#ifdef ACCESSIBILITY
bool nsWindow::sAccessibilityEnabled = false;
#endif
class ContentCreationNotifier;
static nsCOMPtr<ContentCreationNotifier> gContentCreationNotifier;
// A helper class to send updates when content processes
@ -176,6 +188,9 @@ nsWindow::nsWindow() :
mIsVisible(false),
mParent(nsnull),
mFocus(nsnull),
#ifdef ACCESSIBILITY
mRootAccessible(nsnull),
#endif
mIMEComposing(false)
{
}
@ -186,6 +201,10 @@ nsWindow::~nsWindow()
nsWindow *top = FindTopLevel();
if (top->mFocus == this)
top->mFocus = nsnull;
#ifdef ACCESSIBILITY
if (mRootAccessible)
mRootAccessible = nsnull;
#endif
ALOG("nsWindow %p destructor", (void*)this);
}
@ -283,6 +302,15 @@ nsWindow::ConfigureChildren(const nsTArray<nsIWidget::Configuration>& config)
return NS_OK;
}
void
nsWindow::RedrawAll()
{
nsIntRect entireRect(0, 0, TILE_WIDTH, TILE_HEIGHT);
AndroidGeckoEvent *event = new AndroidGeckoEvent(AndroidGeckoEvent::DRAW,
entireRect);
nsAppShell::gAppShell->PostEvent(event);
}
NS_IMETHODIMP
nsWindow::SetParent(nsIWidget *aNewParent)
{
@ -303,7 +331,7 @@ nsWindow::SetParent(nsIWidget *aNewParent)
// if we are now in the toplevel window's hierarchy, schedule a redraw
if (FindTopLevel() == TopWindow())
nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(-1, -1, -1, -1));
RedrawAll();
return NS_OK;
}
@ -371,9 +399,20 @@ nsWindow::Show(bool aState)
}
}
} else if (FindTopLevel() == TopWindow()) {
nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(-1, -1, -1, -1));
RedrawAll();
}
#ifdef ACCESSIBILITY
static bool sAccessibilityChecked = false;
if (!sAccessibilityChecked) {
sAccessibilityChecked = true;
sAccessibilityEnabled =
AndroidBridge::Bridge()->GetAccessibilityEnabled();
}
if (aState && sAccessibilityEnabled)
CreateRootAccessible();
#endif
#ifdef DEBUG_ANDROID_WIDGET
DumpWindows();
#endif
@ -381,6 +420,32 @@ nsWindow::Show(bool aState)
return NS_OK;
}
#ifdef ACCESSIBILITY
void
nsWindow::CreateRootAccessible()
{
if (IsTopLevel() && !mRootAccessible) {
ALOG(("nsWindow:: Create Toplevel Accessibility\n"));
nsAccessible *acc = DispatchAccessibleEvent();
if (acc) {
mRootAccessible = acc;
}
}
}
nsAccessible*
nsWindow::DispatchAccessibleEvent()
{
nsAccessibleEvent event(true, NS_GETACCESSIBLE, this);
nsEventStatus status;
DispatchEvent(&event, status);
return event.mAccessible;
}
#endif
NS_IMETHODIMP
nsWindow::SetModal(bool aState)
{
@ -459,7 +524,7 @@ nsWindow::Resize(PRInt32 aX,
// Should we skip honoring aRepaint here?
if (aRepaint && FindTopLevel() == TopWindow())
nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(-1, -1, -1, -1));
RedrawAll();
return NS_OK;
}
@ -512,7 +577,8 @@ NS_IMETHODIMP
nsWindow::Invalidate(const nsIntRect &aRect,
bool aIsSynchronous)
{
nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(-1, -1, -1, -1));
AndroidGeckoEvent *event = new AndroidGeckoEvent(AndroidGeckoEvent::DRAW, aRect);
nsAppShell::gAppShell->PostEvent(event);
return NS_OK;
}
@ -588,7 +654,7 @@ nsWindow::BringToFront()
// force a window resize
nsAppShell::gAppShell->ResendLastResizeEvent(this);
nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(-1, -1, -1, -1));
RedrawAll();
}
NS_IMETHODIMP
@ -730,6 +796,66 @@ nsWindow::GetThebesSurface()
return new gfxImageSurface(gfxIntSize(5,5), gfxImageSurface::ImageFormatRGB24);
}
bool
nsWindow::DrawToFile(const nsAString &path)
{
if (!IsTopLevel() || !mIsVisible) {
ALOG("### DrawToFile works only for a visible toplevel window!");
return PR_FALSE;
}
if (GetLayerManager(nsnull)->GetBackendType() != LayerManager::LAYERS_BASIC) {
ALOG("### DrawToFile works only for a basic layers!");
return PR_FALSE;
}
nsRefPtr<gfxImageSurface> imgSurface =
new gfxImageSurface(gfxIntSize(mBounds.width, mBounds.height),
gfxImageSurface::ImageFormatARGB32);
if (imgSurface->CairoStatus()) {
ALOG("### Failed to create a valid surface");
return PR_FALSE;
}
nsIntRect boundsRect(0, 0, mBounds.width, mBounds.height);
bool result = DrawTo(imgSurface, boundsRect);
NS_ENSURE_TRUE(result, PR_FALSE);
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance("@mozilla.org/image/encoder;2?type=image/png");
NS_ENSURE_TRUE(encoder, PR_FALSE);
encoder->InitFromData(imgSurface->Data(),
imgSurface->Stride() * mBounds.height,
mBounds.width,
mBounds.height,
imgSurface->Stride(),
imgIEncoder::INPUT_FORMAT_HOSTARGB,
EmptyString());
nsCOMPtr<nsILocalFile> file;
NS_NewLocalFile(path, true, getter_AddRefs(file));
NS_ENSURE_TRUE(file, PR_FALSE);
PRUint32 length;
encoder->Available(&length);
nsCOMPtr<nsIOutputStream> outputStream;
NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file);
NS_ENSURE_TRUE(outputStream, PR_FALSE);
nsCOMPtr<nsIOutputStream> bufferedOutputStream;
NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
outputStream, length);
NS_ENSURE_TRUE(bufferedOutputStream, PR_FALSE);
PRUint32 numWritten;
bufferedOutputStream->WriteFrom(encoder, length, &numWritten);
NS_ENSURE_SUCCESS(length == numWritten, PR_FALSE);
return PR_TRUE;
}
void
nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
{
@ -882,6 +1008,10 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
AndroidBridge::Bridge()->AcknowledgeEventSync();
break;
case AndroidGeckoEvent::SAVE_STATE:
win->DrawToFile(ae->Characters());
break;
default:
break;
}
@ -906,6 +1036,13 @@ nsWindow::OnAndroidEvent(AndroidGeckoEvent *ae)
bool
nsWindow::DrawTo(gfxASurface *targetSurface)
{
nsIntRect boundsRect(0, 0, mBounds.width, mBounds.height);
return DrawTo(targetSurface, boundsRect);
}
bool
nsWindow::DrawTo(gfxASurface *targetSurface, const nsIntRect &invalidRect)
{
if (!mIsVisible)
return false;
@ -927,7 +1064,7 @@ nsWindow::DrawTo(gfxASurface *targetSurface)
// If we have no covering child, then we need to render this.
if (coveringChildIndex == -1) {
nsPaintEvent event(true, NS_PAINT, this);
event.region = boundsRect;
event.region = boundsRect.Intersect(invalidRect);
switch (GetLayerManager(nsnull)->GetBackendType()) {
case LayerManager::LAYERS_BASIC: {
nsRefPtr<gfxContext> ctx = new gfxContext(targetSurface);
@ -982,7 +1119,7 @@ nsWindow::DrawTo(gfxASurface *targetSurface)
targetSurface->SetDeviceOffset(offset + gfxPoint(mChildren[i]->mBounds.x,
mChildren[i]->mBounds.y));
bool ok = mChildren[i]->DrawTo(targetSurface);
bool ok = mChildren[i]->DrawTo(targetSurface, invalidRect);
if (!ok) {
ALOG("nsWindow[%p]::DrawTo child %d[%p] returned FALSE!", (void*) this, i, (void*)mChildren[i]);
@ -998,11 +1135,6 @@ nsWindow::DrawTo(gfxASurface *targetSurface)
void
nsWindow::OnDraw(AndroidGeckoEvent *ae)
{
if (!sSurfaceExists) {
return;
}
if (!IsTopLevel()) {
ALOG("##### redraw for window %p, which is not a toplevel window -- sending to toplevel!", (void*) this);
DumpWindows();
@ -1016,6 +1148,27 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
}
AndroidBridge::AutoLocalJNIFrame jniFrame;
#ifdef MOZ_JAVA_COMPOSITOR
AndroidGeckoSoftwareLayerClient &client =
AndroidBridge::Bridge()->GetSoftwareLayerClient();
client.BeginDrawing();
unsigned char *bits = client.LockBufferBits();
nsRefPtr<gfxImageSurface> targetSurface =
new gfxImageSurface(bits, gfxIntSize(TILE_WIDTH, TILE_HEIGHT), TILE_WIDTH * 2,
gfxASurface::ImageFormatRGB16_565);
if (targetSurface->CairoStatus()) {
ALOG("### Failed to create a valid surface from the bitmap");
} else {
DrawTo(targetSurface, ae->Rect());
client.UnlockBuffer();
client.EndDrawing(ae->Rect());
}
return;
#endif
if (!sSurfaceExists) {
return;
}
AndroidGeckoSurfaceView& sview(AndroidBridge::Bridge()->SurfaceView());

View File

@ -43,6 +43,10 @@
#include "nsTArray.h"
#ifdef ACCESSIBILITY
#include "nsAccessible.h"
#endif
class gfxASurface;
class nsIdleService;
@ -167,10 +171,17 @@ public:
gfxASurface* GetThebesSurface();
NS_IMETHOD ReparentNativeWidget(nsIWidget* aNewParent);
#ifdef ACCESSIBILITY
static bool sAccessibilityEnabled;
#endif
protected:
void BringToFront();
nsWindow *FindTopLevel();
bool DrawTo(gfxASurface *targetSurface);
bool DrawTo(gfxASurface *targetSurface, const nsIntRect &aRect);
bool DrawToFile(const nsAString &path);
bool IsTopLevel();
void OnIMEAddRange(mozilla::AndroidGeckoEvent *ae);
@ -211,6 +222,22 @@ private:
void DispatchGestureEvent(PRUint32 msg, PRUint32 direction, double delta,
const nsIntPoint &refPoint, PRUint64 time);
void HandleSpecialKey(mozilla::AndroidGeckoEvent *ae);
void RedrawAll();
#ifdef ACCESSIBILITY
nsRefPtr<nsAccessible> mRootAccessible;
/**
* Request to create the accessible for this window if it is top level.
*/
void CreateRootAccessible();
/**
* Generate the NS_GETACCESSIBLE event to get accessible for this window
* and return it.
*/
nsAccessible *DispatchAccessibleEvent();
#endif // ACCESSIBILITY
};
#endif /* NSWINDOW_H_ */

View File

@ -53,6 +53,10 @@
#include "nsConsoleMessage.h"
#include "nsIClassInfoImpl.h"
#if defined(ANDROID)
#include <android/log.h>
#endif
using namespace mozilla;
NS_IMPL_THREADSAFE_ADDREF(nsConsoleService)
@ -135,6 +139,16 @@ nsConsoleService::LogMessage(nsIConsoleMessage *message)
{
MutexAutoLock lock(mLock);
#if defined(ANDROID)
{
nsXPIDLString msg;
message->GetMessageMoz(getter_Copies(msg));
__android_log_print(ANDROID_LOG_ERROR, "Gecko *** Console Service *** ",
"%s",
NS_LossyConvertUTF16toASCII(msg).get());
}
#endif
/*
* If there's already a message in the slot we're about to replace,
* we've wrapped around, and we need to release the old message. We