Merge m-c to m-i.

This commit is contained in:
Ms2ger 2012-01-11 12:15:35 +01:00
commit 096afeb104
120 changed files with 4978 additions and 1638 deletions

View File

@ -90,6 +90,11 @@ if [ ! "$LIBXUL_SDK" ]; then
mozglue/android/Makefile
"
fi
if [ "$MOZ_LINKER" ]; then
add_makefiles "
mozglue/linker/Makefile
"
fi
fi
if [ "$OS_ARCH" = "WINNT" ]; then

View File

@ -47,7 +47,7 @@ pref("browser.homescreenURL", "file:///data/local/homescreen.html,file:///system
#endif
// URL for the dialer application.
pref("dom.telephony.app.phone.url", "http://localhost:7777/dialer/dialer.html");
pref("dom.telephony.app.phone.url", "http://localhost:6666/apps/dialer/dialer.html");
// Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.
pref("browser.viewport.scaleRatio", -1);

View File

@ -83,7 +83,7 @@ pref("extensions.update.autoUpdateDefault", true);
pref("extensions.hotfix.id", "firefox-hotfix@mozilla.org");
pref("extensions.hotfix.cert.checkAttributes", true);
pref("extensions.hotfix.certs.1.sha1Fingerprint", "foo");
pref("extensions.hotfix.certs.1.sha1Fingerprint", "F1:DB:F9:6A:7B:B8:04:FA:48:3C:16:95:C7:2F:17:C6:5B:C2:9F:45");
// Disable add-ons installed into the shared user and shared system areas by
// default. This does not include the application directory. See the SCOPE

View File

@ -339,6 +339,9 @@ var MigrationWizard = {
case "chrome":
source = "sourceNameChrome";
break;
case "firefox":
source = "sourceNameFirefox";
break;
}
// semi-wallpaper for crash when multiple profiles exist, since we haven't initialized mSourceProfile in places

View File

@ -76,6 +76,7 @@
#endif
#endif
<radio id="chrome" label="&importFromChrome.label;" accesskey="&importFromChrome.accesskey;"/>
<radio id="firefox" label="&importFromFirefox.label;" accesskey="&importFromFirefox.accesskey;"/>
<radio id="fromfile" label="&importFromHTMLFile.label;" accesskey="&importFromHTMLFile.accesskey;" hidden="true"/>
<radio id="nothing" label="&importFromNothing.label;" accesskey="&importFromNothing.accesskey;" hidden="true"/>
</radiogroup>

View File

@ -1,2 +1,4 @@
component {4cec1de4-1671-4fc3-a53e-6c539dc77a26} ChromeProfileMigrator.js
contract @mozilla.org/profile/migrator;1?app=browser&type=chrome {4cec1de4-1671-4fc3-a53e-6c539dc77a26}
component {91185366-ba97-4438-acba-48deaca63386} FirefoxProfileMigrator.js
contract @mozilla.org/profile/migrator;1?app=browser&type=firefox {91185366-ba97-4438-acba-48deaca63386}

View File

@ -40,21 +40,13 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
const MIGRATOR = Ci.nsIBrowserProfileMigrator;
const LOCAL_FILE_CID = "@mozilla.org/file/local;1";
const FILE_INPUT_STREAM_CID = "@mozilla.org/network/file-input-stream;1";
const BUNDLE_MIGRATION = "chrome://browser/locale/migration/migration.properties";
const MIGRATE_ALL = 0x0000;
const MIGRATE_SETTINGS = 0x0001;
const MIGRATE_COOKIES = 0x0002;
const MIGRATE_HISTORY = 0x0004;
const MIGRATE_FORMDATA = 0x0008;
const MIGRATE_PASSWORDS = 0x0010;
const MIGRATE_BOOKMARKS = 0x0020;
const MIGRATE_OTHERDATA = 0x0040;
const S100NS_FROM1601TO1970 = 0x19DB1DED53E8000;
const S100NS_PER_MS = 10;
@ -142,7 +134,7 @@ ChromeProfileMigrator.prototype = {
* Notify to observers to start migration
*
* @param aType
* notification type such as MIGRATE_BOOKMARKS
* notification type such as MIGRATOR.BOOKMARKS
*/
_notifyStart : function Chrome_notifyStart(aType)
{
@ -154,7 +146,7 @@ ChromeProfileMigrator.prototype = {
* Notify observers that a migration error occured with an item
*
* @param aType
* notification type such as MIGRATE_BOOKMARKS
* notification type such as MIGRATOR.BOOKMARKS
*/
_notifyError : function Chrome_notifyError(aType)
{
@ -166,7 +158,7 @@ ChromeProfileMigrator.prototype = {
* If all items are finished, it sends migration end notification.
*
* @param aType
* notification type such as MIGRATE_BOOKMARKS
* notification type such as MIGRATOR.BOOKMARKS
*/
_notifyCompleted : function Chrome_notifyIfCompleted(aType)
{
@ -182,7 +174,7 @@ ChromeProfileMigrator.prototype = {
*/
_migrateBookmarks : function Chrome_migrateBookmarks()
{
this._notifyStart(MIGRATE_BOOKMARKS);
this._notifyStart(MIGRATOR.BOOKMARKS);
try {
PlacesUtils.bookmarks.runInBatchMode({
@ -194,7 +186,7 @@ ChromeProfileMigrator.prototype = {
NetUtil.asyncFetch(file, function(aInputStream, aResultCode) {
if (!Components.isSuccessCode(aResultCode)) {
migrator._notifyCompleted(MIGRATE_BOOKMARKS);
migrator._notifyCompleted(MIGRATOR.BOOKMARKS);
return;
}
@ -232,14 +224,14 @@ ChromeProfileMigrator.prototype = {
insertBookmarkItems(parentId, roots.other.children);
}
migrator._notifyCompleted(MIGRATE_BOOKMARKS);
migrator._notifyCompleted(MIGRATOR.BOOKMARKS);
});
}
}, null);
} catch (e) {
Cu.reportError(e);
this._notifyError(MIGRATE_BOOKMARKS);
this._notifyCompleted(MIGRATE_BOOKMARKS);
this._notifyError(MIGRATOR.BOOKMARKS);
this._notifyCompleted(MIGRATOR.BOOKMARKS);
}
},
@ -248,7 +240,7 @@ ChromeProfileMigrator.prototype = {
*/
_migrateHistory : function Chrome_migrateHistory()
{
this._notifyStart(MIGRATE_HISTORY);
this._notifyStart(MIGRATOR.HISTORY);
try {
PlacesUtils.history.runInBatchMode({
@ -305,7 +297,7 @@ ChromeProfileMigrator.prototype = {
handleCompletion : function(aReason) {
this._db.asyncClose();
this._self._notifyCompleted(MIGRATE_HISTORY);
this._self._notifyCompleted(MIGRATOR.HISTORY);
}
});
stmt.finalize();
@ -313,8 +305,8 @@ ChromeProfileMigrator.prototype = {
}, null);
} catch (e) {
Cu.reportError(e);
this._notifyError(MIGRATE_HISTORY);
this._notifyCompleted(MIGRATE_HISTORY);
this._notifyError(MIGRATOR.HISTORY);
this._notifyCompleted(MIGRATOR.HISTORY);
}
},
@ -323,7 +315,7 @@ ChromeProfileMigrator.prototype = {
*/
_migrateCookies : function Chrome_migrateCookies()
{
this._notifyStart(MIGRATE_COOKIES);
this._notifyStart(MIGRATOR.COOKIES);
try {
// Access sqlite3 database of Chrome's cookie
@ -369,14 +361,14 @@ ChromeProfileMigrator.prototype = {
handleCompletion : function(aReason) {
this._db.asyncClose();
this._self._notifyCompleted(MIGRATE_COOKIES);
this._self._notifyCompleted(MIGRATOR.COOKIES);
},
});
stmt.finalize();
} catch (e) {
Cu.reportError(e);
this._notifyError(MIGRATE_COOKIES);
this._notifyCompleted(MIGRATE_COOKIES);
this._notifyError(MIGRATOR.COOKIES);
this._notifyCompleted(MIGRATOR.COOKIES);
}
},
@ -409,13 +401,13 @@ ChromeProfileMigrator.prototype = {
// notification is sent
this._pendingCount = 1;
if (aItems & MIGRATE_HISTORY)
if (aItems & MIGRATOR.HISTORY)
this._migrateHistory();
if (aItems & MIGRATE_COOKIES)
if (aItems & MIGRATOR.COOKIES)
this._migrateCookies();
if (aItems & MIGRATE_BOOKMARKS)
if (aItems & MIGRATOR.BOOKMARKS)
this._migrateBookmarks();
if (--this._pendingCount == 0) {
@ -452,7 +444,7 @@ ChromeProfileMigrator.prototype = {
file.append("Bookmarks");
if (file.exists()) {
this._paths.bookmarks = file.path;
result += MIGRATE_BOOKMARKS;
result += MIGRATOR.BOOKMARKS;
}
} catch (e) {
Cu.reportError(e);
@ -471,7 +463,7 @@ ChromeProfileMigrator.prototype = {
file.append("History");
if (file.exists()) {
this._paths.history = file.path;
result += MIGRATE_HISTORY;
result += MIGRATOR.HISTORY;
}
} catch (e) {
Cu.reportError(e);
@ -482,7 +474,7 @@ ChromeProfileMigrator.prototype = {
file.append("Cookies");
if (file.exists()) {
this._paths.cookies = file.path;
result += MIGRATE_COOKIES;
result += MIGRATOR.COOKIES;
}
} catch (e) {
Cu.reportError(e);

View File

@ -0,0 +1,404 @@
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 et
* This Source Code is subject to the terms of the Mozilla Public License
* version 2.0 (the "License"). You can obtain a copy of the License at
* http://mozilla.org/MPL/2.0/. */
/*
* Migrates from a Firefox profile in a lossy manner in order to clean up a user's profile. Data
* is only migrated where the benefits outweigh the potential problems caused by importing
* undesired/invalid configurations from the source profile.
*/
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
const MIGRATOR = Ci.nsIBrowserProfileMigrator;
const LOCAL_FILE_CID = "@mozilla.org/file/local;1";
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
Components.utils.import("resource://gre/modules/NetUtil.jsm");
Components.utils.import("resource://gre/modules/FileUtils.jsm");
function FirefoxProfileMigrator()
{
// profD is not available when the migrator is run during startup so use ProfDS.
this._paths.currentProfile = FileUtils.getDir("ProfDS", []);
}
FirefoxProfileMigrator.prototype = {
_paths: {
bookmarks : null,
cookies : null,
currentProfile: null, // The currently running (destination) profile.
encryptionKey: null,
history : null,
passwords: null,
},
_homepageURL : null,
_replaceBookmarks : false,
_sourceProfile: null,
_profilesCache: null,
/**
* Notify to observers to start migration
*
* @param aType
* notification type such as MIGRATOR.BOOKMARKS
*/
_notifyStart : function Firefox_notifyStart(aType)
{
Services.obs.notifyObservers(null, "Migration:ItemBeforeMigrate", aType);
this._pendingCount++;
},
/**
* Notify observers that a migration error occured with an item
*
* @param aType
* notification type such as MIGRATOR.BOOKMARKS
*/
_notifyError : function Firefox_notifyError(aType)
{
Services.obs.notifyObservers(null, "Migration:ItemError", aType);
},
/**
* Notify to observers to finish migration for item
* If all items are finished, it sends migration end notification.
*
* @param aType
* notification type such as MIGRATOR.BOOKMARKS
*/
_notifyCompleted : function Firefox_notifyIfCompleted(aType)
{
Services.obs.notifyObservers(null, "Migration:ItemAfterMigrate", aType);
if (--this._pendingCount == 0) {
// All items are migrated, so we have to send end notification.
Services.obs.notifyObservers(null, "Migration:Ended", null);
}
},
/**
* The directory used for bookmark backups
* @return directory of the bookmark backups
*/
get _bookmarks_backup_folder()
{
let bookmarksBackupRelativePath = PlacesUtils.backups.profileRelativeFolderPath;
let bookmarksBackupDir = this._sourceProfile.clone().QueryInterface(Ci.nsILocalFile);
bookmarksBackupDir.appendRelativePath(bookmarksBackupRelativePath);
return bookmarksBackupDir;
},
/**
* Migrating bookmark items
*/
_migrateBookmarks : function Firefox_migrateBookmarks()
{
this._notifyStart(MIGRATOR.BOOKMARKS);
try {
let srcBackupsDir = this._bookmarks_backup_folder;
let backupFolder = this._paths.currentProfile.clone();
backupFolder.append(srcBackupsDir.leafName);
if (!backupFolder.exists())
srcBackupsDir.copyTo(this._paths.currentProfile, null);
} catch (e) {
Cu.reportError(e);
// Don't notify about backup migration errors since actual bookmarks are
// migrated with history.
} finally {
this._notifyCompleted(MIGRATOR.BOOKMARKS);
}
},
/**
* Migrating history
*/
_migrateHistory : function Firefox_migrateHistory()
{
this._notifyStart(MIGRATOR.HISTORY);
try {
// access sqlite3 database of history
let file = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
file.initWithPath(this._paths.history);
file.copyTo(this._paths.currentProfile, null);
} catch (e) {
Cu.reportError(e);
this._notifyError(MIGRATOR.HISTORY);
} finally {
this._notifyCompleted(MIGRATOR.HISTORY);
}
},
/**
* Migrating cookies
*
* @note Cookie permissions are not migrated since they may be inadvertently set leading to
* website login problems.
*/
_migrateCookies : function Firefox_migrateCookies()
{
this._notifyStart(MIGRATOR.COOKIES);
try {
// Access sqlite3 database of cookies
let file = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
file.initWithPath(this._paths.cookies);
file.copyTo(this._paths.currentProfile, null);
} catch (e) {
Cu.reportError(e);
this._notifyError(MIGRATOR.COOKIES);
} finally {
this._notifyCompleted(MIGRATOR.COOKIES);
}
},
/**
* Migrating passwords
*/
_migratePasswords : function Firefox_migratePasswords()
{
this._notifyStart(MIGRATOR.PASSWORDS);
try {
// Access sqlite3 database of passwords
let file = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
file.initWithPath(this._paths.passwords);
file.copyTo(this._paths.currentProfile, null);
let encryptionKey = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
encryptionKey.initWithPath(this._paths.encryptionKey);
encryptionKey.copyTo(this._paths.currentProfile, null);
} catch (e) {
Cu.reportError(e);
this._notifyError(MIGRATOR.PASSWORDS);
} finally {
this._notifyCompleted(MIGRATOR.PASSWORDS);
}
},
/**
* nsIBrowserProfileMigrator interface implementation
*/
/**
* Let's migrate all items
*
* @param aItems
* list of data items to migrate.
* @param aStartup
* non-null if called during startup.
* @param aProfile
* profile directory path to migrate from
*/
migrate : function Firefox_migrate(aItems, aStartup, aProfile)
{
if (aStartup) {
aStartup.doStartup();
this._replaceBookmarks = true;
}
Services.obs.notifyObservers(null, "Migration:Started", null);
// Reset pending count. If this count becomes 0, "Migration:Ended"
// notification is sent
this._pendingCount = 1;
if (aItems & MIGRATOR.HISTORY)
this._migrateHistory();
if (aItems & MIGRATOR.COOKIES)
this._migrateCookies();
if (aItems & MIGRATOR.BOOKMARKS)
this._migrateBookmarks();
if (aItems & MIGRATOR.PASSWORDS)
this._migratePasswords();
if (--this._pendingCount == 0) {
// When async imports are immediately completed unfortunately,
// this will be called.
// Usually, this notification is sent by _notifyCompleted()
Services.obs.notifyObservers(null, "Migration:Ended", null);
}
},
/**
* return supported migration types
*
* @param aProfile
* the profile path to migrate from
* @param aDoingStartup
* non-null if called during startup.
* @return supported migration types
*
* @todo Bug 715315 - make sure source databases are not in-use
*/
getMigrateData: function Firefox_getMigrateData(aProfile, aDoingStartup)
{
this._sourceProfile = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
this._sourceProfile.initWithPath(aProfile);
let result = 0;
if (!this._sourceProfile.exists() || !this._sourceProfile.isReadable()) {
Cu.reportError("source profile directory doesn't exist or is not readable");
return result;
}
// Migration initiated from the UI is not supported.
if (!aDoingStartup)
return result;
// Bookmark backups in JSON format
try {
let file = this._bookmarks_backup_folder;
if (file.exists()) {
this._paths.bookmarks = file.path;
result += MIGRATOR.BOOKMARKS;
}
} catch (e) {
Cu.reportError(e);
}
// Bookmarks, history and favicons
try {
let file = this._sourceProfile.clone();
file.append("places.sqlite");
if (file.exists()) {
this._paths.history = file.path;
result += MIGRATOR.HISTORY;
result |= MIGRATOR.BOOKMARKS;
}
} catch (e) {
Cu.reportError(e);
}
// Cookies
try {
let file = this._sourceProfile.clone();
file.append("cookies.sqlite");
if (file.exists()) {
this._paths.cookies = file.path;
result += MIGRATOR.COOKIES;
}
} catch (e) {
Cu.reportError(e);
}
// Passwords & encryption key
try {
let passwords = this._sourceProfile.clone();
passwords.append("signons.sqlite");
let encryptionKey = this._sourceProfile.clone();
encryptionKey.append("key3.db");
if (passwords.exists() && encryptionKey.exists()) {
this._paths.passwords = passwords.path;
this._paths.encryptionKey = encryptionKey.path;
result += MIGRATOR.PASSWORDS;
}
} catch (e) {
Cu.reportError(e);
}
return result;
},
/**
* Whether we support migration of Firefox
*
* @return true if supported
*/
get sourceExists()
{
let userData = Services.dirsvc.get("DefProfRt", Ci.nsIFile).path;
let result = 0;
try {
let userDataDir = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
userDataDir.initWithPath(userData);
if (!userDataDir.exists() || !userDataDir.isReadable())
return false;
let profiles = this.sourceProfiles;
if (profiles.length < 1)
return false;
// Check that we can get data from at least one profile since profile selection has not
// happened yet.
for (let i = 0; i < profiles.length; i++) {
result = this.getMigrateData(profiles.queryElementAt(i, Ci.nsISupportsString), true);
if (result)
break;
}
} catch (e) {
Cu.reportError(e);
}
return result > 0;
},
get sourceHasMultipleProfiles()
{
return this.sourceProfiles.length > 1;
},
get sourceProfiles()
{
try
{
if (!this._profilesCache)
{
this._profilesCache = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
let profileService = Cc["@mozilla.org/toolkit/profile-service;1"]
.getService(Ci.nsIToolkitProfileService);
// Only allow migrating from the default (selected) profile since this will be the only one
// returned by the toolkit profile service after bug 214675 has landed.
var profile = profileService.selectedProfile;
if (profile.rootDir.path === this._paths.currentProfile.path)
return null;
let str = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
str.data = profile.rootDir.path;
this._profilesCache.appendElement(str, false);
}
} catch (e) {
Cu.reportError("Error detecting Firefox profiles: " + e);
}
return this._profilesCache;
},
/**
* Return home page URL
*
* @return home page URL
*
* @todo Bug 715348 will migrate preferences such as the homepage
*/
get sourceHomePageURL()
{
try {
if (this._homepageURL)
return this._homepageURL;
} catch (e) {
Cu.reportError(e);
}
return "";
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIBrowserProfileMigrator
]),
classDescription: "Firefox Profile Migrator",
contractID: "@mozilla.org/profile/migrator;1?app=browser&type=firefox",
classID: Components.ID("{91185366-ba97-4438-acba-48deaca63386}")
};
const NSGetFactory = XPCOMUtils.generateNSGetFactory([FirefoxProfileMigrator]);

View File

@ -65,6 +65,7 @@ endif
EXTRA_PP_COMPONENTS = \
ChromeProfileMigrator.js \
FirefoxProfileMigrator.js \
$(NULL)
EXTRA_COMPONENTS = \

View File

@ -140,6 +140,7 @@ NS_IMPL_ISUPPORTS1(nsProfileMigrator, nsIProfileMigrator)
#define INTERNAL_NAME_IEXPLORE "iexplore"
#define INTERNAL_NAME_MOZILLA_SUITE "apprunner"
#define INTERNAL_NAME_CHROME "chrome"
#define INTERNAL_NAME_FIREFOX "firefox"
#endif
nsresult
@ -223,6 +224,10 @@ nsProfileMigrator::GetDefaultBrowserMigratorKey(nsACString& aKey,
aKey = "chrome";
return NS_OK;
}
else if (internalName.LowerCaseEqualsLiteral(INTERNAL_NAME_FIREFOX)) {
aKey = "firefox";
return NS_OK;
}
#else
bool exists = false;
@ -239,6 +244,7 @@ nsProfileMigrator::GetDefaultBrowserMigratorKey(nsACString& aKey,
CHECK_MIGRATOR("safari");
#endif
CHECK_MIGRATOR("chrome");
CHECK_MIGRATOR("firefox");
#undef CHECK_MIGRATOR
#endif

View File

@ -8,6 +8,7 @@ this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/Chr
function test() {
waitForExplicitFinish();
var searchEntries = ["test", "More Text", "Some Text"];
var searchBar = BrowserSearch.searchBar;
var searchButton = document.getAnonymousElementByAttribute(searchBar,
"anonid", "search-go-button");
@ -166,7 +167,7 @@ function test() {
is(searchBar.value, "More Text", "drop text/uri-list on searchbar");
SimpleTest.executeSoon(testRightClick);
}
function testRightClick() {
init();
searchBar.removeEventListener("popupshowing", stopPopup, true);
@ -177,10 +178,29 @@ function test() {
is(gBrowser.tabs.length, preTabNo, "RightClick did not open new tab");
is(gBrowser.currentURI.spec, "about:blank", "RightClick did nothing");
finalize();
testSearchHistory();
}, 5000);
}
function testSearchHistory() {
var textbox = searchBar._textbox;
for (var i = 0; i < searchEntries.length; i++) {
let exists = textbox._formHistSvc.entryExists(textbox.searchParam, searchEntries[i]);
ok(exists, "form history entry '" + searchEntries[i] + "' should exist");
}
testAutocomplete();
}
function testAutocomplete() {
var popup = searchBar.textbox.popup;
popup.addEventListener("popupshowing", function() {
checkMenuEntries(searchEntries);
finalize();
popup.removeEventListener("popupshowing", this, false);
}, false);
searchBar.textbox.showHistoryPopup();
}
function finalize() {
searchBar.value = "";
while (gBrowser.tabs.length != 1) {
@ -211,5 +231,26 @@ function test() {
buttonArg, null);
aTarget.dispatchEvent(event);
}
// modified from toolkit/components/satchel/test/test_form_autocomplete.html
function checkMenuEntries(expectedValues) {
var actualValues = getMenuEntries();
is(actualValues.length, expectedValues.length, "Checking length of expected menu");
for (var i = 0; i < expectedValues.length; i++)
is(actualValues[i], expectedValues[i], "Checking menu entry #"+i);
}
function getMenuEntries() {
var entries = [];
var autocompleteMenu = searchBar.textbox.popup;
// Could perhaps pull values directly from the controller, but it seems
// more reliable to test the values that are actually in the tree?
var column = autocompleteMenu.tree.columns[0];
var numRows = autocompleteMenu.tree.view.rowCount;
for (var i = 0; i < numRows; i++) {
entries.push(autocompleteMenu.tree.view.getValueAt(i, column));
}
return entries;
}
}

View File

@ -96,8 +96,6 @@ static const MimeTypeAssociation appTypes[] = {
{ "application/xhtml+xml", "xhtml xht" }
};
static const char kDocumentIconPath[] = "firefox-document.png";
// GConf registry key constants
#define DG_BACKGROUND "/desktop/gnome/background"

View File

@ -372,6 +372,7 @@
#endif
@BINPATH@/components/BrowserProfileMigrators.manifest
@BINPATH@/components/ChromeProfileMigrator.js
@BINPATH@/components/FirefoxProfileMigrator.js
#ifdef XP_MACOSX
@BINPATH@/components/libalerts_s.dylib
#endif

View File

@ -13,6 +13,8 @@
<!ENTITY importFromSafari.accesskey "S">
<!ENTITY importFromChrome.label "Chrome">
<!ENTITY importFromChrome.accesskey "C">
<!ENTITY importFromFirefox.label "Firefox">
<!ENTITY importFromFirefox.accesskey "X">
<!ENTITY importFromHTMLFile.label "From an HTML File">
<!ENTITY importFromHTMLFile.accesskey "F">

View File

@ -4,6 +4,7 @@ profileName_format=%S %S
sourceNameIE=Internet Explorer
sourceNameSafari=Safari
sourceNameChrome=Google Chrome
sourceNameFirefox=Mozilla Firefox
importedBookmarksFolder=From %S
importedSearchURLsFolder=Keyword Searches (From %S)
@ -20,10 +21,12 @@ importedSafariBookmarks=From Safari
2_ie=Cookies
2_safari=Cookies
2_chrome=Cookies
2_firefox=Cookies
4_ie=Browsing History
4_safari=Browsing History
4_chrome=Browsing History
4_firefox=Browsing History
8_ie=Saved Form History
8_safari=Saved Form History
@ -32,10 +35,12 @@ importedSafariBookmarks=From Safari
16_ie=Saved Passwords
16_safari=Saved Passwords
16_chrome=Saved Passwords
16_firefox=Saved Passwords
32_ie=Favorites
32_safari=Bookmarks
32_chrome=Bookmarks
32_firefox=Bookmarks
64_ie=Other Data
64_safari=Other Data

View File

@ -121,6 +121,8 @@ MOZ_ETW = @MOZ_ETW@
MOZ_TRACE_JSCALLS = @MOZ_TRACE_JSCALLS@
DEHYDRA_PATH = @DEHYDRA_PATH@
MOZ_LINKER = @MOZ_LINKER@
MOZ_OLD_LINKER = @MOZ_OLD_LINKER@
NS_TRACE_MALLOC = @NS_TRACE_MALLOC@
USE_ELF_DYNSTR_GC = @USE_ELF_DYNSTR_GC@
USE_ELF_HACK = @USE_ELF_HACK@

View File

@ -2465,6 +2465,7 @@ ia64*-hpux*)
_PLATFORM_DEFAULT_TOOLKIT=cairo-gonk
else
_PLATFORM_DEFAULT_TOOLKIT=cairo-android
MOZ_LINKER=1
fi
TARGET_NSPR_MDCPUCFG='\"md/_linux.cfg\"'
@ -2899,6 +2900,8 @@ ia64*-hpux*)
esac
AC_SUBST(MOZ_LINKER)
dnl Only one oddball right now (QNX), but this gives us flexibility
dnl if any other platforms need to override this in the future.
AC_DEFINE_UNQUOTED(D_INO,$DIRENT_INO)
@ -4059,7 +4062,8 @@ AC_CACHE_CHECK(for __thread keyword for TLS variables,
ac_cv_thread_keyword=yes,
ac_cv_thread_keyword=no)])
LDFLAGS=$_SAVE_LDFLAGS
if test "$ac_cv_thread_keyword" = yes; then
# The custom dynamic linker doesn't support TLS variables
if test "$ac_cv_thread_keyword" = yes -a "$MOZ_LINKER" != 1; then
# mips builds fail with TLS variables because of a binutils bug.
# See bug 528687
case "${target}" in
@ -4225,7 +4229,7 @@ MOZ_ARG_WITH_BOOL(system-nspr,
_USE_SYSTEM_NSPR=1 )
if test -n "$_USE_SYSTEM_NSPR"; then
AM_PATH_NSPR(4.8.8, [MOZ_NATIVE_NSPR=1], [AC_MSG_ERROR([your don't have NSPR installed or your version is too old])])
AM_PATH_NSPR(4.9.0, [MOZ_NATIVE_NSPR=1], [AC_MSG_ERROR([your don't have NSPR installed or your version is too old])])
fi
if test -n "$MOZ_NATIVE_NSPR"; then
@ -4233,10 +4237,16 @@ if test -n "$MOZ_NATIVE_NSPR"; then
CFLAGS="$CFLAGS $NSPR_CFLAGS"
AC_TRY_COMPILE([#include "prtypes.h"],
[#ifndef PR_STATIC_ASSERT
#error PR_STATIC_ASSERT not defined or requires including prlog.h
#error PR_STATIC_ASSERT not defined or requires including prtypes.h
#endif],
[MOZ_NATIVE_NSPR=1],
AC_MSG_ERROR([system NSPR does not support PR_STATIC_ASSERT or including prtypes.h does not provide it]))
AC_TRY_COMPILE([#include "prtypes.h"],
[#ifndef PR_UINT64
#error PR_UINT64 not defined or requires including prtypes.h
#endif],
[MOZ_NATIVE_NSPR=1],
AC_MSG_ERROR([system NSPR does not support PR_UINT64 or including prtypes.h does not provide it]))
CFLAGS=$_SAVE_CFLAGS
else
if test "$OS_ARCH" = "WINNT"; then
@ -4426,6 +4436,10 @@ if test "${ZLIB_DIR}" -a -d "${ZLIB_DIR}" -a "$SYSTEM_ZLIB" = 1; then
ZLIB_LIBS="-L${ZLIB_DIR}/lib ${ZLIB_LIBS}"
fi
if test "$MOZ_LINKER" = 1 -a "$SYSTEM_ZLIB" != 1; then
AC_MSG_ERROR([Custom dynamic linker requires --with-system-zlib])
fi
dnl system BZIP2 Support
dnl ========================================================
MOZ_ARG_WITH_STRING(system-bz2,
@ -4888,6 +4902,7 @@ cairo-android)
MOZ_WEBGL=1
MOZ_PDF_PRINTING=1
MOZ_INSTRUMENT_EVENT_LOOP=1
MOZ_OLD_LINKER=1
;;
cairo-gonk)
@ -4902,6 +4917,7 @@ cairo-gonk)
esac
AC_SUBST(MOZ_OLD_LINKER)
AC_SUBST(MOZ_PDF_PRINTING)
if test "$MOZ_PDF_PRINTING"; then
PDF_SURFACE_FEATURE="#define CAIRO_HAS_PDF_SURFACE 1"
@ -7230,7 +7246,10 @@ dnl our own linker.
if test "$OS_TARGET" = Android; then
WRAP_LDFLAGS="${WRAP_LDFLAGS} -L$_objdir/dist/lib -lmozglue"
if test "$MOZ_WIDGET_TOOLKIT" = android; then
WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=dlopen,--wrap=dlclose,--wrap=dlerror,--wrap=dlsym,--wrap=dladdr,--wrap=getaddrinfo,--wrap=freeaddrinfo,--wrap=gai_strerror"
if test -n "$MOZ_OLD_LINKER"; then
WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=dlopen,--wrap=dlclose,--wrap=dlerror,--wrap=dlsym,--wrap=dladdr"
fi
WRAP_LDFLAGS="${WRAP_LDFLAGS} --wrap=getaddrinfo,--wrap=freeaddrinfo,--wrap=gai_strerror"
fi
fi

View File

@ -409,6 +409,30 @@ nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
}
}
nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
nsIPrincipal* aRequestingPrincipal,
nsIChannel* aChannel,
bool aWithCredentials,
bool aAllowDataURI,
nsresult* aResult)
: mOuterListener(aOuter),
mRequestingPrincipal(aRequestingPrincipal),
mWithCredentials(aWithCredentials && !gDisableCORSPrivateData),
mRequestApproved(false),
mHasBeenCrossSite(false),
mIsPreflight(false)
{
aChannel->GetNotificationCallbacks(getter_AddRefs(mOuterNotificationCallbacks));
aChannel->SetNotificationCallbacks(this);
*aResult = UpdateChannel(aChannel, aAllowDataURI);
if (NS_FAILED(*aResult)) {
mOuterListener = nsnull;
mRequestingPrincipal = nsnull;
mOuterNotificationCallbacks = nsnull;
}
}
nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
nsIPrincipal* aRequestingPrincipal,
nsIChannel* aChannel,
@ -723,13 +747,23 @@ nsCORSListenerProxy::OnRedirectVerifyCallback(nsresult result)
}
nsresult
nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel)
nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel, bool aAllowDataURI)
{
nsCOMPtr<nsIURI> uri, originalURI;
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
rv = aChannel->GetOriginalURI(getter_AddRefs(originalURI));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_SUCCESS(rv, rv);
// exempt data URIs from the same origin check.
if (aAllowDataURI && originalURI == uri) {
bool dataScheme = false;
rv = uri->SchemeIs("data", &dataScheme);
NS_ENSURE_SUCCESS(rv, rv);
if (dataScheme) {
return NS_OK;
}
}
// Check that the uri is ok to load
rv = nsContentUtils::GetSecurityManager()->

View File

@ -74,6 +74,12 @@ public:
nsIChannel* aChannel,
bool aWithCredentials,
nsresult* aResult);
nsCORSListenerProxy(nsIStreamListener* aOuter,
nsIPrincipal* aRequestingPrincipal,
nsIChannel* aChannel,
bool aWithCredentials,
bool aAllowDataURI,
nsresult* aResult);
nsCORSListenerProxy(nsIStreamListener* aOuter,
nsIPrincipal* aRequestingPrincipal,
nsIChannel* aChannel,
@ -95,7 +101,7 @@ public:
static void Shutdown();
private:
nsresult UpdateChannel(nsIChannel* aChannel);
nsresult UpdateChannel(nsIChannel* aChannel, bool aAllowDataURI = false);
nsresult CheckRequestApproved(nsIRequest* aRequest);
nsCOMPtr<nsIStreamListener> mOuterListener;

View File

@ -1537,9 +1537,7 @@ GK_ATOM(lspace_, "lspace")
GK_ATOM(lt_, "lt")
GK_ATOM(maction_, "maction")
GK_ATOM(maligngroup_, "maligngroup")
GK_ATOM(malign_, "malign")
GK_ATOM(malignmark_, "malignmark")
GK_ATOM(malignscope_, "malignscope")
GK_ATOM(mathbackground_, "mathbackground")
GK_ATOM(mathcolor_, "mathcolor")
GK_ATOM(mathsize_, "mathsize")
@ -1553,7 +1551,6 @@ GK_ATOM(menclose_, "menclose")
GK_ATOM(merror_, "merror")
GK_ATOM(mfenced_, "mfenced")
GK_ATOM(mfrac_, "mfrac")
GK_ATOM(mfraction_, "mfraction")
GK_ATOM(mglyph_, "mglyph")
GK_ATOM(mi_, "mi")
GK_ATOM(minlabelspacing_, "minlabelspacing")
@ -1566,7 +1563,6 @@ GK_ATOM(mn_, "mn")
GK_ATOM(momentabout_, "momentabout")
GK_ATOM(moment_, "moment")
GK_ATOM(mo_, "mo")
GK_ATOM(monospaced_, "monospaced")
GK_ATOM(movablelimits_, "movablelimits")
GK_ATOM(mover_, "mover")
GK_ATOM(mpadded_, "mpadded")

View File

@ -738,10 +738,8 @@ nsIAtom** const kElementsMathML[] = {
&nsGkAtoms::lowlimit_, // lowlimit
&nsGkAtoms::lt_, // lt
&nsGkAtoms::maction_, // maction
&nsGkAtoms::malign_, // malign
&nsGkAtoms::maligngroup_, // maligngroup
&nsGkAtoms::malignmark_, // malignmark
&nsGkAtoms::malignscope_, // malignscope
&nsGkAtoms::math, // math
&nsGkAtoms::matrix, // matrix
&nsGkAtoms::matrixrow_, // matrixrow
@ -752,7 +750,6 @@ nsIAtom** const kElementsMathML[] = {
&nsGkAtoms::merror_, // merror
&nsGkAtoms::mfenced_, // mfenced
&nsGkAtoms::mfrac_, // mfrac
&nsGkAtoms::mfraction_, // mfraction
&nsGkAtoms::mglyph_, // mglyph
&nsGkAtoms::mi_, // mi
&nsGkAtoms::min, // min
@ -924,7 +921,6 @@ nsIAtom** const kAttributesMathML[] = {
&nsGkAtoms::mediummathspace_, // mediummathspace
&nsGkAtoms::minlabelspacing_, // minlabelspacing
&nsGkAtoms::minsize_, // minsize
&nsGkAtoms::monospaced_, // monospaced
&nsGkAtoms::movablelimits_, // movablelimits
&nsGkAtoms::msgroup_, // msgroup
&nsGkAtoms::name, // name

View File

@ -1459,13 +1459,23 @@ nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
return NS_OK;
}
// exempt data URIs from the same origin check.
nsCOMPtr<nsIURI> channelURI;
bool dataScheme = false;
if (NS_SUCCEEDED(NS_GetFinalChannelURI(aChannel,
getter_AddRefs(channelURI))) &&
NS_SUCCEEDED(channelURI->SchemeIs("data", &dataScheme)) &&
dataScheme) {
return NS_OK;
}
// This is a cross-site request
mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
// Check if we need to do a preflight request.
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
NS_ENSURE_TRUE(httpChannel, NS_ERROR_DOM_BAD_URI);
nsCAutoString method;
httpChannel->GetRequestMethod(method);
if (!mCORSUnsafeHeaders.IsEmpty() ||
@ -2587,7 +2597,7 @@ nsXMLHttpRequest::Send(nsIVariant *aBody)
// Always create a nsCORSListenerProxy here even if it's
// a same-origin request right now, since it could be redirected.
listener = new nsCORSListenerProxy(listener, mPrincipal, mChannel,
withCredentials, &rv);
withCredentials, true, &rv);
NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_SUCCESS(rv, rv);
}

View File

@ -21,9 +21,11 @@ function runTests() {
var path = "/tests/content/base/test/";
var passFiles = [['file_XHR_pass1.xml', 'GET'],
['file_XHR_pass2.txt', 'GET'],
['file_XHR_pass3.txt', 'GET'],
var passFiles = [['file_XHR_pass1.xml', 'GET', 200],
['file_XHR_pass2.txt', 'GET', 200],
['file_XHR_pass3.txt', 'GET', 200],
['data:text/xml,%3Cres%3Ehello%3C/res%3E%0A', 'GET', 0],
['data:text/plain,hello%20pass%0A', 'GET', 0],
];
var failFiles = [['//example.com' + path + 'file_XHR_pass1.xml', 'GET'],
@ -36,7 +38,7 @@ for (i = 0; i < passFiles.length; ++i) {
is(xhr.responseType, "", "wrong initial responseType");
xhr.open(passFiles[i][1], passFiles[i][0], false);
xhr.send(null);
is(xhr.status, 200, "wrong status");
is(xhr.status, passFiles[i][2], "wrong status");
if (xhr.responseXML) {
is((new XMLSerializer()).serializeToString(xhr.responseXML.documentElement),
"<res>hello</res>",

View File

@ -1624,16 +1624,16 @@ nsGenericHTMLElement::RestoreFormControlState(nsGenericHTMLElement* aContent,
{
nsCOMPtr<nsILayoutHistoryState> history;
nsCAutoString key;
nsresult rv = GetLayoutHistoryAndKey(aContent, true,
getter_AddRefs(history), key);
GetLayoutHistoryAndKey(aContent, true,
getter_AddRefs(history), key);
if (!history) {
return false;
}
nsPresState *state;
// Get the pres state for this key
rv = history->GetState(key, &state);
if (state) {
nsresult rv = history->GetState(key, &state);
if (NS_SUCCEEDED(rv) && state) {
bool result = aControl->RestoreState(state);
history->RemoveState(key);
return result;

View File

@ -1317,21 +1317,51 @@ typedef nsTHashtable<MediaElementSetForURI> MediaElementURITable;
// same mLoadingSrc.
static MediaElementURITable* gElementTable;
#ifdef DEBUG
// Returns the number of times aElement appears in the media element table
// for aURI. If this returns other than 0 or 1, there's a bug somewhere!
static unsigned
MediaElementTableCount(nsHTMLMediaElement* aElement, nsIURI* aURI)
{
if (!gElementTable || !aElement || !aURI) {
return 0;
}
MediaElementSetForURI* entry = gElementTable->GetEntry(aURI);
if (!entry) {
return 0;
}
PRUint32 count = 0;
for (PRUint32 i = 0; i < entry->mElements.Length(); ++i) {
nsHTMLMediaElement* elem = entry->mElements[i];
if (elem == aElement) {
count++;
}
}
return count;
}
#endif
void
nsHTMLMediaElement::AddMediaElementToURITable()
{
NS_ASSERTION(mDecoder && mDecoder->GetStream(), "Call this only with decoder Load called");
NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 0,
"Should not have entry for element in element table before addition");
if (!gElementTable) {
gElementTable = new MediaElementURITable();
gElementTable->Init();
}
MediaElementSetForURI* entry = gElementTable->PutEntry(mLoadingSrc);
entry->mElements.AppendElement(this);
NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 1,
"Should have a single entry for element in element table after addition");
}
void
nsHTMLMediaElement::RemoveMediaElementFromURITable()
{
NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 1,
"Before remove, should have a single entry for element in element table");
NS_ASSERTION(mDecoder, "Don't call this without decoder!");
NS_ASSERTION(mLoadingSrc, "Can't have decoder without source!");
if (!gElementTable)
@ -1347,6 +1377,8 @@ nsHTMLMediaElement::RemoveMediaElementFromURITable()
gElementTable = nsnull;
}
}
NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 0,
"After remove, should no longer have an entry in element table");
}
nsHTMLMediaElement*
@ -1431,6 +1463,10 @@ nsHTMLMediaElement::~nsHTMLMediaElement()
RemoveMediaElementFromURITable();
mDecoder->Shutdown();
}
NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 0,
"Destroyed media element should no longer be in element table");
if (mChannel) {
mChannel->Cancel(NS_BINDING_ABORTED);
}
@ -1951,6 +1987,7 @@ nsHTMLMediaElement::CreateDecoder(const nsACString& aType)
nsresult nsHTMLMediaElement::InitializeDecoderAsClone(nsMediaDecoder* aOriginal)
{
NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
NS_ASSERTION(mDecoder == nsnull, "Shouldn't have a decoder");
nsMediaStream* originalStream = aOriginal->GetStream();
if (!originalStream)
@ -1990,6 +2027,7 @@ nsresult nsHTMLMediaElement::InitializeDecoderForChannel(nsIChannel *aChannel,
nsIStreamListener **aListener)
{
NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
NS_ASSERTION(mDecoder == nsnull, "Shouldn't have a decoder");
nsCAutoString mimeType;
aChannel->GetContentType(mimeType);
@ -2059,6 +2097,15 @@ nsresult nsHTMLMediaElement::FinishDecoderSetup(nsMediaDecoder* aDecoder)
NotifyAudioAvailableListener();
}
if (NS_FAILED(rv)) {
RemoveMediaElementFromURITable();
mDecoder->Shutdown();
mDecoder = nsnull;
}
NS_ASSERTION(NS_SUCCEEDED(rv) == (MediaElementTableCount(this, mLoadingSrc) == 1),
"Media element should have single table entry if decode initialized");
mBegun = true;
return rv;
}

View File

@ -202,12 +202,14 @@ nsresult nsBuiltinDecoder::Load(nsMediaStream* aStream,
mDecoderStateMachine = CreateStateMachine();
if (!mDecoderStateMachine) {
LOG(PR_LOG_DEBUG, ("%p Failed to create state machine!", this));
return NS_ERROR_FAILURE;
}
nsBuiltinDecoder* cloneDonor = static_cast<nsBuiltinDecoder*>(aCloneDonor);
if (NS_FAILED(mDecoderStateMachine->Init(cloneDonor ?
cloneDonor->mDecoderStateMachine : nsnull))) {
LOG(PR_LOG_DEBUG, ("%p Failed to init state machine!", this));
return NS_ERROR_FAILURE;
}
{

View File

@ -59,6 +59,7 @@ using mozilla::gfxSurfaceType;
using gfxIntSize;
using mozilla::null_t;
using mozilla::plugins::WindowsSharedMemoryHandle;
using SurfaceDescriptorX11;
using nsIntRect;
using nsTextEvent;
using nsKeyEvent;
@ -66,12 +67,6 @@ using nsKeyEvent;
namespace mozilla {
namespace plugins {
struct SurfaceDescriptorX11 {
int XID;
int xrenderPictID;
gfxIntSize size;
};
struct IOSurfaceDescriptor {
uint32_t surfaceId;
};

View File

@ -963,37 +963,6 @@ PluginInstanceChild::RecvWindowPosChanged(const NPRemoteEvent& event)
#endif
}
#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
static bool
XVisualIDToInfo(Display* aDisplay, VisualID aVisualID,
Visual** aVisual, unsigned int* aDepth)
{
if (aVisualID == None) {
*aVisual = NULL;
*aDepth = 0;
return true;
}
const Screen* screen = DefaultScreenOfDisplay(aDisplay);
for (int d = 0; d < screen->ndepths; d++) {
Depth *d_info = &screen->depths[d];
for (int v = 0; v < d_info->nvisuals; v++) {
Visual* visual = &d_info->visuals[v];
if (visual->visualid == aVisualID) {
*aVisual = visual;
*aDepth = d_info->depth;
return true;
}
}
}
NS_ERROR("VisualID not on Screen.");
return false;
}
#endif
bool
PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
{
@ -3280,8 +3249,7 @@ PluginInstanceChild::ShowPluginFrame()
#ifdef MOZ_X11
if (mCurrentSurface->GetType() == gfxASurface::SurfaceTypeXlib) {
gfxXlibSurface *xsurf = static_cast<gfxXlibSurface*>(mCurrentSurface.get());
currSurf = SurfaceDescriptorX11(xsurf->XDrawable(), xsurf->XRenderFormat()->id,
mCurrentSurface->GetSize());
currSurf = SurfaceDescriptorX11(xsurf);
// Need to sync all pending x-paint requests
// before giving drawable to another process
XSync(mWsInfo.display, False);
@ -3446,14 +3414,7 @@ PluginInstanceChild::RecvUpdateBackground(const SurfaceDescriptor& aBackground,
switch (aBackground.type()) {
#ifdef MOZ_X11
case SurfaceDescriptor::TSurfaceDescriptorX11: {
SurfaceDescriptorX11 xdesc = aBackground.get_SurfaceDescriptorX11();
XRenderPictFormat pf;
pf.id = xdesc.xrenderPictID();
XRenderPictFormat *incFormat =
XRenderFindFormat(DefaultXDisplay(), PictFormatID, &pf, 0);
mBackground =
new gfxXlibSurface(DefaultScreenOfDisplay(DefaultXDisplay()),
xdesc.XID(), incFormat, xdesc.size());
mBackground = aBackground.get_SurfaceDescriptorX11().OpenForeign();
break;
}
#endif

View File

@ -535,14 +535,7 @@ PluginInstanceParent::RecvShow(const NPRect& updatedRect,
#endif
#ifdef MOZ_X11
else if (newSurface.type() == SurfaceDescriptor::TSurfaceDescriptorX11) {
SurfaceDescriptorX11 xdesc = newSurface.get_SurfaceDescriptorX11();
XRenderPictFormat pf;
pf.id = xdesc.xrenderPictID();
XRenderPictFormat *incFormat =
XRenderFindFormat(DefaultXDisplay(), PictFormatID, &pf, 0);
surface =
new gfxXlibSurface(DefaultScreenOfDisplay(DefaultXDisplay()),
xdesc.XID(), incFormat, xdesc.size());
surface = newSurface.get_SurfaceDescriptorX11().OpenForeign();
}
#endif
#ifdef XP_WIN
@ -847,8 +840,7 @@ PluginInstanceParent::BackgroundDescriptor()
#ifdef MOZ_X11
gfxXlibSurface* xsurf = static_cast<gfxXlibSurface*>(mBackground.get());
return SurfaceDescriptorX11(xsurf->XDrawable(), xsurf->XRenderFormat()->id,
xsurf->GetSize());
return SurfaceDescriptorX11(xsurf);
#endif
#ifdef XP_WIN

View File

@ -43,6 +43,7 @@
#include "base/message_loop.h"
#include "mozilla/ipc/RPCChannel.h"
#include "gfxipc/ShadowLayerUtils.h"
#include "npapi.h"
#include "npruntime.h"
@ -66,6 +67,8 @@ using mac_plugin_interposing::NSCursorInfo;
namespace mozilla {
namespace plugins {
using layers::SurfaceDescriptorX11;
enum ScriptableObjectType
{
LocalObject,

View File

@ -102,6 +102,7 @@ class nsIsOfflineSQLFunction : public mozIStorageFunction
NS_IMPL_ISUPPORTS1(nsIsOfflineSQLFunction, mozIStorageFunction)
nsDOMStoragePersistentDB::nsDOMStoragePersistentDB()
: mStatements(mConnection)
{
mTempTableLoads.Init(16);
}
@ -135,53 +136,6 @@ nsIsOfflineSQLFunction::OnFunctionCall(
return NS_OK;
}
class Binder
{
public:
Binder(mozIStorageStatement* statement, nsresult *rv);
mozIStorageBindingParams* operator->();
nsresult Add();
private:
mozIStorageStatement* mStmt;
nsCOMPtr<mozIStorageBindingParamsArray> mArray;
nsCOMPtr<mozIStorageBindingParams> mParams;
};
Binder::Binder(mozIStorageStatement* statement, nsresult *rv)
: mStmt(statement)
{
*rv = mStmt->NewBindingParamsArray(getter_AddRefs(mArray));
if (NS_FAILED(*rv))
return;
*rv = mArray->NewBindingParams(getter_AddRefs(mParams));
if (NS_FAILED(*rv))
return;
*rv = NS_OK;
}
mozIStorageBindingParams*
Binder::operator->()
{
return mParams;
}
nsresult
Binder::Add()
{
nsresult rv;
rv = mArray->AddParams(mParams);
NS_ENSURE_SUCCESS(rv, rv);
rv = mStmt->BindParameters(mArray);
NS_ENSURE_SUCCESS(rv, rv);
return rv;
}
nsresult
nsDOMStoragePersistentDB::Init(const nsString& aDatabaseName)
{
@ -320,129 +274,6 @@ nsDOMStoragePersistentDB::Init(const nsString& aDatabaseName)
NS_ENSURE_SUCCESS(rv, rv);
}
// temporary - disk synchronization statements
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"INSERT INTO webappsstore2_temp"
" SELECT * FROM webappsstore2"
" WHERE scope = :scope AND NOT EXISTS ("
"SELECT scope, key FROM webappsstore2_temp "
"WHERE scope = webappsstore2.scope AND key = webappsstore2.key)"),
getter_AddRefs(mCopyToTempTableStatement));
NS_ENSURE_SUCCESS(rv, rv);
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"INSERT OR REPLACE INTO webappsstore2"
" SELECT * FROM webappsstore2_temp"
" WHERE scope = :scope;"),
getter_AddRefs(mCopyBackToDiskStatement));
NS_ENSURE_SUCCESS(rv, rv);
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM webappsstore2_temp"
" WHERE scope = :scope;"),
getter_AddRefs(mDeleteTemporaryTableStatement));
NS_ENSURE_SUCCESS(rv, rv);
// retrieve all keys associated with a domain
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"SELECT key, value, secure FROM webappsstore2_temp "
"WHERE scope = :scope"),
getter_AddRefs(mGetAllKeysStatement));
NS_ENSURE_SUCCESS(rv, rv);
// retrieve a value given a domain and a key
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"SELECT value, secure FROM webappsstore2_temp "
"WHERE scope = :scope "
"AND key = :key"),
getter_AddRefs(mGetKeyValueStatement));
NS_ENSURE_SUCCESS(rv, rv);
// insert a new key
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"INSERT OR REPLACE INTO "
"webappsstore2_temp(scope, key, value, secure) "
"VALUES (:scope, :key, :value, :secure)"),
getter_AddRefs(mInsertKeyStatement));
NS_ENSURE_SUCCESS(rv, rv);
// update the secure status of an existing key
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"UPDATE webappsstore2_temp "
"SET secure = :secure "
"WHERE scope = :scope "
"AND key = :key "),
getter_AddRefs(mSetSecureStatement));
NS_ENSURE_SUCCESS(rv, rv);
// remove a key
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM webappsstore2_view "
"WHERE scope = :scope "
"AND key = :key"),
getter_AddRefs(mRemoveKeyStatement));
NS_ENSURE_SUCCESS(rv, rv);
// remove keys owned by a specific domain
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM webappsstore2_view "
"WHERE scope GLOB :scope"),
getter_AddRefs(mRemoveOwnerStatement));
NS_ENSURE_SUCCESS(rv, rv);
// remove keys belonging exactly only to a specific domain
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM webappsstore2_view "
"WHERE scope = :scope"),
getter_AddRefs(mRemoveStorageStatement));
NS_ENSURE_SUCCESS(rv, rv);
// remove all keys
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM webappsstore2_view"),
getter_AddRefs(mRemoveAllStatement));
NS_ENSURE_SUCCESS(rv, rv);
// check the usage for a given owner that is an offline-app allowed domain
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"SELECT SUM(LENGTH(key) + LENGTH(value)) "
"FROM ("
"SELECT key,value FROM webappsstore2_temp "
"WHERE scope GLOB :scope "
"UNION ALL "
"SELECT key,value FROM webappsstore2 "
"WHERE scope GLOB :scope "
"AND NOT EXISTS ("
"SELECT scope, key "
"FROM webappsstore2_temp "
"WHERE scope = webappsstore2.scope "
"AND key = webappsstore2.key"
")"
")"),
getter_AddRefs(mGetFullUsageStatement));
NS_ENSURE_SUCCESS(rv, rv);
// check the usage for a given owner that is not an offline-app allowed domain
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
"SELECT SUM(LENGTH(key) + LENGTH(value)) "
"FROM ("
"SELECT key, value FROM webappsstore2_temp "
"WHERE scope GLOB :scope "
"AND NOT ISOFFLINE(scope) "
"UNION ALL "
"SELECT key, value FROM webappsstore2 "
"WHERE scope GLOB :scope "
"AND NOT ISOFFLINE(scope) "
"AND NOT EXISTS ("
"SELECT scope, key "
"FROM webappsstore2_temp "
"WHERE scope = webappsstore2.scope "
"AND key = webappsstore2.key"
")"
")"),
getter_AddRefs(mGetOfflineExcludedUsageStatement));
NS_ENSURE_SUCCESS(rv, rv);
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
@ -452,20 +283,8 @@ nsDOMStoragePersistentDB::Init(const nsString& aDatabaseName)
void
nsDOMStoragePersistentDB::Close()
{
// Null the statements, this will finalize them.
mCopyToTempTableStatement = nsnull;
mCopyBackToDiskStatement = nsnull;
mDeleteTemporaryTableStatement = nsnull;
mGetAllKeysStatement = nsnull;
mGetKeyValueStatement = nsnull;
mInsertKeyStatement = nsnull;
mSetSecureStatement = nsnull;
mRemoveKeyStatement = nsnull;
mRemoveOwnerStatement = nsnull;
mRemoveStorageStatement = nsnull;
mRemoveAllStatement = nsnull;
mGetOfflineExcludedUsageStatement = nsnull;
mGetFullUsageStatement = nsnull;
// Finalize the cached statements.
mStatements.FinalizeStatements();
DebugOnly<nsresult> rv = mConnection->Close();
MOZ_ASSERT(NS_SUCCEEDED(rv));
@ -482,19 +301,23 @@ nsDOMStoragePersistentDB::EnsureLoadTemporaryTableForStorage(DOMStorageImpl* aSt
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mCopyToTempTableStatement);
nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
"INSERT INTO webappsstore2_temp "
"SELECT * FROM webappsstore2 "
"WHERE scope = :scope "
"AND NOT EXISTS ( "
"SELECT scope, key FROM webappsstore2_temp "
"WHERE scope = webappsstore2.scope AND key = webappsstore2.key "
") "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scope(stmt);
Binder binder(mCopyToTempTableStatement, &rv);
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
rv = mCopyToTempTableStatement->Execute();
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
mTempTableLoads.Put(aStorage->GetScopeDBKey(), TimeStamp::Now());
@ -518,34 +341,35 @@ nsDOMStoragePersistentDB::FlushTemporaryTable(nsCStringHashKey::KeyType aKey,
return PL_DHASH_NEXT;
{
mozStorageStatementScoper scope(data->mDB->mCopyBackToDiskStatement);
nsCOMPtr<mozIStorageStatement> stmt =
data->mDB->mStatements.GetCachedStatement(
"INSERT OR REPLACE INTO webappsstore2 "
"SELECT * FROM webappsstore2_temp "
"WHERE scope = :scope "
);
NS_ENSURE_TRUE(stmt, PL_DHASH_STOP);
mozStorageStatementScoper scope(stmt);
Binder binder(data->mDB->mCopyBackToDiskStatement, &data->mRV);
data->mRV = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), aKey);
NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
data->mRV = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), aKey);
NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
data->mRV = binder.Add();
NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
data->mRV = data->mDB->mCopyBackToDiskStatement->Execute();
data->mRV = stmt->Execute();
NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
}
{
mozStorageStatementScoper scope(data->mDB->mDeleteTemporaryTableStatement);
nsCOMPtr<mozIStorageStatement> stmt =
data->mDB->mStatements.GetCachedStatement(
"DELETE FROM webappsstore2_temp "
"WHERE scope = :scope "
);
NS_ENSURE_TRUE(stmt, PL_DHASH_STOP);
mozStorageStatementScoper scope(stmt);
Binder binder(data->mDB->mDeleteTemporaryTableStatement, &data->mRV);
data->mRV = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), aKey);
NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
data->mRV = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), aKey);
NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
data->mRV = binder.Add();
NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
data->mRV = data->mDB->mDeleteTemporaryTableStatement->Execute();
data->mRV = stmt->Execute();
NS_ENSURE_SUCCESS(data->mRV, PL_DHASH_STOP);
}
@ -588,32 +412,29 @@ nsDOMStoragePersistentDB::GetAllKeys(DOMStorageImpl* aStorage,
rv = EnsureLoadTemporaryTableForStorage(aStorage);
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mGetAllKeysStatement);
nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
"SELECT key, value, secure FROM webappsstore2_temp "
"WHERE scope = :scope "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scope(stmt);
Binder binder(mGetAllKeysStatement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
bool exists;
while (NS_SUCCEEDED(rv = mGetAllKeysStatement->ExecuteStep(&exists)) &&
exists) {
while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&exists)) && exists) {
nsAutoString key;
rv = mGetAllKeysStatement->GetString(0, key);
rv = stmt->GetString(0, key);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString value;
rv = mGetAllKeysStatement->GetString(1, value);
rv = stmt->GetString(1, value);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 secureInt = 0;
rv = mGetAllKeysStatement->GetInt32(2, &secureInt);
rv = stmt->GetInt32(2, &secureInt);
NS_ENSURE_SUCCESS(rv, rv);
nsSessionStorageEntry* entry = aKeys->PutEntry(key);
@ -643,31 +464,31 @@ nsDOMStoragePersistentDB::GetKeyValue(DOMStorageImpl* aStorage,
rv = EnsureLoadTemporaryTableForStorage(aStorage);
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mGetKeyValueStatement);
nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
"SELECT value, secure FROM webappsstore2_temp "
"WHERE scope = :scope "
"AND key = :key "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scope(stmt);
Binder binder(mGetKeyValueStatement, &rv);
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindStringByName(NS_LITERAL_CSTRING("key"),
aKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key"),
aKey);
NS_ENSURE_SUCCESS(rv, rv);
bool exists;
rv = mGetKeyValueStatement->ExecuteStep(&exists);
rv = stmt->ExecuteStep(&exists);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 secureInt = 0;
if (exists) {
rv = mGetKeyValueStatement->GetString(0, aValue);
rv = stmt->GetString(0, aValue);
NS_ENSURE_SUCCESS(rv, rv);
rv = mGetKeyValueStatement->GetInt32(1, &secureInt);
rv = stmt->GetInt32(1, &secureInt);
NS_ENSURE_SUCCESS(rv, rv);
}
else {
@ -719,28 +540,27 @@ nsDOMStoragePersistentDB::SetKey(DOMStorageImpl* aStorage,
rv = EnsureInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scopeinsert(mInsertKeyStatement);
nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
"INSERT OR REPLACE INTO webappsstore2_temp (scope, key, value, secure) "
"VALUES (:scope, :key, :value, :secure) "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scopeinsert(stmt);
Binder binder(mInsertKeyStatement, &rv);
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key"),
aKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("value"),
aValue);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("secure"),
aSecure ? 1 : 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindStringByName(NS_LITERAL_CSTRING("key"),
aKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindStringByName(NS_LITERAL_CSTRING("value"),
aValue);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindInt32ByName(NS_LITERAL_CSTRING("secure"),
aSecure ? 1 : 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
rv = mInsertKeyStatement->Execute();
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
if (!aStorage->GetQuotaDomainDBKey(!aExcludeOfflineFromUsage).IsEmpty()) {
@ -768,25 +588,26 @@ nsDOMStoragePersistentDB::SetSecure(DOMStorageImpl* aStorage,
rv = EnsureInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mSetSecureStatement);
nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
"UPDATE webappsstore2_temp "
"SET secure = :secure "
"WHERE scope = :scope "
"AND key = :key "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scope(stmt);
Binder binder(mSetSecureStatement, &rv);
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key"),
aKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("secure"),
aSecure ? 1 : 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindStringByName(NS_LITERAL_CSTRING("key"),
aKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindInt32ByName(NS_LITERAL_CSTRING("secure"),
aSecure ? 1 : 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
rv = mSetSecureStatement->Execute();
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
MarkScopeDirty(aStorage);
@ -805,7 +626,13 @@ nsDOMStoragePersistentDB::RemoveKey(DOMStorageImpl* aStorage,
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mRemoveKeyStatement);
nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
"DELETE FROM webappsstore2_view "
"WHERE scope = :scope "
"AND key = :key "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scope(stmt);
if (DomainMaybeCached(
aStorage->GetQuotaDomainDBKey(!aExcludeOfflineFromUsage))) {
@ -813,20 +640,14 @@ nsDOMStoragePersistentDB::RemoveKey(DOMStorageImpl* aStorage,
mCachedOwner.Truncate();
}
Binder binder(mRemoveKeyStatement, &rv);
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key"),
aKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindStringByName(NS_LITERAL_CSTRING("key"),
aKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
rv = mRemoveKeyStatement->Execute();
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
MarkScopeDirty(aStorage);
@ -842,22 +663,21 @@ nsDOMStoragePersistentDB::ClearStorage(DOMStorageImpl* aStorage)
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mRemoveStorageStatement);
nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
"DELETE FROM webappsstore2_view "
"WHERE scope = :scope "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scope(stmt);
mCachedUsage = 0;
mCachedOwner.Truncate();
Binder binder(mRemoveStorageStatement, &rv);
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
aStorage->GetScopeDBKey());
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
rv = mRemoveStorageStatement->Execute();
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
MarkScopeDirty(aStorage);
@ -874,7 +694,12 @@ nsDOMStoragePersistentDB::RemoveOwner(const nsACString& aOwner,
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mRemoveOwnerStatement);
nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
"DELETE FROM webappsstore2_view "
"WHERE scope GLOB :scope "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scope(stmt);
nsCAutoString subdomainsDBKey;
nsDOMStorageDBWrapper::CreateDomainScopeDBKey(aOwner, subdomainsDBKey);
@ -888,17 +713,11 @@ nsDOMStoragePersistentDB::RemoveOwner(const nsACString& aOwner,
subdomainsDBKey.AppendLiteral(":");
subdomainsDBKey.AppendLiteral("*");
Binder binder(mRemoveOwnerStatement, &rv);
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
subdomainsDBKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"),
subdomainsDBKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
rv = mRemoveOwnerStatement->Execute();
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
MarkAllScopesDirty();
@ -954,9 +773,6 @@ nsDOMStoragePersistentDB::RemoveOwners(const nsTArray<nsString> &aOwners,
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
Binder binder(statement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
for (PRUint32 i = 0; i < aOwners.Length(); i++) {
nsCAutoString quotaKey;
rv = nsDOMStorageDBWrapper::CreateDomainScopeDBKey(
@ -975,13 +791,10 @@ nsDOMStoragePersistentDB::RemoveOwners(const nsTArray<nsString> &aOwners,
paramName.Assign("scope");
paramName.AppendInt(i);
rv = binder->BindUTF8StringByName(paramName, quotaKey);
rv = statement->BindUTF8StringByName(paramName, quotaKey);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = binder.Add();
NS_ENSURE_SUCCESS(rv, rv);
rv = statement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
@ -998,9 +811,13 @@ nsDOMStoragePersistentDB::RemoveAll()
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozStorageStatementScoper scope(mRemoveAllStatement);
nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
"DELETE FROM webappsstore2_view "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scope(stmt);
rv = mRemoveAllStatement->Execute();
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
MarkAllScopesDirty();
@ -1050,25 +867,55 @@ nsDOMStoragePersistentDB::GetUsageInternal(const nsACString& aQuotaDomainDBKey,
rv = MaybeCommitInsertTransaction();
NS_ENSURE_SUCCESS(rv, rv);
mozIStorageStatement* statement = aExcludeOfflineFromUsage
? mGetOfflineExcludedUsageStatement : mGetFullUsageStatement;
mozStorageStatementScoper scope(statement);
nsCOMPtr<mozIStorageStatement> stmt;
if (aExcludeOfflineFromUsage) {
stmt = mStatements.GetCachedStatement(
"SELECT SUM(LENGTH(key) + LENGTH(value)) "
"FROM ( "
"SELECT key, value FROM webappsstore2_temp "
"WHERE scope GLOB :scope "
"AND NOT ISOFFLINE(scope) "
"UNION ALL "
"SELECT key, value FROM webappsstore2 "
"WHERE scope GLOB :scope "
"AND NOT ISOFFLINE(scope) "
"AND NOT EXISTS ( "
"SELECT scope, key "
"FROM webappsstore2_temp "
"WHERE scope = webappsstore2.scope "
"AND key = webappsstore2.key "
") "
") "
);
} else {
stmt = mStatements.GetCachedStatement(
"SELECT SUM(LENGTH(key) + LENGTH(value)) "
"FROM ( "
"SELECT key,value FROM webappsstore2_temp "
"WHERE scope GLOB :scope "
"UNION ALL "
"SELECT key,value FROM webappsstore2 "
"WHERE scope GLOB :scope "
"AND NOT EXISTS ( "
"SELECT scope, key "
"FROM webappsstore2_temp "
"WHERE scope = webappsstore2.scope "
"AND key = webappsstore2.key "
") "
") "
);
}
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scope(stmt);
nsCAutoString scopeValue(aQuotaDomainDBKey);
scopeValue += NS_LITERAL_CSTRING("*");
Binder binder(statement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), scopeValue);
NS_ENSURE_SUCCESS(rv, rv);
rv = binder.Add();
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), scopeValue);
NS_ENSURE_SUCCESS(rv, rv);
bool exists;
rv = statement->ExecuteStep(&exists);
rv = stmt->ExecuteStep(&exists);
NS_ENSURE_SUCCESS(rv, rv);
if (!exists) {
@ -1076,7 +923,7 @@ nsDOMStoragePersistentDB::GetUsageInternal(const nsACString& aQuotaDomainDBKey,
return NS_OK;
}
rv = statement->GetInt32(0, aUsage);
rv = stmt->GetInt32(0, aUsage);
NS_ENSURE_SUCCESS(rv, rv);
if (!aQuotaDomainDBKey.IsEmpty()) {

View File

@ -46,6 +46,7 @@
#include "nsTHashtable.h"
#include "nsDataHashtable.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/storage/StatementCache.h"
class DOMStorageImpl;
class nsSessionStorageEntry;
@ -55,6 +56,8 @@ using mozilla::TimeDuration;
class nsDOMStoragePersistentDB : public nsDOMStorageBaseDB
{
typedef mozilla::storage::StatementCache<mozIStorageStatement> StatementCache;
public:
nsDOMStoragePersistentDB();
~nsDOMStoragePersistentDB() {}
@ -191,21 +194,7 @@ protected:
void* aUserArg);
nsCOMPtr<mozIStorageConnection> mConnection;
nsCOMPtr<mozIStorageStatement> mCopyToTempTableStatement;
nsCOMPtr<mozIStorageStatement> mCopyBackToDiskStatement;
nsCOMPtr<mozIStorageStatement> mDeleteTemporaryTableStatement;
nsCOMPtr<mozIStorageStatement> mGetAllKeysStatement;
nsCOMPtr<mozIStorageStatement> mGetKeyValueStatement;
nsCOMPtr<mozIStorageStatement> mInsertKeyStatement;
nsCOMPtr<mozIStorageStatement> mSetSecureStatement;
nsCOMPtr<mozIStorageStatement> mRemoveKeyStatement;
nsCOMPtr<mozIStorageStatement> mRemoveOwnerStatement;
nsCOMPtr<mozIStorageStatement> mRemoveStorageStatement;
nsCOMPtr<mozIStorageStatement> mRemoveAllStatement;
nsCOMPtr<mozIStorageStatement> mGetOfflineExcludedUsageStatement;
nsCOMPtr<mozIStorageStatement> mGetFullUsageStatement;
// If you add an statement, remember to null in in Close.
StatementCache mStatements;
nsCString mCachedOwner;
PRInt32 mCachedUsage;

View File

@ -189,6 +189,19 @@ const RADIO_STATE_RUIM_LOCKED_OR_ABSENT = 7;
const RADIO_STATE_NV_NOT_READY = 8;
const RADIO_STATE_NV_READY = 9;
const CARD_STATE_ABSENT = 0;
const CARD_STATE_PRESENT = 1;
const CARD_STATE_ERROR = 2;
const CARD_APP_STATE_UNKNOWN = 0;
const CARD_APP_STATE_DETECTED = 1;
const CARD_APP_STATE_PIN = 2; // If PIN1 or UPin is required.
const CARD_APP_STATE_PUK = 3; // If PUK1 or Puk for UPin is required.
const CARD_APP_STATE_SUBSCRIPTION_PERSO = 4; // perso_substate should be looked
// at when app_state is assigned
// to this value.
const CARD_APP_STATE_READY = 5;
const CARD_MAX_APPS = 8;
const CALL_STATE_ACTIVE = 0;

View File

@ -513,7 +513,7 @@ let Buf = {
let RIL = {
/**
* Retrieve the ICC card's status.
* Retrieve the ICC's status.
*
* Response will call Phone.onICCStatus().
*/
@ -527,7 +527,7 @@ let RIL = {
* @param pin
* String containing the PIN.
*
* Response will call Phone.onEnterSIMPIN().
* Response will call Phone.onEnterICCPIN().
*/
enterICCPIN: function enterICCPIN(pin) {
Buf.newParcel(REQUEST_ENTER_SIM_PIN);
@ -536,6 +536,42 @@ let RIL = {
Buf.sendParcel();
},
/**
* Change the current ICC PIN number
*
* @param oldPin
* String containing the old PIN value
* @param newPin
* String containing the new PIN value
*
* Response will call Phone.onChangeICCPIN().
*/
changeICCPIN: function changeICCPIN(oldPin, newPin) {
Buf.newParcel(REQUEST_CHANGE_SIM_PIN);
Buf.writeUint32(2);
Buf.writeString(oldPin);
Buf.writeString(newPin);
Buf.sendParcel();
},
/**
* Supplies SIM PUK and a new PIN to unlock the ICC
*
* @param puk
* String containing the PUK value.
* @param newPin
* String containing the new PIN value.
*
* Response will call Phone.onEnterICCPUK().
*/
enterICCPUK: function enterICCPUK(puk, newPin) {
Buf.newParcel(REQUEST_ENTER_SIM_PUK);
Buf.writeUint32(2);
Buf.writeString(puk);
Buf.writeString(newPin);
Buf.sendParcel();
},
/**
* Request the phone's radio power to be switched on or off.
*
@ -767,7 +803,7 @@ let RIL = {
RIL[REQUEST_GET_SIM_STATUS] = function REQUEST_GET_SIM_STATUS() {
let iccStatus = {
cardState: Buf.readUint32(), // CARDSTATE_*
cardState: Buf.readUint32(), // CARD_STATE_*
universalPINState: Buf.readUint32(), // PINSTATE_*
gsmUmtsSubscriptionAppIndex: Buf.readUint32(),
setCdmaSubscriptionAppIndex: Buf.readUint32(),
@ -782,7 +818,7 @@ RIL[REQUEST_GET_SIM_STATUS] = function REQUEST_GET_SIM_STATUS() {
for (let i = 0 ; i < apps_length ; i++) {
iccStatus.apps.push({
app_type: Buf.readUint32(), // APPTYPE_*
app_state: Buf.readUint32(), // APPSTATE_*
app_state: Buf.readUint32(), // CARD_APP_STATE_*
perso_substate: Buf.readUint32(), // PERSOSUBSTATE_*
aid: Buf.readString(),
app_label: Buf.readString(),
@ -797,10 +833,15 @@ RIL[REQUEST_ENTER_SIM_PIN] = function REQUEST_ENTER_SIM_PIN() {
let response = Buf.readUint32List();
Phone.onEnterICCPIN(response);
};
RIL[REQUEST_ENTER_SIM_PUK] = null;
RIL[REQUEST_ENTER_SIM_PUK] = function REQUEST_ENTER_SIM_PUK() {
let response = Buf.readUint32List();
Phone.onEnterICCPUK(response);
};
RIL[REQUEST_ENTER_SIM_PIN2] = null;
RIL[REQUEST_ENTER_SIM_PUK2] = null;
RIL[REQUEST_CHANGE_SIM_PIN] = null;
RIL[REQUEST_CHANGE_SIM_PIN] = function REQUEST_CHANGE_SIM_PIN() {
Phone.onChangeICCPIN();
};
RIL[REQUEST_CHANGE_SIM_PIN2] = null;
RIL[REQUEST_ENTER_NETWORK_DEPERSONALIZATION] = null;
RIL[REQUEST_GET_CURRENT_CALLS] = function REQUEST_GET_CURRENT_CALLS(length) {
@ -1060,7 +1101,9 @@ RIL[UNSOLICITED_CALL_RING] = function UNSOLICITED_CALL_RING() {
}
Phone.onCallRing(info);
};
RIL[UNSOLICITED_RESPONSE_SIM_STATUS_CHANGED] = null;
RIL[UNSOLICITED_RESPONSE_SIM_STATUS_CHANGED] = function UNSOLICITED_RESPONSE_SIM_STATUS_CHANGED() {
Phone.onICCStatusChanged();
};
RIL[UNSOLICITED_RESPONSE_CDMA_NEW_SMS] = null;
RIL[UNSOLICITED_RESPONSE_NEW_BROADCAST_SMS] = null;
RIL[UNSOLICITED_CDMA_RUIM_SMS_STORAGE_FULL] = null;
@ -1113,10 +1156,16 @@ let Phone = {
networkSelectionMode: null,
/**
* ICC card status
* ICC status. Keeps a reference of the data response to the
* getICCStatus request.
*/
iccStatus: null,
/**
* Card state
*/
cardState: null,
/**
* Active calls
*/
@ -1216,7 +1265,7 @@ let Phone = {
if (newState == RADIO_STATE_SIM_READY ||
newState == RADIO_STATE_RUIM_READY ||
newState == RADIO_STATE_NV_READY) {
// The ICC card has become available. Get all the things.
// The ICC has become available. Get all the things.
RIL.getICCStatus();
this.requestNetworkInfo();
RIL.getSignalStrength();
@ -1307,15 +1356,101 @@ let Phone = {
},
onICCStatus: function onICCStatus(iccStatus) {
debug("SIM card state is " + iccStatus.cardState);
debug("Universal PIN state is " + iccStatus.universalPINState);
debug(iccStatus);
//TODO set to simStatus and figure out state transitions.
this.iccStatus = iccStatus; //XXX TODO
if (DEBUG) {
debug("iccStatus: " + JSON.stringify(iccStatus));
}
this.iccStatus = iccStatus;
if ((!iccStatus) || (iccStatus.cardState == CARD_STATE_ABSENT)) {
if (DEBUG) debug("ICC absent");
if (this.cardState == DOM_CARDSTATE_ABSENT) {
return;
}
this.cardState = DOM_CARDSTATE_ABSENT;
this.sendDOMMessage({type: "cardstatechange",
cardState: this.cardState});
return;
}
if ((this.radioState == RADIO_STATE_OFF) ||
(this.radioState == RADIO_STATE_UNAVAILABLE) ||
(this.radioState == RADIO_STATE_SIM_NOT_READY) ||
(this.radioState == RADIO_STATE_RUIM_NOT_READY) ||
(this.radioState == RADIO_STATE_NV_NOT_READY) ||
(this.radioState == RADIO_STATE_NV_READY)) {
if (DEBUG) debug("ICC not ready");
if (this.cardState == DOM_CARDSTATE_NOT_READY) {
return;
}
this.cardState = DOM_CARDSTATE_NOT_READY;
this.sendDOMMessage({type: "cardstatechange",
cardState: this.cardState});
return;
}
if ((this.radioState == RADIO_STATE_SIM_LOCKED_OR_ABSENT) ||
(this.radioState == RADIO_STATE_SIM_READY) ||
(this.radioState == RADIO_STATE_RUIM_LOCKED_OR_ABSENT) ||
(this.radioState == RADIO_STATE_RUIM_READY)) {
let app = iccStatus.apps[iccStatus.gsmUmtsSubscriptionAppIndex];
if (!app) {
if (DEBUG) {
debug("Subscription application is not present in iccStatus.");
}
if (this.cardState == DOM_CARDSTATE_ABSENT) {
return;
}
this.cardState = DOM_CARDSTATE_ABSENT;
this.sendDOMMessage({type: "cardstatechange",
cardState: this.cardState});
return;
}
let newCardState;
switch (app.app_state) {
case CARD_APP_STATE_PIN:
newCardState = DOM_CARDSTATE_PIN_REQUIRED;
break;
case CARD_APP_STATE_PUK:
newCardState = DOM_CARDSTATE_PUK_REQUIRED;
break;
case CARD_APP_STATE_SUBSCRIPTION_PERSO:
newCardState = DOM_CARDSTATE_NETWORK_LOCKED;
break;
case CARD_APP_STATE_READY:
newCardState = DOM_CARDSTATE_READY;
break;
case CARD_APP_STATE_UNKNOWN:
case CARD_APP_STATE_DETECTED:
default:
newCardState = DOM_CARDSTATE_NOT_READY;
}
if (this.cardState == newCardState) {
return;
}
this.cardState = newCardState;
this.sendDOMMessage({type: "cardstatechange",
cardState: this.cardState});
}
},
onICCStatusChanged: function onICCStatusChanged() {
RIL.getICCStatus();
},
onEnterICCPIN: function onEnterICCPIN(response) {
debug("REQUEST_ENTER_SIM_PIN returned " + response);
if (DEBUG) debug("REQUEST_ENTER_SIM_PIN returned " + response);
//TODO
},
onChangeICCPIN: function onChangeICCPIN() {
if (DEBUG) debug("REQUEST_CHANGE_SIM_PIN");
//TODO
},
onEnterICCPUK: function onEnterICCPUK(response) {
if (DEBUG) debug("REQUEST_ENTER_SIM_PUK returned " + response);
//TODO
},
@ -1349,7 +1484,7 @@ let Phone = {
onOperator: function onOperator(operator) {
if (operator.length < 3) {
debug("Expected at least 3 strings for operator.");
if (DEBUG) debug("Expected at least 3 strings for operator.");
}
if (!this.operator ||
this.operator.alphaLong != operator[0] ||
@ -1414,9 +1549,9 @@ let Phone = {
// An SMS is a string, but we won't read it as such, so let's read the
// string length and then defer to PDU parsing helper.
let messageStringLength = Buf.readUint32();
debug("Got new SMS, length " + messageStringLength);
if (DEBUG) debug("Got new SMS, length " + messageStringLength);
let message = GsmPDUHelper.readMessage();
debug(message);
if (DEBUG) debug(message);
// Read string delimiters. See Buf.readString().
let delimiter = Buf.readUint16();
@ -1581,7 +1716,9 @@ let Phone = {
//TODO: we shouldn't get here, but if we do, we might want to hold on
// to the message and retry once we know the SMSC... or just notify an
// error to the mainthread and let them deal with retrying?
debug("Cannot send the SMS. Need to get the SMSC address first.");
if (DEBUG) {
debug("Cannot send the SMS. Need to get the SMSC address first.");
}
return;
}
//TODO: verify values on 'options'
@ -1602,7 +1739,9 @@ let Phone = {
if (DEBUG) debug("Received DOM message " + JSON.stringify(message));
let method = this[message.type];
if (typeof method != "function") {
debug("Don't know what to do with message " + JSON.stringify(message));
if (DEBUG) {
debug("Don't know what to do with message " + JSON.stringify(message));
}
return;
}
method.call(this, message);
@ -1726,7 +1865,7 @@ let GsmPDUHelper = {
/**
* Write numerical data as swapped nibble BCD.
*
*
* @param data
* Data to write (as a string or a number)
*/

View File

@ -135,7 +135,7 @@ function iframeLoaded() {
</script>
<iframe onload='iframeLoaded()' id='not-queryable' src='http://example.net'></iframe>
<iframe onload='iframeLoaded()' id='not-queryable' src='http://example.org'></iframe>
<iframe onload='iframeLoaded()' mozbrowser id='queryable' src='http://example.com'></iframe>
</body>

View File

@ -35,65 +35,232 @@
* ***** END LICENSE BLOCK ***** */
#include "DrawTargetCairo.h"
#include "SourceSurfaceCairo.h"
#include "PathCairo.h"
#include "HelpersCairo.h"
#include "ScaledFontCairo.h"
#include "cairo.h"
#include "Blur.h"
#ifdef CAIRO_HAS_QUARTZ_SURFACE
#include "cairo-quartz.h"
#include <ApplicationServices/ApplicationServices.h>
#endif
#ifdef CAIRO_HAS_XLIB_SURFACE
#include "cairo-xlib.h"
#endif
#include <algorithm>
namespace mozilla {
namespace gfx {
cairo_operator_t
GfxOpToCairoOp(CompositionOp op)
namespace {
// An RAII class to prepare to draw a context and optional path. Saves and
// restores the context on construction/destruction.
class AutoPrepareForDrawing
{
switch (op)
public:
AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx)
: mCtx(ctx)
{
case OP_OVER:
return CAIRO_OPERATOR_OVER;
case OP_SOURCE:
return CAIRO_OPERATOR_SOURCE;
case OP_ADD:
return CAIRO_OPERATOR_ADD;
case OP_ATOP:
return CAIRO_OPERATOR_ATOP;
case OP_COUNT:
dt->PrepareForDrawing(ctx);
cairo_save(mCtx);
}
AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx, const Path* path)
: mCtx(ctx)
{
dt->PrepareForDrawing(ctx, path);
cairo_save(mCtx);
}
~AutoPrepareForDrawing() { cairo_restore(mCtx); }
private:
cairo_t* mCtx;
};
} // end anonymous namespace
static bool
GetCairoSurfaceSize(cairo_surface_t* surface, IntSize& size)
{
switch (cairo_surface_get_type(surface))
{
case CAIRO_SURFACE_TYPE_IMAGE:
{
size.width = cairo_image_surface_get_width(surface);
size.height = cairo_image_surface_get_height(surface);
return true;
}
#ifdef CAIRO_HAS_XLIB_SURFACE
case CAIRO_SURFACE_TYPE_XLIB:
{
size.width = cairo_xlib_surface_get_width(surface);
size.height = cairo_xlib_surface_get_height(surface);
return true;
}
#endif
#ifdef CAIRO_HAS_QUARTZ_SURFACE
case CAIRO_SURFACE_TYPE_QUARTZ:
{
CGContextRef cgc = cairo_quartz_surface_get_cg_context(surface);
// It's valid to call these CGBitmapContext functions on non-bitmap
// contexts; they'll just return 0 in that case.
size.width = CGBitmapContextGetWidth(cgc);
size.height = CGBitmapContextGetWidth(cgc);
return size.width != 0;
}
#endif
default:
return false;
}
}
static bool
PatternIsCompatible(const Pattern& aPattern)
{
switch (aPattern.GetType())
{
case PATTERN_LINEAR_GRADIENT:
{
const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern);
return pattern.mStops->GetBackendType() == BACKEND_CAIRO;
}
case PATTERN_RADIAL_GRADIENT:
{
const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern);
return pattern.mStops->GetBackendType() == BACKEND_CAIRO;
}
}
return true;
}
// Never returns NULL. As such, you must always pass in Cairo-compatible
// patterns, most notably gradients with a GradientStopCairo.
// The pattern returned must have cairo_pattern_destroy() called on it by the
// caller.
// As the cairo_pattern_t returned may depend on the Pattern passed in, the
// lifetime of the cairo_pattern_t returned must not exceed the lifetime of the
// Pattern passed in.
static cairo_pattern_t*
GfxPatternToCairoPattern(const Pattern& aPattern, Float aAlpha)
{
cairo_pattern_t* pat;
switch (aPattern.GetType())
{
case PATTERN_COLOR:
{
Color color = static_cast<const ColorPattern&>(aPattern).mColor;
pat = cairo_pattern_create_rgba(color.r, color.g, color.b, color.a * aAlpha);
break;
}
case PATTERN_SURFACE:
{
const SurfacePattern& pattern = static_cast<const SurfacePattern&>(aPattern);
cairo_surface_t* surf;
// After this block, |surf| always has an extra cairo reference to be
// destroyed. This makes creating new surfaces or reusing old ones more
// uniform.
if (pattern.mSurface->GetType() == SURFACE_CAIRO) {
const SourceSurfaceCairo* source = static_cast<const SourceSurfaceCairo*>(pattern.mSurface.get());
surf = source->GetSurface();
cairo_surface_reference(surf);
} else if (pattern.mSurface->GetType() == SURFACE_CAIRO_IMAGE) {
const DataSourceSurfaceCairo* source =
static_cast<const DataSourceSurfaceCairo*>(pattern.mSurface.get());
surf = source->GetSurface();
cairo_surface_reference(surf);
} else {
RefPtr<DataSourceSurface> source = pattern.mSurface->GetDataSurface();
surf = cairo_image_surface_create_for_data(source->GetData(),
GfxFormatToCairoFormat(source->GetFormat()),
source->GetSize().width,
source->GetSize().height,
source->Stride());
}
pat = cairo_pattern_create_for_surface(surf);
cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(pattern.mFilter));
cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(pattern.mExtendMode));
cairo_surface_destroy(surf);
break;
}
case PATTERN_LINEAR_GRADIENT:
{
const LinearGradientPattern& pattern = static_cast<const LinearGradientPattern&>(aPattern);
pat = cairo_pattern_create_linear(pattern.mBegin.x, pattern.mBegin.y,
pattern.mEnd.x, pattern.mEnd.y);
MOZ_ASSERT(pattern.mStops->GetBackendType() == BACKEND_CAIRO);
const std::vector<GradientStop>& stops =
static_cast<GradientStopsCairo*>(pattern.mStops.get())->GetStops();
for (size_t i = 0; i < stops.size(); ++i) {
const GradientStop& stop = stops[i];
cairo_pattern_add_color_stop_rgba(pat, stop.offset, stop.color.r,
stop.color.g, stop.color.b,
stop.color.a);
}
break;
}
case PATTERN_RADIAL_GRADIENT:
{
const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern);
pat = cairo_pattern_create_radial(pattern.mCenter1.x, pattern.mCenter1.y, pattern.mRadius1,
pattern.mCenter2.x, pattern.mCenter2.y, pattern.mRadius2);
const std::vector<GradientStop>& stops =
static_cast<GradientStopsCairo*>(pattern.mStops.get())->GetStops();
for (size_t i = 0; i < stops.size(); ++i) {
const GradientStop& stop = stops[i];
cairo_pattern_add_color_stop_rgba(pat, stop.offset, stop.color.r,
stop.color.g, stop.color.b,
stop.color.a);
}
break;
}
default:
{
// We should support all pattern types!
MOZ_ASSERT(false);
}
}
return CAIRO_OPERATOR_OVER;
return pat;
}
cairo_filter_t
GfxFilterToCairoFilter(Filter filter)
static bool
NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions)
{
switch (filter)
{
case FILTER_LINEAR:
return CAIRO_FILTER_BILINEAR;
case FILTER_POINT:
return CAIRO_FILTER_NEAREST;
}
// We pre-multiply colours' alpha by the global alpha, so we don't need to
// use an intermediate surface for them.
if (aPattern.GetType() == PATTERN_COLOR)
return false;
return CAIRO_FILTER_BILINEAR;
}
if (aOptions.mAlpha == 1.0)
return false;
cairo_format_t
GfxFormatToCairoFormat(SurfaceFormat format)
{
switch (format)
{
case FORMAT_B8G8R8A8:
return CAIRO_FORMAT_ARGB32;
case FORMAT_B8G8R8X8:
return CAIRO_FORMAT_RGB24;
case FORMAT_A8:
return CAIRO_FORMAT_A8;
}
return CAIRO_FORMAT_ARGB32;
}
void
GfxMatrixToCairoMatrix(const Matrix& mat, cairo_matrix_t& retval)
{
cairo_matrix_init(&retval, mat._11, mat._12, mat._21, mat._22, mat._31, mat._32);
return true;
}
DrawTargetCairo::DrawTargetCairo()
@ -103,12 +270,33 @@ DrawTargetCairo::DrawTargetCairo()
DrawTargetCairo::~DrawTargetCairo()
{
MarkSnapshotsIndependent();
if (mPathObserver) {
mPathObserver->ForgetDrawTarget();
}
cairo_destroy(mContext);
}
IntSize
DrawTargetCairo::GetSize()
{
return IntSize();
}
TemporaryRef<SourceSurface>
DrawTargetCairo::Snapshot()
{
cairo_surface_t* csurf = cairo_get_target(mContext);
IntSize size;
if (GetCairoSurfaceSize(csurf, size)) {
cairo_content_t content = cairo_surface_get_content(csurf);
RefPtr<SourceSurfaceCairo> surf = new SourceSurfaceCairo(csurf, size,
CairoContentToGfxFormat(content),
this);
AppendSnapshot(surf);
return surf;
}
return NULL;
}
@ -119,6 +307,12 @@ DrawTargetCairo::Flush()
cairo_surface_flush(surf);
}
void
DrawTargetCairo::PrepareForDrawing(cairo_t* aContext, const Path* aPath /* = NULL */)
{
WillChange(aPath);
}
void
DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
const Rect &aDest,
@ -126,12 +320,14 @@ DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
const DrawSurfaceOptions &aSurfOptions,
const DrawOptions &aOptions)
{
AutoPrepareForDrawing prep(this, mContext);
float sx = aSource.Width() / aDest.Width();
float sy = aSource.Height() / aDest.Height();
cairo_matrix_t src_mat;
cairo_matrix_init_scale(&src_mat, sx, sy);
cairo_matrix_translate(&src_mat, -aSource.X(), -aSource.Y());
cairo_matrix_translate(&src_mat, aSource.X(), aSource.Y());
cairo_surface_t* surf = NULL;
if (aSurface->GetType() == SURFACE_CAIRO) {
@ -142,10 +338,147 @@ DrawTargetCairo::DrawSurface(SourceSurface *aSurface,
cairo_pattern_set_matrix(pat, &src_mat);
cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(aSurfOptions.mFilter));
cairo_save(mContext);
cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
cairo_rectangle(mContext, aDest.X(), aDest.Y(),
aDest.Width(), aDest.Height());
cairo_fill(mContext);
cairo_translate(mContext, aDest.X(), aDest.Y());
cairo_set_source(mContext, pat);
cairo_new_path(mContext);
cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height());
cairo_clip(mContext);
cairo_paint_with_alpha(mContext, aOptions.mAlpha);
cairo_restore(mContext);
cairo_pattern_destroy(pat);
}
void
DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface,
const Point &aDest,
const Color &aColor,
const Point &aOffset,
Float aSigma,
CompositionOp aOperator)
{
WillChange();
if (aSurface->GetType() != SURFACE_CAIRO) {
return;
}
SourceSurfaceCairo* source = static_cast<SourceSurfaceCairo*>(aSurface);
Float width = aSurface->GetSize().width;
Float height = aSurface->GetSize().height;
Rect extents(0, 0, width, height);
AlphaBoxBlur blur(extents, IntSize(0, 0),
AlphaBoxBlur::CalculateBlurRadius(Point(aSigma, aSigma)),
NULL, NULL);
if (!blur.GetData()) {
return;
}
IntSize blursize = blur.GetSize();
cairo_surface_t* blursurf = cairo_image_surface_create_for_data(blur.GetData(),
CAIRO_FORMAT_A8,
blursize.width,
blursize.height,
blur.GetStride());
// Draw the source surface into the surface we're going to blur.
cairo_surface_t* surf = source->GetSurface();
cairo_pattern_t* pat = cairo_pattern_create_for_surface(surf);
cairo_t* ctx = cairo_create(blursurf);
cairo_set_source(ctx, pat);
IntRect blurrect = blur.GetRect();
cairo_new_path(ctx);
cairo_rectangle(ctx, blurrect.x, blurrect.y, blurrect.width, blurrect.height);
cairo_clip(ctx);
cairo_paint(ctx);
cairo_destroy(ctx);
// Blur the result, then use that blurred result as a mask to draw the shadow
// colour to the surface.
blur.Blur();
cairo_save(mContext);
cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
cairo_set_source_rgba(mContext, aColor.r, aColor.g, aColor.b, aColor.a);
cairo_identity_matrix(mContext);
cairo_translate(mContext, aDest.x, aDest.y);
cairo_mask_surface(mContext, blursurf, aOffset.x, aOffset.y);
// Now that the shadow has been drawn, we can draw the surface on top.
cairo_set_operator(mContext, GfxOpToCairoOp(aOperator));
cairo_set_source(mContext, pat);
cairo_new_path(mContext);
cairo_rectangle(mContext, 0, 0, width, height);
cairo_clip(mContext);
cairo_paint(mContext);
cairo_restore(mContext);
cairo_pattern_destroy(pat);
}
void
DrawTargetCairo::DrawPattern(const Pattern& aPattern,
const StrokeOptions& aStrokeOptions,
const DrawOptions& aOptions,
DrawPatternType aDrawType)
{
if (!PatternIsCompatible(aPattern)) {
return;
}
cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha);
cairo_set_source(mContext, pat);
if (NeedIntermediateSurface(aPattern, aOptions)) {
cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA);
// Don't want operators to be applied twice
cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
if (aDrawType == DRAW_STROKE) {
SetCairoStrokeOptions(mContext, aStrokeOptions);
cairo_stroke_preserve(mContext);
} else {
cairo_fill_preserve(mContext);
}
cairo_pop_group_to_source(mContext);
// Now draw the content using the desired operator
cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
cairo_paint_with_alpha(mContext, aOptions.mAlpha);
} else {
cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
if (aDrawType == DRAW_STROKE) {
SetCairoStrokeOptions(mContext, aStrokeOptions);
cairo_stroke_preserve(mContext);
} else {
cairo_fill_preserve(mContext);
}
}
cairo_pattern_destroy(pat);
}
@ -155,18 +488,173 @@ DrawTargetCairo::FillRect(const Rect &aRect,
const Pattern &aPattern,
const DrawOptions &aOptions)
{
AutoPrepareForDrawing prep(this, mContext);
cairo_new_path(mContext);
cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height());
cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL);
}
if (aPattern.GetType() == PATTERN_COLOR) {
Color color = static_cast<const ColorPattern&>(aPattern).mColor;
cairo_set_source_rgba(mContext, color.r, color.g,
color.b, color.a);
void
DrawTargetCairo::CopySurface(SourceSurface *aSurface,
const IntRect &aSourceRect,
const IntPoint &aDestination)
{
AutoPrepareForDrawing prep(this, mContext);
}
void
DrawTargetCairo::ClearRect(const Rect& aRect)
{
AutoPrepareForDrawing prep(this, mContext);
cairo_save(mContext);
cairo_new_path(mContext);
cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR);
cairo_rectangle(mContext, aRect.X(), aRect.Y(),
aRect.Width(), aRect.Height());
cairo_fill(mContext);
cairo_restore(mContext);
}
void
DrawTargetCairo::StrokeRect(const Rect &aRect,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
const DrawOptions &aOptions /* = DrawOptions() */)
{
AutoPrepareForDrawing prep(this, mContext);
cairo_new_path(mContext);
cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height());
DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
}
void
DrawTargetCairo::StrokeLine(const Point &aStart,
const Point &aEnd,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
const DrawOptions &aOptions /* = DrawOptions() */)
{
AutoPrepareForDrawing prep(this, mContext);
cairo_new_path(mContext);
cairo_move_to(mContext, aStart.x, aStart.y);
cairo_line_to(mContext, aEnd.x, aEnd.y);
DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
}
void
DrawTargetCairo::Stroke(const Path *aPath,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
const DrawOptions &aOptions /* = DrawOptions() */)
{
AutoPrepareForDrawing prep(this, mContext, aPath);
if (aPath->GetBackendType() != BACKEND_CAIRO)
return;
PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
path->CopyPathTo(mContext, this);
DrawPattern(aPattern, aStrokeOptions, aOptions, DRAW_STROKE);
}
void
DrawTargetCairo::Fill(const Path *aPath,
const Pattern &aPattern,
const DrawOptions &aOptions /* = DrawOptions() */)
{
AutoPrepareForDrawing prep(this, mContext, aPath);
if (aPath->GetBackendType() != BACKEND_CAIRO)
return;
PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
path->CopyPathTo(mContext, this);
DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL);
}
void
DrawTargetCairo::FillGlyphs(ScaledFont *aFont,
const GlyphBuffer &aBuffer,
const Pattern &aPattern,
const DrawOptions &aOptions)
{
AutoPrepareForDrawing prep(this, mContext);
if (aFont->GetType() != FONT_CAIRO)
return;
ScaledFontCairo* scaledFont = static_cast<ScaledFontCairo*>(aFont);
cairo_set_scaled_font(mContext, scaledFont->GetCairoScaledFont());
cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha);
cairo_set_source(mContext, pat);
cairo_pattern_destroy(pat);
// Convert our GlyphBuffer into an array of Cairo glyphs.
std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
}
cairo_fill(mContext);
cairo_show_glyphs(mContext, &glyphs[0], aBuffer.mNumGlyphs);
}
void
DrawTargetCairo::Mask(const Pattern &aSource,
const Pattern &aMask,
const DrawOptions &aOptions /* = DrawOptions() */)
{
AutoPrepareForDrawing prep(this, mContext);
}
void
DrawTargetCairo::PushClip(const Path *aPath)
{
}
void
DrawTargetCairo::PushClipRect(const Rect& aRect)
{
}
void
DrawTargetCairo::PopClip()
{
}
TemporaryRef<PathBuilder>
DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FILL_WINDING */) const
{
RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(mContext,
const_cast<DrawTargetCairo*>(this),
aFillRule);
// Creating a PathBuilder implicitly resets our mPathObserver, as it calls
// SetPathObserver() on us. Since this guarantees our old path is saved off,
// it's safe to reset the path here.
cairo_new_path(mContext);
return builder;
}
TemporaryRef<GradientStops>
DrawTargetCairo::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const
{
RefPtr<GradientStopsCairo> stops = new GradientStopsCairo(aStops, aNumStops);
return stops;
}
TemporaryRef<SourceSurface>
@ -180,8 +668,7 @@ DrawTargetCairo::CreateSourceSurfaceFromData(unsigned char *aData,
aSize.width,
aSize.height,
aStride);
RefPtr<SourceSurfaceCairo> source_surf = new SourceSurfaceCairo();
source_surf->InitFromSurface(surf, aSize, aFormat);
RefPtr<SourceSurfaceCairo> source_surf = new SourceSurfaceCairo(surf, aSize, aFormat);
cairo_surface_destroy(surf);
return source_surf;
}
@ -189,12 +676,38 @@ DrawTargetCairo::CreateSourceSurfaceFromData(unsigned char *aData,
TemporaryRef<SourceSurface>
DrawTargetCairo::OptimizeSourceSurface(SourceSurface *aSurface) const
{
return NULL;
return aSurface;
}
TemporaryRef<SourceSurface>
DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
{
if (aSurface.mType == NATIVE_SURFACE_CAIRO_SURFACE) {
IntSize size;
cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface);
if (GetCairoSurfaceSize(surf, size)) {
RefPtr<SourceSurfaceCairo> source =
new SourceSurfaceCairo(surf, size, aSurface.mFormat);
return source;
}
}
return NULL;
}
TemporaryRef<DrawTarget>
DrawTargetCairo::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
{
cairo_surface_t* similar = cairo_surface_create_similar(cairo_get_target(mContext),
GfxFormatToCairoContent(aFormat),
aSize.width, aSize.height);
if (!cairo_surface_status(similar)) {
RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
target->Init(similar);
return target;
}
return NULL;
}
@ -206,13 +719,86 @@ DrawTargetCairo::Init(cairo_surface_t* aSurface)
return true;
}
void *
DrawTargetCairo::GetNativeSurface(NativeSurfaceType aType)
{
if (aType == NATIVE_SURFACE_CAIRO_SURFACE) {
return cairo_get_target(mContext);
}
return NULL;
}
void
DrawTargetCairo::MarkSnapshotsIndependent()
{
// Make a copy of the vector, since MarkIndependent implicitly modifies mSnapshots.
std::vector<SourceSurfaceCairo*> snapshots = mSnapshots;
for (std::vector<SourceSurfaceCairo*>::iterator iter = snapshots.begin();
iter != snapshots.end();
++iter) {
(*iter)->MarkIndependent();
}
}
void
DrawTargetCairo::AppendSnapshot(SourceSurfaceCairo* aSnapshot)
{
mSnapshots.push_back(aSnapshot);
}
void
DrawTargetCairo::RemoveSnapshot(SourceSurfaceCairo* aSnapshot)
{
std::vector<SourceSurfaceCairo*>::iterator iter = std::find(mSnapshots.begin(),
mSnapshots.end(),
aSnapshot);
if (iter != mSnapshots.end()) {
mSnapshots.erase(iter);
}
}
void
DrawTargetCairo::WillChange(const Path* aPath /* = NULL */)
{
if (!mSnapshots.empty()) {
for (std::vector<SourceSurfaceCairo*>::iterator iter = mSnapshots.begin();
iter != mSnapshots.end(); ++iter) {
(*iter)->DrawTargetWillChange();
}
// All snapshots will now have copied data.
mSnapshots.clear();
}
if (aPath && mPathObserver && !mPathObserver->ContainsPath(aPath)) {
mPathObserver->PathWillChange();
mPathObserver = NULL;
}
}
void
DrawTargetCairo::SetPathObserver(CairoPathContext* aPathObserver)
{
if (mPathObserver && mPathObserver != aPathObserver) {
mPathObserver->PathWillChange();
}
mPathObserver = aPathObserver;
}
void
DrawTargetCairo::SetTransform(const Matrix& aTransform)
{
cairo_matrix_t mat;
GfxMatrixToCairoMatrix(aTransform, mat);
cairo_set_matrix(mContext, &mat);
// We're about to logically change our transformation. Our current path will
// need to change, because Cairo stores paths in device space.
if (mPathObserver) {
mPathObserver->MatrixWillChange(aTransform);
}
mTransform = aTransform;
cairo_matrix_t mat;
GfxMatrixToCairoMatrix(mTransform, mat);
cairo_set_matrix(mContext, &mat);
}
}

View File

@ -39,10 +39,38 @@
#include "2D.h"
#include "cairo.h"
#include "PathCairo.h"
#include <vector>
namespace mozilla {
namespace gfx {
class SourceSurfaceCairo;
class GradientStopsCairo : public GradientStops
{
public:
GradientStopsCairo(GradientStop* aStops, uint32_t aNumStops)
{
for (uint32_t i = 0; i < aNumStops; ++i) {
mStops.push_back(aStops[i]);
}
}
virtual ~GradientStopsCairo() {}
const std::vector<GradientStop>& GetStops() const
{
return mStops;
}
virtual BackendType GetBackendType() const { return BACKEND_CAIRO; }
private:
std::vector<GradientStop> mStops;
};
class DrawTargetCairo : public DrawTarget
{
public:
@ -51,7 +79,7 @@ public:
virtual BackendType GetType() const { return BACKEND_CAIRO; }
virtual TemporaryRef<SourceSurface> Snapshot();
virtual IntSize GetSize() { return IntSize(); }
virtual IntSize GetSize();
virtual void Flush();
virtual void DrawSurface(SourceSurface *aSurface,
@ -64,16 +92,13 @@ public:
const Color &aColor,
const Point &aOffset,
Float aSigma,
CompositionOp aOperator)
{ }
CompositionOp aOperator);
virtual void ClearRect(const Rect &aRect)
{ }
virtual void ClearRect(const Rect &aRect);
virtual void CopySurface(SourceSurface *aSurface,
const IntRect &aSourceRect,
const IntPoint &aDestination)
{ }
const IntPoint &aDestination);
virtual void FillRect(const Rect &aRect,
const Pattern &aPattern,
@ -81,41 +106,35 @@ public:
virtual void StrokeRect(const Rect &aRect,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
const DrawOptions &aOptions = DrawOptions())
{ return; }
const DrawOptions &aOptions = DrawOptions());
virtual void StrokeLine(const Point &aStart,
const Point &aEnd,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
const DrawOptions &aOptions = DrawOptions())
{ return; }
const DrawOptions &aOptions = DrawOptions());
virtual void Stroke(const Path *aPath,
const Pattern &aPattern,
const StrokeOptions &aStrokeOptions = StrokeOptions(),
const DrawOptions &aOptions = DrawOptions())
{ return; }
const DrawOptions &aOptions = DrawOptions());
virtual void Fill(const Path *aPath,
const Pattern &aPattern,
const DrawOptions &aOptions = DrawOptions())
{ return; }
const DrawOptions &aOptions = DrawOptions());
virtual void FillGlyphs(ScaledFont *aFont,
const GlyphBuffer &aBuffer,
const Pattern &aPattern,
const DrawOptions &aOptions)
{ return; }
const DrawOptions &aOptions);
virtual void Mask(const Pattern &aSource,
const Pattern &aMask,
const DrawOptions &aOptions = DrawOptions())
{ return; }
const DrawOptions &aOptions = DrawOptions());
virtual void PushClip(const Path *aPath) { }
virtual void PushClipRect(const Rect &aRect) { }
virtual void PopClip() { }
virtual void PushClip(const Path *aPath);
virtual void PushClipRect(const Rect &aRect);
virtual void PopClip();
virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const { return NULL; }
virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const;
virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
const IntSize &aSize,
@ -125,22 +144,51 @@ public:
virtual TemporaryRef<SourceSurface>
CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const;
virtual TemporaryRef<DrawTarget>
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
{ return NULL; }
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const;
virtual TemporaryRef<GradientStops> CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode = EXTEND_CLAMP) const
{ return NULL; }
virtual TemporaryRef<GradientStops>
CreateGradientStops(GradientStop *aStops,
uint32_t aNumStops,
ExtendMode aExtendMode = EXTEND_CLAMP) const;
virtual void *GetNativeSurface(NativeSurfaceType aType)
{ return NULL; }
virtual void SetTransform(const Matrix& aTransform);
virtual void *GetNativeSurface(NativeSurfaceType aType);
bool Init(cairo_surface_t* aSurface);
private:
void SetPathObserver(CairoPathContext* aPathObserver);
virtual void SetTransform(const Matrix& aTransform);
// Call to set up aContext for drawing (with the current transform, etc).
// Pass the path you're going to be using if you have one.
// Implicitly calls WillChange(aPath).
void PrepareForDrawing(cairo_t* aContext, const Path* aPath = NULL);
private: // methods
enum DrawPatternType { DRAW_FILL, DRAW_STROKE };
void DrawPattern(const Pattern& aPattern,
const StrokeOptions& aStrokeOptions,
const DrawOptions& aOptions,
DrawPatternType aDrawType);
// Copy-on-write support for snapshot surfaces.
friend class SourceSurfaceCairo;
void AppendSnapshot(SourceSurfaceCairo* aSnapshot);
void RemoveSnapshot(SourceSurfaceCairo* aSnapshot);
// Call before you make any changes to the backing surface with which this
// context is associated. Pass the path you're going to be using if you have
// one.
void WillChange(const Path* aPath = NULL);
// Call if there is any reason to disassociate all snapshots from this draw
// target; for example, because we're going to be destroyed.
void MarkSnapshotsIndependent();
private: // data
cairo_t* mContext;
std::vector<SourceSurfaceCairo*> mSnapshots;
mutable RefPtr<CairoPathContext> mPathObserver;
};
}

View File

@ -39,6 +39,7 @@
#ifdef USE_CAIRO
#include "DrawTargetCairo.h"
#include "ScaledFontCairo.h"
#endif
#ifdef USE_SKIA
@ -139,6 +140,10 @@ Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSiz
return new ScaledFontSkia(static_cast<gfxFont*>(aNativeFont.mFont), aSize);
}
#endif
case NATIVE_FONT_CAIRO_FONT_FACE:
{
return new ScaledFontCairo(static_cast<gfxFont*>(aNativeFont.mFont));
}
default:
gfxWarning() << "Invalid native font type specified.";
return NULL;

237
gfx/2d/HelpersCairo.h Normal file
View File

@ -0,0 +1,237 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* ***** 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 Corporation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 ***** */
#ifndef MOZILLA_GFX_HELPERSCAIRO_H_
#define MOZILLA_GFX_HELPERSCAIRO_H_
#include "2D.h"
#include "cairo.h"
namespace mozilla {
namespace gfx {
static inline cairo_operator_t
GfxOpToCairoOp(CompositionOp op)
{
switch (op)
{
case OP_OVER:
return CAIRO_OPERATOR_OVER;
case OP_ADD:
return CAIRO_OPERATOR_ADD;
case OP_ATOP:
return CAIRO_OPERATOR_ATOP;
case OP_OUT:
return CAIRO_OPERATOR_OUT;
case OP_IN:
return CAIRO_OPERATOR_IN;
case OP_SOURCE:
return CAIRO_OPERATOR_SOURCE;
case OP_DEST_IN:
return CAIRO_OPERATOR_DEST_IN;
case OP_DEST_OUT:
return CAIRO_OPERATOR_DEST_OUT;
case OP_DEST_OVER:
return CAIRO_OPERATOR_DEST_OVER;
case OP_DEST_ATOP:
return CAIRO_OPERATOR_DEST_ATOP;
case OP_XOR:
return CAIRO_OPERATOR_XOR;
case OP_COUNT:
break;
}
return CAIRO_OPERATOR_OVER;
}
static inline cairo_filter_t
GfxFilterToCairoFilter(Filter filter)
{
switch (filter)
{
case FILTER_LINEAR:
return CAIRO_FILTER_BILINEAR;
case FILTER_POINT:
return CAIRO_FILTER_NEAREST;
}
return CAIRO_FILTER_BILINEAR;
}
static inline cairo_extend_t
GfxExtendToCairoExtend(ExtendMode extend)
{
switch (extend)
{
case EXTEND_CLAMP:
return CAIRO_EXTEND_PAD;
case EXTEND_REPEAT:
return CAIRO_EXTEND_REPEAT;
case EXTEND_REFLECT:
return CAIRO_EXTEND_REFLECT;
}
return CAIRO_EXTEND_PAD;
}
static inline cairo_format_t
GfxFormatToCairoFormat(SurfaceFormat format)
{
switch (format)
{
case FORMAT_B8G8R8A8:
return CAIRO_FORMAT_ARGB32;
case FORMAT_B8G8R8X8:
return CAIRO_FORMAT_RGB24;
case FORMAT_A8:
return CAIRO_FORMAT_A8;
}
return CAIRO_FORMAT_ARGB32;
}
static inline cairo_content_t
GfxFormatToCairoContent(SurfaceFormat format)
{
switch (format)
{
case FORMAT_B8G8R8A8:
return CAIRO_CONTENT_COLOR_ALPHA;
case FORMAT_B8G8R8X8:
return CAIRO_CONTENT_COLOR;
case FORMAT_A8:
return CAIRO_CONTENT_ALPHA;
}
return CAIRO_CONTENT_COLOR_ALPHA;
}
static inline cairo_line_join_t
GfxLineJoinToCairoLineJoin(JoinStyle style)
{
switch (style)
{
case JOIN_BEVEL:
return CAIRO_LINE_JOIN_BEVEL;
case JOIN_ROUND:
return CAIRO_LINE_JOIN_ROUND;
case JOIN_MITER:
return CAIRO_LINE_JOIN_MITER;
case JOIN_MITER_OR_BEVEL:
return CAIRO_LINE_JOIN_MITER;
}
return CAIRO_LINE_JOIN_MITER;
}
static inline cairo_line_cap_t
GfxLineCapToCairoLineCap(CapStyle style)
{
switch (style)
{
case CAP_BUTT:
return CAIRO_LINE_CAP_BUTT;
case CAP_ROUND:
return CAIRO_LINE_CAP_ROUND;
case CAP_SQUARE:
return CAIRO_LINE_CAP_SQUARE;
}
return CAIRO_LINE_CAP_BUTT;
}
static inline SurfaceFormat
CairoContentToGfxFormat(cairo_content_t content)
{
switch (content)
{
case CAIRO_CONTENT_COLOR_ALPHA:
return FORMAT_B8G8R8A8;
case CAIRO_CONTENT_COLOR:
return FORMAT_B8G8R8X8;
case CAIRO_CONTENT_ALPHA:
return FORMAT_A8;
}
return FORMAT_B8G8R8A8;
}
static inline void
GfxMatrixToCairoMatrix(const Matrix& mat, cairo_matrix_t& retval)
{
cairo_matrix_init(&retval, mat._11, mat._12, mat._21, mat._22, mat._31, mat._32);
}
static inline void
SetCairoStrokeOptions(cairo_t* aCtx, const StrokeOptions& aStrokeOptions)
{
cairo_set_line_width(aCtx, aStrokeOptions.mLineWidth);
cairo_set_miter_limit(aCtx, aStrokeOptions.mMiterLimit);
if (aStrokeOptions.mDashPattern) {
// Convert array of floats to array of doubles
std::vector<double> dashes(aStrokeOptions.mDashLength);
for (size_t i = 0; i < aStrokeOptions.mDashLength; ++i) {
dashes[i] = aStrokeOptions.mDashPattern[i];
}
cairo_set_dash(aCtx, &dashes[0], aStrokeOptions.mDashLength,
aStrokeOptions.mDashOffset);
}
cairo_set_line_join(aCtx, GfxLineJoinToCairoLineJoin(aStrokeOptions.mLineJoin));
cairo_set_line_cap(aCtx, GfxLineCapToCairoLineCap(aStrokeOptions.mLineCap));
}
static inline cairo_fill_rule_t
GfxFillRuleToCairoFillRule(FillRule rule)
{
switch (rule)
{
case FILL_WINDING:
return CAIRO_FILL_RULE_WINDING;
case FILL_EVEN_ODD:
return CAIRO_FILL_RULE_EVEN_ODD;
}
return CAIRO_FILL_RULE_WINDING;
}
}
}
#endif /* MOZILLA_GFX_HELPERSCAIRO_H_ */

View File

@ -68,7 +68,9 @@ CPPSRCS = \
Factory.cpp \
Matrix.cpp \
DrawTargetCairo.cpp \
ScaledFontCairo.cpp \
SourceSurfaceCairo.cpp \
PathCairo.cpp \
Blur.cpp \
$(NULL)

363
gfx/2d/PathCairo.cpp Normal file
View File

@ -0,0 +1,363 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* ***** 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 Corporation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 ***** */
#include "PathCairo.h"
#include <math.h>
#include "DrawTargetCairo.h"
#include "Logging.h"
#include "PathHelpers.h"
#include "HelpersCairo.h"
namespace mozilla {
namespace gfx {
CairoPathContext::CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget,
FillRule aFillRule,
const Matrix& aTransform /* = Matrix() */)
: mTransform(aTransform)
, mContext(aCtx)
, mDrawTarget(aDrawTarget)
, mFillRule(aFillRule)
{
cairo_reference(mContext);
cairo_set_fill_rule(mContext, GfxFillRuleToCairoFillRule(mFillRule));
// If we don't have an identity transformation, we need to have a separate
// context from the draw target, because we can't set a transformation on its
// context.
if (mDrawTarget && !mTransform.IsIdentity()) {
DuplicateContextAndPath(mTransform);
ForgetDrawTarget();
} else if (mDrawTarget) {
mDrawTarget->SetPathObserver(this);
}
}
CairoPathContext::~CairoPathContext()
{
if (mDrawTarget) {
mDrawTarget->SetPathObserver(NULL);
}
cairo_destroy(mContext);
}
void
CairoPathContext::DuplicateContextAndPath(const Matrix& aMatrix /* = Matrix() */)
{
// Duplicate the path.
cairo_path_t* path = cairo_copy_path(mContext);
cairo_fill_rule_t rule = cairo_get_fill_rule(mContext);
// Duplicate the context.
cairo_surface_t* surf = cairo_get_target(mContext);
cairo_destroy(mContext);
mContext = cairo_create(surf);
// Transform the context.
cairo_matrix_t matrix;
GfxMatrixToCairoMatrix(aMatrix, matrix);
cairo_transform(mContext, &matrix);
// Add the path, and throw away our duplicate.
cairo_append_path(mContext, path);
cairo_set_fill_rule(mContext, rule);
cairo_path_destroy(path);
}
void
CairoPathContext::PathWillChange()
{
// Once we've copied out the context's path, there's no use to holding on to
// the draw target. Thus, there's nothing for us to do if we're independent
// of the draw target, since we'll have already copied out the context's
// path.
if (mDrawTarget) {
// The context we point to is going to change from under us. To continue
// using this path, we need to copy it to a new context.
DuplicateContextAndPath();
ForgetDrawTarget();
}
}
void
CairoPathContext::MatrixWillChange(const Matrix& aNewMatrix)
{
// Cairo paths are stored in device space. Since we logically operate in user
// space, we want to make it so our path will be in the same location if and
// when our path is copied out.
// To effect this, we copy out our path (which, in Cairo, implicitly converts
// to user space), then temporarily set the context to have the new
// transform. We then set the path, which ensures that the points are all
// transformed correctly. Finally, we set the matrix back to its original
// value.
cairo_path_t* path = cairo_copy_path(mContext);
cairo_matrix_t origMatrix;
cairo_get_matrix(mContext, &origMatrix);
cairo_matrix_t newMatrix;
GfxMatrixToCairoMatrix(aNewMatrix, newMatrix);
cairo_set_matrix(mContext, &newMatrix);
cairo_new_path(mContext);
cairo_append_path(mContext, path);
cairo_path_destroy(path);
cairo_set_matrix(mContext, &origMatrix);
}
void
CairoPathContext::CopyPathTo(cairo_t* aToContext)
{
if (aToContext != mContext) {
cairo_set_fill_rule(aToContext, GfxFillRuleToCairoFillRule(mFillRule));
cairo_matrix_t origMat;
cairo_get_matrix(aToContext, &origMat);
cairo_matrix_t mat;
GfxMatrixToCairoMatrix(mTransform, mat);
cairo_transform(aToContext, &mat);
// cairo_copy_path gives us a user-space copy of the path, so we don't have
// to worry about transformations here.
cairo_path_t* path = cairo_copy_path(mContext);
cairo_new_path(aToContext);
cairo_append_path(aToContext, path);
cairo_path_destroy(path);
cairo_set_matrix(aToContext, &origMat);
}
}
void
CairoPathContext::ForgetDrawTarget()
{
mDrawTarget = NULL;
}
bool
CairoPathContext::ContainsPath(const Path* aPath)
{
if (aPath->GetBackendType() != BACKEND_CAIRO) {
return false;
}
const PathCairo* path = static_cast<const PathCairo*>(aPath);
RefPtr<CairoPathContext> ctx = const_cast<PathCairo*>(path)->GetPathContext();
return ctx == this;
}
PathBuilderCairo::PathBuilderCairo(CairoPathContext* aPathContext,
const Matrix& aTransform /* = Matrix() */)
: mFillRule(aPathContext->GetFillRule())
{
RefPtr<DrawTargetCairo> drawTarget = aPathContext->GetDrawTarget();
mPathContext = new CairoPathContext(*aPathContext, drawTarget, mFillRule,
aPathContext->GetTransform() * aTransform);
// We need to ensure that we are allowed to modify the path currently set on
// aPathContext. If we don't have a draw target, CairoPathContext's
// constructor has no way to make aPathContext duplicate its path (normally,
// calling drawTarget->SetPathObserver() would do so). In this case, we
// explicitly make aPathContext copy out its context and path, leaving our
// path alone.
if (!drawTarget) {
aPathContext->DuplicateContextAndPath();
}
}
PathBuilderCairo::PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule)
: mPathContext(new CairoPathContext(aCtx, aDrawTarget, aFillRule))
, mFillRule(aFillRule)
{}
void
PathBuilderCairo::MoveTo(const Point &aPoint)
{
cairo_move_to(*mPathContext, aPoint.x, aPoint.y);
}
void
PathBuilderCairo::LineTo(const Point &aPoint)
{
cairo_line_to(*mPathContext, aPoint.x, aPoint.y);
}
void
PathBuilderCairo::BezierTo(const Point &aCP1,
const Point &aCP2,
const Point &aCP3)
{
cairo_curve_to(*mPathContext, aCP1.x, aCP1.y, aCP2.x, aCP2.y, aCP3.x, aCP3.y);
}
void
PathBuilderCairo::QuadraticBezierTo(const Point &aCP1,
const Point &aCP2)
{
// We need to elevate the degree of this quadratic Bézier to cubic, so we're
// going to add an intermediate control point, and recompute control point 1.
// The first and last control points remain the same.
// This formula can be found on http://fontforge.sourceforge.net/bezier.html
Point CP0 = CurrentPoint();
Point CP1 = (CP0 + aCP1 * 2.0) / 3.0;
Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
Point CP3 = aCP2;
cairo_curve_to(*mPathContext, CP1.x, CP1.y, CP2.x, CP2.y, CP3.x, CP3.y);
}
void
PathBuilderCairo::Close()
{
cairo_close_path(*mPathContext);
}
void
PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
float aEndAngle, bool aAntiClockwise)
{
ArcToBezier(this, aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise);
}
Point
PathBuilderCairo::CurrentPoint() const
{
double x, y;
cairo_get_current_point(*mPathContext, &x, &y);
return Point(x, y);
}
TemporaryRef<Path>
PathBuilderCairo::Finish()
{
RefPtr<PathCairo> path = new PathCairo(*mPathContext,
mPathContext->GetDrawTarget(),
mFillRule,
mPathContext->GetTransform());
return path;
}
TemporaryRef<CairoPathContext>
PathBuilderCairo::GetPathContext()
{
return mPathContext;
}
PathCairo::PathCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule, const Matrix& aTransform)
: mPathContext(new CairoPathContext(aCtx, aDrawTarget, aFillRule, aTransform))
, mFillRule(aFillRule)
{}
TemporaryRef<PathBuilder>
PathCairo::CopyToBuilder(FillRule aFillRule) const
{
// Note: This PathBuilderCairo constructor causes our mPathContext to copy
// out the path, since the path builder is going to change the path on us.
RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(mPathContext);
return builder;
}
TemporaryRef<PathBuilder>
PathCairo::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
{
// Note: This PathBuilderCairo constructor causes our mPathContext to copy
// out the path, since the path builder is going to change the path on us.
RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(mPathContext,
aTransform);
return builder;
}
bool
PathCairo::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
{
Matrix inverse = aTransform;
inverse.Invert();
Point transformed = inverse * aPoint;
return cairo_in_fill(*mPathContext, transformed.x, transformed.y);
}
Rect
PathCairo::GetBounds(const Matrix &aTransform) const
{
double x1, y1, x2, y2;
cairo_path_extents(*mPathContext, &x1, &y1, &x2, &y2);
Rect bounds(x1, y1, x2 - x1, y2 - y1);
return aTransform.TransformBounds(bounds);
}
Rect
PathCairo::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
const Matrix &aTransform) const
{
double x1, y1, x2, y2;
SetCairoStrokeOptions(*mPathContext, aStrokeOptions);
cairo_stroke_extents(*mPathContext, &x1, &y1, &x2, &y2);
Rect bounds(x1, y1, x2 - x1, y2 - y1);
return aTransform.TransformBounds(bounds);
}
TemporaryRef<CairoPathContext>
PathCairo::GetPathContext()
{
return mPathContext;
}
void
PathCairo::CopyPathTo(cairo_t* aContext, DrawTargetCairo* aDrawTarget)
{
if (mPathContext->GetContext() != aContext) {
mPathContext->CopyPathTo(aContext);
// Since aDrawTarget wants us to be the current path on its context, we
// should also listen to it for updates to that path (as an optimization).
// The easiest way to do this is to just recreate mPathContext, since it
// registers with aDrawTarget for updates.
mPathContext = new CairoPathContext(aContext, aDrawTarget,
mPathContext->GetFillRule(),
mPathContext->GetTransform());
}
}
}
}

194
gfx/2d/PathCairo.h Normal file
View File

@ -0,0 +1,194 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* ***** 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 Corporation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 ***** */
#ifndef MOZILLA_GFX_PATH_CAIRO_H_
#define MOZILLA_GFX_PATH_CAIRO_H_
#include "2D.h"
#include "cairo.h"
namespace mozilla {
namespace gfx {
class DrawTargetCairo;
// A reference to a cairo context that can maintain and set a path.
//
// This class exists to make it possible for us to not construct paths manually
// using cairo_path_t, which in the common case is a speed and memory
// optimization (as the cairo_t maintains the path for us, and we don't have to
// use cairo_append_path). Instead, we can share a cairo_t with a DrawTarget,
// and have it inform us when we need to make a copy of the path.
//
// Exactly one Path* object represents the current path on a given DrawTarget's
// context. That Path* object registers its CairoPathContext with the
// DrawTarget it's associated with. If that DrawTarget is going to change its
// path, it has to tell the CairoPathContext beforehand so the path can be
// saved off.
// The path ownership is transferred to every new instance of CairoPathContext
// in the constructor. We inform the draw target of the new context object,
// which causes us to save off a copy of the path, as we're not going to be
// informed upon changes any more.
// Any transformation on aCtx is not applied to this path, though a path can be
// transformed separately from its context by passing a matrix to the
// constructor.
class CairoPathContext : public RefCounted<CairoPathContext>
{
public:
// Construct a CairoPathContext and set it to be the path observer of
// aDrawTarget. Optionally, this path can be transformed by aMatrix.
CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget,
FillRule aFillRule,
const Matrix& aMatrix = Matrix());
~CairoPathContext();
// Copy the path on mContext to be the path on aToContext, if they aren't the
// same.
void CopyPathTo(cairo_t* aToContext);
// This method must be called by the draw target before it changes the path
// currently on the cairo context.
void PathWillChange();
// This method must be called by the draw target whenever it is going to
// change the current transformation on mContext.
void MatrixWillChange(const Matrix& aMatrix);
// This method must be called as the draw target is dying. In this case, we
// forget our reference to the draw target, and become the only reference to
// our context.
void ForgetDrawTarget();
// Create a duplicate context, and copy this path to that context. Optionally,
// the new context can be transformed.
void DuplicateContextAndPath(const Matrix& aMatrix = Matrix());
// Returns true if this CairoPathContext represents path.
bool ContainsPath(const Path* path);
cairo_t* GetContext() const { return mContext; }
DrawTargetCairo* GetDrawTarget() const { return mDrawTarget; }
Matrix GetTransform() const { return mTransform; }
FillRule GetFillRule() const { return mFillRule; }
operator cairo_t* () const { return mContext; }
private: // methods
CairoPathContext(const CairoPathContext&) MOZ_DELETE;
private: // data
Matrix mTransform;
cairo_t* mContext;
// Not a RefPtr to avoid cycles.
DrawTargetCairo* mDrawTarget;
FillRule mFillRule;
};
class PathBuilderCairo : public PathBuilder
{
public:
// This constructor implicitly takes ownership of aCtx by calling
// aDrawTarget->SetPathObserver(). Therefore, if the draw target has a path
// observer, this constructor will cause it to copy out its path.
// The path currently set on aCtx is not changed.
PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule);
// This constructor, called with a CairoPathContext*, implicitly takes
// ownership of the path, and therefore makes aPathContext copy out its path
// regardless of whether it has a pointer to a DrawTargetCairo.
// The path currently set on aPathContext is not changed.
explicit PathBuilderCairo(CairoPathContext* aPathContext,
const Matrix& aTransform = Matrix());
virtual void MoveTo(const Point &aPoint);
virtual void LineTo(const Point &aPoint);
virtual void BezierTo(const Point &aCP1,
const Point &aCP2,
const Point &aCP3);
virtual void QuadraticBezierTo(const Point &aCP1,
const Point &aCP2);
virtual void Close();
virtual void Arc(const Point &aOrigin, float aRadius, float aStartAngle,
float aEndAngle, bool aAntiClockwise = false);
virtual Point CurrentPoint() const;
virtual TemporaryRef<Path> Finish();
TemporaryRef<CairoPathContext> GetPathContext();
private: // methods
void SetFillRule(FillRule aFillRule);
private: // data
RefPtr<CairoPathContext> mPathContext;
FillRule mFillRule;
};
class PathCairo : public Path
{
public:
PathCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule, const Matrix& aTransform);
virtual BackendType GetBackendType() const { return BACKEND_CAIRO; }
virtual TemporaryRef<PathBuilder> CopyToBuilder(FillRule aFillRule = FILL_WINDING) const;
virtual TemporaryRef<PathBuilder> TransformedCopyToBuilder(const Matrix &aTransform,
FillRule aFillRule = FILL_WINDING) const;
virtual bool ContainsPoint(const Point &aPoint, const Matrix &aTransform) const;
virtual Rect GetBounds(const Matrix &aTransform = Matrix()) const;
virtual Rect GetStrokedBounds(const StrokeOptions &aStrokeOptions,
const Matrix &aTransform = Matrix()) const;
virtual FillRule GetFillRule() const { return mFillRule; }
TemporaryRef<CairoPathContext> GetPathContext();
// Set this path to be the current path for aContext (if it's not already
// aContext's path). You must pass the draw target associated with the
// context as aDrawTarget.
void CopyPathTo(cairo_t* aContext, DrawTargetCairo* aDrawTarget);
private:
RefPtr<CairoPathContext> mPathContext;
FillRule mFillRule;
};
}
}
#endif /* MOZILLA_GFX_PATH_CAIRO_H_ */

View File

@ -0,0 +1,97 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* ***** 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 Corporation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 ***** */
#include "ScaledFontCairo.h"
#include "cairo.h"
#include "PathCairo.h"
#include "gfxFont.h"
#include <vector>
using namespace std;
namespace mozilla {
namespace gfx {
ScaledFontCairo::ScaledFontCairo(gfxFont* aFont)
{
mScaledFont = aFont->GetCairoScaledFont();
cairo_scaled_font_reference(mScaledFont);
}
ScaledFontCairo::~ScaledFontCairo()
{
cairo_scaled_font_destroy(mScaledFont);
}
TemporaryRef<Path>
ScaledFontCairo::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
{
if (aTarget->GetType() != BACKEND_CAIRO) {
return NULL;
}
RefPtr<PathBuilder> builder_iface = aTarget->CreatePathBuilder();
PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(builder_iface.get());
// Manually build the path for the PathBuilder.
RefPtr<CairoPathContext> context = builder->GetPathContext();
cairo_set_scaled_font(*context, mScaledFont);
// Convert our GlyphBuffer into an array of Cairo glyphs.
std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
}
cairo_glyph_path(*context, &glyphs[0], aBuffer.mNumGlyphs);
return builder->Finish();
}
cairo_scaled_font_t*
ScaledFontCairo::GetCairoScaledFont()
{
return mScaledFont;
}
}
}

67
gfx/2d/ScaledFontCairo.h Normal file
View File

@ -0,0 +1,67 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* ***** 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 Corporation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 ***** */
#ifndef MOZILLA_GFX_SCALEDFONTCAIRO_H_
#define MOZILLA_GFX_SCALEDFONTCAIRO_H_
#include "2D.h"
class gfxFont;
typedef struct _cairo_scaled_font cairo_scaled_font_t;
namespace mozilla {
namespace gfx {
class ScaledFontCairo : public ScaledFont
{
public:
ScaledFontCairo(gfxFont* aFont);
virtual ~ScaledFontCairo();
virtual FontType GetType() const { return FONT_CAIRO; }
virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
cairo_scaled_font_t* GetCairoScaledFont();
private:
cairo_scaled_font_t* mScaledFont;
};
}
}
#endif /* MOZILLA_GFX_SCALEDFONTCAIRO_H_ */

View File

@ -36,18 +36,76 @@
* ***** END LICENSE BLOCK ***** */
#include "SourceSurfaceCairo.h"
#include "DrawTargetCairo.h"
#include "cairo.h"
namespace mozilla {
namespace gfx {
SourceSurfaceCairo::SourceSurfaceCairo()
static cairo_format_t
GfxFormatToCairoFormat(SurfaceFormat format)
{
switch (format)
{
case FORMAT_B8G8R8A8:
return CAIRO_FORMAT_ARGB32;
case FORMAT_B8G8R8X8:
return CAIRO_FORMAT_RGB24;
case FORMAT_A8:
return CAIRO_FORMAT_A8;
}
return CAIRO_FORMAT_ARGB32;
}
static SurfaceFormat
CairoFormatToSurfaceFormat(cairo_format_t format)
{
switch (format)
{
case CAIRO_FORMAT_ARGB32:
return FORMAT_B8G8R8A8;
case CAIRO_FORMAT_RGB24:
return FORMAT_B8G8R8X8;
case CAIRO_FORMAT_A8:
return FORMAT_A8;
}
return FORMAT_B8G8R8A8;
}
static cairo_content_t
GfxFormatToCairoContent(SurfaceFormat format)
{
switch(format)
{
case FORMAT_B8G8R8A8:
return CAIRO_CONTENT_COLOR_ALPHA;
case FORMAT_B8G8R8X8:
return CAIRO_CONTENT_COLOR;
case FORMAT_A8:
return CAIRO_CONTENT_ALPHA;
}
return CAIRO_CONTENT_COLOR_ALPHA;
}
SourceSurfaceCairo::SourceSurfaceCairo(cairo_surface_t* aSurface,
const IntSize& aSize,
const SurfaceFormat& aFormat,
DrawTargetCairo* aDrawTarget /* = NULL */)
: mSize(aSize)
, mFormat(aFormat)
, mSurface(aSurface)
, mDrawTarget(aDrawTarget)
{
cairo_surface_reference(mSurface);
}
SourceSurfaceCairo::~SourceSurfaceCairo()
{
MarkIndependent();
cairo_surface_destroy(mSurface);
}
@ -66,26 +124,107 @@ SourceSurfaceCairo::GetFormat() const
TemporaryRef<DataSourceSurface>
SourceSurfaceCairo::GetDataSurface()
{
return NULL;
RefPtr<DataSourceSurfaceCairo> dataSurf;
if (cairo_surface_get_type(mSurface) == CAIRO_SURFACE_TYPE_IMAGE) {
dataSurf = new DataSourceSurfaceCairo(mSurface);
} else {
cairo_surface_t* imageSurf = cairo_image_surface_create(GfxFormatToCairoFormat(mFormat),
mSize.width, mSize.height);
// Fill the new image surface with the contents of our surface.
cairo_t* ctx = cairo_create(imageSurf);
cairo_set_source_surface(ctx, mSurface, 0, 0);
cairo_paint(ctx);
cairo_destroy(ctx);
dataSurf = new DataSourceSurfaceCairo(imageSurf);
cairo_surface_destroy(imageSurf);
}
return dataSurf;
}
cairo_surface_t*
SourceSurfaceCairo::GetSurface()
SourceSurfaceCairo::GetSurface() const
{
return mSurface;
}
bool
SourceSurfaceCairo::InitFromSurface(cairo_surface_t* aSurface,
const IntSize& aSize,
const SurfaceFormat& aFormat)
void
SourceSurfaceCairo::DrawTargetWillChange()
{
mSurface = aSurface;
cairo_surface_reference(mSurface);
mSize = aSize;
mFormat = aFormat;
if (mDrawTarget) {
mDrawTarget = NULL;
return true;
// We're about to lose our version of the surface, so make a copy of it.
cairo_surface_t* surface = cairo_surface_create_similar(mSurface,
GfxFormatToCairoContent(mFormat),
mSize.width, mSize.height);
cairo_t* ctx = cairo_create(surface);
cairo_pattern_t* pat = cairo_pattern_create_for_surface(mSurface);
cairo_set_source(ctx, pat);
cairo_paint(ctx);
cairo_destroy(ctx);
// Swap in this new surface.
cairo_surface_destroy(mSurface);
mSurface = surface;
}
}
void
SourceSurfaceCairo::MarkIndependent()
{
if (mDrawTarget) {
mDrawTarget->RemoveSnapshot(this);
mDrawTarget = NULL;
}
}
DataSourceSurfaceCairo::DataSourceSurfaceCairo(cairo_surface_t* imageSurf)
: mImageSurface(imageSurf)
{
cairo_surface_reference(mImageSurface);
}
DataSourceSurfaceCairo::~DataSourceSurfaceCairo()
{
cairo_surface_destroy(mImageSurface);
}
unsigned char *
DataSourceSurfaceCairo::GetData()
{
return cairo_image_surface_get_data(mImageSurface);
}
int32_t
DataSourceSurfaceCairo::Stride()
{
return cairo_image_surface_get_stride(mImageSurface);
}
IntSize
DataSourceSurfaceCairo::GetSize() const
{
IntSize size;
size.width = cairo_image_surface_get_width(mImageSurface);
size.height = cairo_image_surface_get_height(mImageSurface);
return size;
}
SurfaceFormat
DataSourceSurfaceCairo::GetFormat() const
{
return CairoFormatToSurfaceFormat(cairo_image_surface_get_format(mImageSurface));
}
cairo_surface_t*
DataSourceSurfaceCairo::GetSurface() const
{
return mImageSurface;
}
}

View File

@ -42,27 +42,56 @@
namespace mozilla {
namespace gfx {
class DrawTargetCairo;
class SourceSurfaceCairo : public SourceSurface
{
public:
SourceSurfaceCairo();
~SourceSurfaceCairo();
// Create a SourceSurfaceCairo. The surface will not be copied, but simply
// referenced.
// If aDrawTarget is non-NULL, it is assumed that this is a snapshot source
// surface, and we'll call DrawTargetCairo::RemoveSnapshot(this) on it when
// we're destroyed.
SourceSurfaceCairo(cairo_surface_t* aSurface, const IntSize& aSize,
const SurfaceFormat& aFormat,
DrawTargetCairo* aDrawTarget = NULL);
virtual ~SourceSurfaceCairo();
virtual SurfaceType GetType() const { return SURFACE_CAIRO; }
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const;
virtual TemporaryRef<DataSourceSurface> GetDataSurface();
cairo_surface_t* GetSurface();
cairo_surface_t* GetSurface() const;
bool InitFromSurface(cairo_surface_t* aSurface,
const IntSize& aSize,
const SurfaceFormat& aFormat);
private: // methods
friend class DrawTargetCairo;
void DrawTargetWillChange();
void MarkIndependent();
private:
private: // data
IntSize mSize;
SurfaceFormat mFormat;
cairo_surface_t* mSurface;
DrawTargetCairo* mDrawTarget;
};
class DataSourceSurfaceCairo : public DataSourceSurface
{
public:
DataSourceSurfaceCairo(cairo_surface_t* imageSurf);
virtual ~DataSourceSurfaceCairo();
virtual unsigned char *GetData();
virtual int32_t Stride();
virtual SurfaceType GetType() const { return SURFACE_CAIRO_IMAGE; }
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const;
cairo_surface_t* GetSurface() const;
private:
cairo_surface_t* mImageSurface;
};
}

View File

@ -79,12 +79,14 @@ enum FontType
FONT_DWRITE,
FONT_GDI,
FONT_MAC,
FONT_SKIA
FONT_SKIA,
FONT_CAIRO
};
enum NativeSurfaceType
{
NATIVE_SURFACE_D3D10_TEXTURE
NATIVE_SURFACE_D3D10_TEXTURE,
NATIVE_SURFACE_CAIRO_SURFACE
};
enum NativeFontType
@ -92,7 +94,8 @@ enum NativeFontType
NATIVE_FONT_DWRITE_FONT_FACE,
NATIVE_FONT_GDI_FONT_FACE,
NATIVE_FONT_MAC_FONT_FACE,
NATIVE_FONT_SKIA_FONT_FACE
NATIVE_FONT_SKIA_FONT_FACE,
NATIVE_FONT_CAIRO_FONT_FACE
};
enum CompositionOp { OP_OVER, OP_ADD, OP_ATOP, OP_OUT, OP_IN, OP_SOURCE, OP_DEST_IN, OP_DEST_OUT, OP_DEST_OVER, OP_DEST_ATOP, OP_XOR, OP_COUNT };

View File

@ -45,6 +45,7 @@
#include "gfxXlibSurface.h"
#include "mozilla/X11Util.h"
#include "cairo-xlib.h"
namespace mozilla {
namespace layers {
@ -82,13 +83,20 @@ TakeAndDestroyXlibSurface(SurfaceDescriptor* aSurface)
SurfaceDescriptorX11::SurfaceDescriptorX11(gfxXlibSurface* aSurf)
: mId(aSurf->XDrawable())
, mSize(aSurf->GetSize())
, mFormat(aSurf->XRenderFormat()->id)
{ }
{
const XRenderPictFormat *pictFormat = aSurf->XRenderFormat();
if (pictFormat) {
mFormat = pictFormat->id;
} else {
mFormat = cairo_xlib_surface_get_visual(aSurf->CairoSurface())->visualid;
}
}
SurfaceDescriptorX11::SurfaceDescriptorX11(const int aXid, const int aXrenderPictID, const gfxIntSize& aSize)
: mId(aXid)
SurfaceDescriptorX11::SurfaceDescriptorX11(Drawable aDrawable, XID aFormatID,
const gfxIntSize& aSize)
: mId(aDrawable)
, mFormat(aFormatID)
, mSize(aSize)
, mFormat(aXrenderPictID)
{ }
already_AddRefed<gfxXlibSurface>
@ -97,9 +105,19 @@ SurfaceDescriptorX11::OpenForeign() const
Display* display = DefaultXDisplay();
Screen* screen = DefaultScreenOfDisplay(display);
XRenderPictFormat* format = GetXRenderPictFormatFromId(display, mFormat);
nsRefPtr<gfxXlibSurface> surf =
new gfxXlibSurface(screen, mId, format, mSize);
nsRefPtr<gfxXlibSurface> surf;
XRenderPictFormat* pictFormat = GetXRenderPictFormatFromId(display, mFormat);
if (pictFormat) {
surf = new gfxXlibSurface(screen, mId, pictFormat, mSize);
} else {
Visual* visual = NULL;
unsigned int depth;
XVisualIDToInfo(display, mFormat, &visual, &depth);
if (!visual)
return nsnull;
surf = new gfxXlibSurface(display, mId, visual, mSize);
}
return surf->CairoStatus() ? nsnull : surf.forget();
}

View File

@ -60,7 +60,8 @@ struct SurfaceDescriptorX11 {
SurfaceDescriptorX11(gfxXlibSurface* aSurf);
SurfaceDescriptorX11(const int aXid, const int aXrenderPictID, const gfxIntSize& aSize);
SurfaceDescriptorX11(Drawable aDrawable, XID aFormatID,
const gfxIntSize& aSize);
// Default copy ctor and operator= are OK
@ -76,8 +77,8 @@ struct SurfaceDescriptorX11 {
already_AddRefed<gfxXlibSurface> OpenForeign() const;
Drawable mId;
XID mFormat; // either a PictFormat or VisualID
gfxIntSize mSize;
PictFormat mFormat;
};
} // namespace layers

View File

@ -41,6 +41,34 @@
namespace mozilla {
bool
XVisualIDToInfo(Display* aDisplay, VisualID aVisualID,
Visual** aVisual, unsigned int* aDepth)
{
if (aVisualID == None) {
*aVisual = NULL;
*aDepth = 0;
return true;
}
const Screen* screen = DefaultScreenOfDisplay(aDisplay);
for (int d = 0; d < screen->ndepths; d++) {
Depth *d_info = &screen->depths[d];
for (int v = 0; v < d_info->nvisuals; v++) {
Visual* visual = &d_info->visuals[v];
if (visual->visualid == aVisualID) {
*aVisual = visual;
*aDepth = d_info->depth;
return true;
}
}
}
NS_ERROR("VisualID not on Screen.");
return false;
}
ScopedXErrorHandler::ErrorEvent* ScopedXErrorHandler::sXErrorPtr;
int

View File

@ -72,6 +72,17 @@ DefaultXDisplay()
#endif
}
/**
* Sets *aVisual to point to aDisplay's Visual struct corresponding to
* aVisualID, and *aDepth to its depth. When aVisualID is None, these are set
* to NULL and 0 respectively. Both out-parameter pointers are assumed
* non-NULL. Returns true in both of these situations, but false if aVisualID
* is not None and not found on the Display.
*/
bool
XVisualIDToInfo(Display* aDisplay, VisualID aVisualID,
Visual** aVisual, unsigned int* aDepth);
/**
* Invoke XFree() on a pointer to memory allocated by Xlib (if the
* pointer is nonnull) when this class goes out of scope.

View File

@ -1128,6 +1128,8 @@ public:
const nsString& GetName() const { return mFontEntry->Name(); }
const gfxFontStyle *GetStyle() const { return &mStyle; }
cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; }
virtual gfxFont* CopyWithAntialiasOption(AntialiasOption anAAOption) {
// platforms where this actually matters should override
return nsnull;

View File

@ -516,7 +516,13 @@ gfxPlatform::GetSourceSurfaceForSurface(DrawTarget *aTarget, gfxASurface *aSurfa
RefPtr<ScaledFont>
gfxPlatform::GetScaledFontForFont(gfxFont *aFont)
{
return NULL;
NativeFont nativeFont;
nativeFont.mType = NATIVE_FONT_CAIRO_FONT_FACE;
nativeFont.mFont = aFont;
RefPtr<ScaledFont> scaledFont =
Factory::CreateScaledFontForNativeFont(nativeFont,
aFont->GetAdjustedSize());
return scaledFont;
}
cairo_user_data_key_t kDrawSourceSurface;

View File

@ -956,7 +956,9 @@ RasterImage::GetImageContainer(LayerManager* aManager,
CairoImage::Data cairoData;
nsRefPtr<gfxASurface> imageSurface;
GetFrame(FRAME_CURRENT, FLAG_SYNC_DECODE, getter_AddRefs(imageSurface));
nsresult rv = GetFrame(FRAME_CURRENT, FLAG_SYNC_DECODE, getter_AddRefs(imageSurface));
NS_ENSURE_SUCCESS(rv, rv);
cairoData.mSurface = imageSurface;
GetWidth(&cairoData.mSize.width);
GetHeight(&cairoData.mSize.height);

View File

@ -8,7 +8,7 @@
#include <io.h>
#endif
#include <stdio.h>
#if defined(ANDROID)
#if defined(ANDROID) || defined(OS_POSIX)
#include <unistd.h>
#endif

View File

@ -0,0 +1 @@
uneval(function() { @o() })

View File

@ -5346,6 +5346,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
CopyDecompiledTextForDecomposedOp(jp, pc);
}
if (op == JSOP_CALLXMLNAME) {
todo = Sprint(&ss->sprinter, "");
if (todo < 0 || !PushOff(ss, todo, saveop))
return NULL;
}
pc += len;
}

View File

@ -102,6 +102,7 @@ abstract public class GeckoApp
public static final String SAVED_STATE_TITLE = "title";
public static final String SAVED_STATE_VIEWPORT = "viewport";
public static final String SAVED_STATE_SCREEN = "screen";
public static final String SAVED_STATE_SESSION = "session";
private LinearLayout mMainLayout;
private RelativeLayout mGeckoLayout;
@ -141,6 +142,7 @@ abstract public class GeckoApp
public String mLastViewport;
public byte[] mLastScreen;
public int mOwnActivityDepth = 0;
private boolean mRestoreSession = false;
private Vector<View> mPluginViews = new Vector<View>();
@ -575,6 +577,7 @@ abstract public class GeckoApp
outState.putString(SAVED_STATE_TITLE, mLastTitle);
outState.putString(SAVED_STATE_VIEWPORT, mLastViewport);
outState.putByteArray(SAVED_STATE_SCREEN, mLastScreen);
outState.putBoolean(SAVED_STATE_SESSION, true);
}
public class SessionSnapshotRunnable implements Runnable {
@ -955,7 +958,9 @@ abstract public class GeckoApp
final double zoom = message.getDouble("zoom");
mMainHandler.post(new Runnable() {
public void run() {
mAutoCompletePopup.show(suggestions, rect, zoom);
// Don't show autocomplete popup when using fullscreen VKB
if (!GeckoInputConnection.mIMELandscapeFS)
mAutoCompletePopup.show(suggestions, rect, zoom);
}
});
}
@ -1425,6 +1430,7 @@ abstract public class GeckoApp
mLastTitle = savedInstanceState.getString(SAVED_STATE_TITLE);
mLastViewport = savedInstanceState.getString(SAVED_STATE_VIEWPORT);
mLastScreen = savedInstanceState.getByteArray(SAVED_STATE_SCREEN);
mRestoreSession = savedInstanceState.getBoolean(SAVED_STATE_SESSION);
}
Intent intent = getIntent();
@ -1460,7 +1466,7 @@ abstract public class GeckoApp
prefetchDNS(intent.getData());
sGeckoThread = new GeckoThread(intent, mLastUri, mLastTitle);
sGeckoThread = new GeckoThread(intent, mLastUri, mRestoreSession);
if (!ACTION_DEBUG.equals(intent.getAction()) &&
checkAndSetLaunchState(LaunchState.Launching, LaunchState.Launched))
sGeckoThread.start();
@ -1482,6 +1488,8 @@ abstract public class GeckoApp
mBrowserToolbar = (BrowserToolbar) findViewById(R.id.browser_toolbar);
}
mBrowserToolbar.setTitle(mLastTitle);
mFavicons = new Favicons(this);
// setup gecko layout

View File

@ -132,7 +132,9 @@ public class GeckoAppShell
public static native void loadLibs(String apkName, boolean shouldExtract);
public static native void onChangeNetworkLinkStatus(String status);
public static native void reportJavaCrash(String stack);
public static native void notifyUriVisited(String uri);
public static void notifyUriVisited(String uri) {
sendEventToGecko(new GeckoEvent(GeckoEvent.VISTITED, uri));
}
public static native void processNextNativeEvent();
@ -417,7 +419,7 @@ public class GeckoAppShell
}
}
public static void runGecko(String apkPath, String args, String url) {
public static void runGecko(String apkPath, String args, String url, boolean restoreSession) {
// run gecko -- it will spawn its own thread
GeckoAppShell.nativeInit();
@ -442,6 +444,8 @@ public class GeckoAppShell
combinedArgs += " " + args;
if (url != null)
combinedArgs += " -remote " + url;
if (restoreSession)
combinedArgs += " -restoresession";
GeckoApp.mAppContext.runOnUiThread(new Runnable() {
public void run() {

View File

@ -1,4 +1,4 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; -*-
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -80,6 +80,7 @@ public class GeckoEvent {
public static final int BROADCAST = 19;
public static final int VIEWPORT = 20;
public static final int TILE_SIZE = 21;
public static final int VISTITED = 22;
public static final int IME_COMPOSITION_END = 0;
public static final int IME_COMPOSITION_BEGIN = 1;
@ -257,4 +258,8 @@ public class GeckoEvent {
mCharacters = uri;
}
public GeckoEvent(int type, String data) {
mType = type;
mCharacters = data;
}
}

View File

@ -54,12 +54,12 @@ public class GeckoThread extends Thread {
Intent mIntent;
String mUri;
String mTitle;
boolean mRestoreSession;
GeckoThread (Intent intent, String uri, String title) {
GeckoThread (Intent intent, String uri, boolean restoreSession) {
mIntent = intent;
mUri = uri;
mTitle = title;
mRestoreSession = restoreSession;
}
public void run() {
@ -95,18 +95,13 @@ public class GeckoThread extends Thread {
Log.w(LOGTAG, "zerdatime " + new Date().getTime() + " - runGecko");
// and then fire us up
app.mMainHandler.post(new Runnable() {
public void run() {
app.mBrowserToolbar.setTitle(mTitle);
}
});
try {
Log.w(LOGTAG, "RunGecko - URI = " + mUri);
GeckoAppShell.runGecko(app.getApplication().getPackageResourcePath(),
mIntent.getStringExtra("args"),
mUri);
mUri,
mRestoreSession);
} catch (Exception e) {
Log.e(LOGTAG, "top level exception", e);
StringWriter sw = new StringWriter();

View File

@ -121,7 +121,9 @@ FENNEC_JAVA_FILES = \
gfx/TileLayer.java \
gfx/ViewportMetrics.java \
gfx/WidgetTileLayer.java \
ui/Axis.java \
ui/PanZoomController.java \
ui/SubdocumentScrollHelper.java \
$(NULL)
FENNEC_PP_JAVA_FILES = \

View File

@ -12,8 +12,8 @@
<!ENTITY sync.subtitle.connect.label 'To activate your new device, select “Set up &syncBrand.shortName.label;” on the device.'>
<!ENTITY sync.subtitle.pair.label 'To activate, select “Pair a device” on your other device.'>
<!ENTITY sync.pin.default.label '...\n...\n...\n'>
<!ENTITY sync.link.show.label '<a href="https://support.mozilla.com/kb/add-a-device-to-firefox-sync">Show me how</a>'>
<!ENTITY sync.link.nodevice.label '<a href="#">I don\&apos;t have the device with me…</a>'>
<!ENTITY sync.link.show.label 'Show me how.'>
<!ENTITY sync.link.nodevice.label 'I don\&apos;t have the device with me…'>
<!-- J-PAKE Waiting Screen -->
<!ENTITY sync.jpake.subtitle.waiting.label 'Waiting for other device…'>
@ -31,6 +31,7 @@
<!ENTITY sync.subtitle.fail.label '&syncBrand.fullName.label; could not connect to the server. Would you like to try again?'>
<!ENTITY sync.button.tryagain.label 'Try again'>
<!ENTITY sync.button.manual.label 'Manual Setup'>
<!ENTITY sync.subtitle.nointernet.label 'No internet connection available.'>
<!-- Setup Success -->
<!ENTITY sync.title.success.label 'Setup Complete'>
@ -44,6 +45,7 @@
<!-- Common text -->
<!ENTITY sync.button.cancel.label 'Cancel'>
<!ENTITY sync.button.connect.label 'Connect'>
<!ENTITY sync.button.ok.label 'OK'>
<!-- Account strings -->
<!ENTITY sync.account.label.label '&syncBrand.fullName.label;'>

View File

@ -0,0 +1,259 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* ***** 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 Android code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2012
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@mozilla.com>
* Kartikaya Gupta <kgupta@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 ***** */
package org.mozilla.gecko.ui;
import org.mozilla.gecko.FloatUtils;
/**
* This class represents the physics for one axis of movement (i.e. either
* horizontal or vertical). It tracks the different properties of movement
* like displacement, velocity, viewport dimensions, etc. pertaining to
* a particular axis.
*/
abstract class Axis {
// This fraction of velocity remains after every animation frame when the velocity is low.
private static final float FRICTION_SLOW = 0.85f;
// This fraction of velocity remains after every animation frame when the velocity is high.
private static final float FRICTION_FAST = 0.97f;
// Below this velocity (in pixels per frame), the friction starts increasing from FRICTION_FAST
// to FRICTION_SLOW.
private static final float VELOCITY_THRESHOLD = 10.0f;
// The maximum velocity change factor between events, per ms, in %.
// Direction changes are excluded.
private static final float MAX_EVENT_ACCELERATION = 0.012f;
// The rate of deceleration when the surface has overscrolled.
private static final float OVERSCROLL_DECEL_RATE = 0.04f;
// The percentage of the surface which can be overscrolled before it must snap back.
private static final float SNAP_LIMIT = 0.75f;
// The minimum amount of space that must be present for an axis to be considered scrollable,
// in pixels.
private static final float MIN_SCROLLABLE_DISTANCE = 0.5f;
// The number of milliseconds per frame assuming 60 fps
private static final float MS_PER_FRAME = 1000.0f / 60.0f;
private enum FlingStates {
STOPPED,
PANNING,
FLINGING,
}
private enum Overscroll {
NONE,
MINUS, // Overscrolled in the negative direction
PLUS, // Overscrolled in the positive direction
BOTH, // Overscrolled in both directions (page is zoomed to smaller than screen)
}
private final SubdocumentScrollHelper mSubscroller;
private float mFirstTouchPos; /* Position of the first touch event on the current drag. */
private float mTouchPos; /* Position of the most recent touch event on the current drag. */
private float mLastTouchPos; /* Position of the touch event before touchPos. */
private float mVelocity; /* Velocity in this direction; pixels per animation frame. */
private boolean mLocked; /* Whether movement on this axis is locked. */
private boolean mDisableSnap; /* Whether overscroll snapping is disabled. */
private float mDisplacement;
private FlingStates mFlingState; /* The fling state we're in on this axis. */
protected abstract float getOrigin();
protected abstract float getViewportLength();
protected abstract float getPageLength();
Axis(SubdocumentScrollHelper subscroller) {
mSubscroller = subscroller;
}
private float getViewportEnd() {
return getOrigin() + getViewportLength();
}
void startTouch(float pos) {
mVelocity = 0.0f;
mLocked = false;
mFirstTouchPos = mTouchPos = mLastTouchPos = pos;
}
float panDistance(float currentPos) {
return currentPos - mFirstTouchPos;
}
void setLocked(boolean locked) {
mLocked = locked;
}
void saveTouchPos() {
mLastTouchPos = mTouchPos;
}
void updateWithTouchAt(float pos, float timeDelta) {
float newVelocity = (mTouchPos - pos) / timeDelta * MS_PER_FRAME;
// If there's a direction change, or current velocity is very low,
// allow setting of the velocity outright. Otherwise, use the current
// velocity and a maximum change factor to set the new velocity.
boolean curVelocityIsLow = Math.abs(mVelocity) < 1.0f;
boolean directionChange = (mVelocity > 0) != (newVelocity > 0);
if (curVelocityIsLow || (directionChange && !FloatUtils.fuzzyEquals(newVelocity, 0.0f))) {
mVelocity = newVelocity;
} else {
float maxChange = Math.abs(mVelocity * timeDelta * MAX_EVENT_ACCELERATION);
mVelocity = Math.min(mVelocity + maxChange, Math.max(mVelocity - maxChange, newVelocity));
}
mTouchPos = pos;
}
boolean overscrolled() {
return getOverscroll() != Overscroll.NONE;
}
private Overscroll getOverscroll() {
boolean minus = (getOrigin() < 0.0f);
boolean plus = (getViewportEnd() > getPageLength());
if (minus && plus) {
return Overscroll.BOTH;
} else if (minus) {
return Overscroll.MINUS;
} else if (plus) {
return Overscroll.PLUS;
} else {
return Overscroll.NONE;
}
}
// Returns the amount that the page has been overscrolled. If the page hasn't been
// overscrolled on this axis, returns 0.
private float getExcess() {
switch (getOverscroll()) {
case MINUS: return -getOrigin();
case PLUS: return getViewportEnd() - getPageLength();
case BOTH: return getViewportEnd() - getPageLength() - getOrigin();
default: return 0.0f;
}
}
/*
* Returns true if the page is zoomed in to some degree along this axis such that scrolling
* is possible. Otherwise, returns false.
*/
private boolean scrollable() {
return getViewportLength() <= getPageLength() - MIN_SCROLLABLE_DISTANCE;
}
/*
* Returns the resistance, as a multiplier, that should be taken into account when
* tracking or pinching.
*/
float getEdgeResistance() {
float excess = getExcess();
return (excess > 0.0f) ? SNAP_LIMIT - excess / getViewportLength() : 1.0f;
}
/* Returns the velocity. If the axis is locked, returns 0. */
float getRealVelocity() {
return mLocked ? 0.0f : mVelocity;
}
void startPan() {
mFlingState = FlingStates.PANNING;
}
void startFling(boolean stopped) {
mDisableSnap = mSubscroller.scrolling();
if (stopped) {
mFlingState = FlingStates.STOPPED;
} else {
mFlingState = FlingStates.FLINGING;
}
}
/* Advances a fling animation by one step. */
boolean advanceFling() {
if (mFlingState != FlingStates.FLINGING) {
return false;
}
float excess = getExcess();
if (mDisableSnap || FloatUtils.fuzzyEquals(excess, 0.0f)) {
// If we aren't overscrolled, just apply friction.
if (Math.abs(mVelocity) >= VELOCITY_THRESHOLD) {
mVelocity *= FRICTION_FAST;
} else {
float t = mVelocity / VELOCITY_THRESHOLD;
mVelocity *= FloatUtils.interpolate(FRICTION_SLOW, FRICTION_FAST, t);
}
} else {
// Otherwise, decrease the velocity linearly.
float elasticity = 1.0f - excess / (getViewportLength() * SNAP_LIMIT);
if (getOverscroll() == Overscroll.MINUS) {
mVelocity = Math.min((mVelocity + OVERSCROLL_DECEL_RATE) * elasticity, 0.0f);
} else { // must be Overscroll.PLUS
mVelocity = Math.max((mVelocity - OVERSCROLL_DECEL_RATE) * elasticity, 0.0f);
}
}
return true;
}
void stopFling() {
mVelocity = 0.0f;
mFlingState = FlingStates.STOPPED;
}
// Performs displacement of the viewport position according to the current velocity.
void displace() {
if (!mSubscroller.scrolling() && (mLocked || !scrollable()))
return;
if (mFlingState == FlingStates.PANNING)
mDisplacement += (mLastTouchPos - mTouchPos) * getEdgeResistance();
else
mDisplacement += mVelocity;
}
float resetDisplacement() {
float d = mDisplacement;
mDisplacement = 0.0f;
return d;
}
}

View File

@ -15,11 +15,12 @@
* The Original Code is Mozilla Android code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009-2010
* Portions created by the Initial Developer are Copyright (C) 2009-2012
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@mozilla.com>
* Kartikaya Gupta <kgupta@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
@ -50,12 +51,11 @@ import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.GeckoEventListener;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.FloatMath;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import java.lang.Math;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
@ -71,34 +71,18 @@ public class PanZoomController
{
private static final String LOGTAG = "GeckoPanZoomController";
private LayerController mController;
private static String MESSAGE_ZOOM_RECT = "Browser:ZoomToRect";
private static String MESSAGE_ZOOM_PAGE = "Browser:ZoomToPageWidth";
// This fraction of velocity remains after every animation frame when the velocity is low.
private static final float FRICTION_SLOW = 0.85f;
// This fraction of velocity remains after every animation frame when the velocity is high.
private static final float FRICTION_FAST = 0.97f;
// Below this velocity (in pixels per frame), the friction starts increasing from FRICTION_FAST
// to FRICTION_SLOW.
private static final float VELOCITY_THRESHOLD = 10.0f;
// Animation stops if the velocity is below this value when overscrolled or panning.
private static final float STOPPED_THRESHOLD = 4.0f;
// Animation stops is the velocity is below this threshold when flinging.
private static final float FLING_STOPPED_THRESHOLD = 0.1f;
// The percentage of the surface which can be overscrolled before it must snap back.
private static final float SNAP_LIMIT = 0.75f;
// The rate of deceleration when the surface has overscrolled.
private static final float OVERSCROLL_DECEL_RATE = 0.04f;
// The distance the user has to pan before we recognize it as such (e.g. to avoid
// 1-pixel pans between the touch-down and touch-up of a click). In units of inches.
private static final float PAN_THRESHOLD = 0.1f;
// Angle from axis within which we stay axis-locked
private static final double AXIS_LOCK_ANGLE = Math.PI / 6.0; // 30 degrees
// The maximum velocity change factor between events, per ms, in %.
// Direction changes are excluded.
private static final float MAX_EVENT_ACCELERATION = 0.012f;
// The minimum amount of space that must be present for an axis to be considered scrollable,
// in pixels.
private static final float MIN_SCROLLABLE_DISTANCE = 0.5f;
// The maximum amount we allow you to zoom into a page
private static final float MAX_ZOOM = 4.0f;
@ -122,19 +106,6 @@ public class PanZoomController
0.99309f, /* 15 */
};
/* The timer that handles flings or bounces. */
private Timer mAnimationTimer;
/* The runnable being scheduled by the animation timer. */
private AnimationRunnable mAnimationRunnable;
/* Information about the X axis. */
private AxisX mX;
/* Information about the Y axis. */
private AxisY mY;
/* The zoom focus at the first zoom event (in page coordinates). */
private PointF mLastZoomFocus;
/* The time the last motion event took place. */
private long mLastEventTime;
private enum PanZoomState {
NOTHING, /* no touch-start events received */
FLING, /* all touches removed, but we're still scrolling page */
@ -148,82 +119,66 @@ public class PanZoomController
ANIMATED_ZOOM /* animated zoom to a new rect */
}
private PanZoomState mState;
private final LayerController mController;
private final SubdocumentScrollHelper mSubscroller;
private final Axis mX;
private final Axis mY;
private boolean mOverridePanning;
private boolean mOverrideScrollAck;
private boolean mOverrideScrollPending;
/* The timer that handles flings or bounces. */
private Timer mAnimationTimer;
/* The runnable being scheduled by the animation timer. */
private AnimationRunnable mAnimationRunnable;
/* The zoom focus at the first zoom event (in page coordinates). */
private PointF mLastZoomFocus;
/* The time the last motion event took place. */
private long mLastEventTime;
/* Current state the pan/zoom UI is in. */
private PanZoomState mState;
public PanZoomController(LayerController controller) {
mController = controller;
mX = new AxisX(); mY = new AxisY();
mSubscroller = new SubdocumentScrollHelper(this);
mX = new AxisX(mSubscroller);
mY = new AxisY(mSubscroller);
mState = PanZoomState.NOTHING;
GeckoAppShell.registerGeckoEventListener("Browser:ZoomToRect", this);
GeckoAppShell.registerGeckoEventListener("Browser:ZoomToPageWidth", this);
GeckoAppShell.registerGeckoEventListener("Panning:Override", this);
GeckoAppShell.registerGeckoEventListener("Panning:CancelOverride", this);
GeckoAppShell.registerGeckoEventListener("Gesture:ScrollAck", this);
}
protected void finalize() throws Throwable {
GeckoAppShell.unregisterGeckoEventListener("Browser:ZoomToRect", this);
GeckoAppShell.unregisterGeckoEventListener("Browser:ZoomToPageWidth", this);
GeckoAppShell.unregisterGeckoEventListener("Panning:Override", this);
GeckoAppShell.unregisterGeckoEventListener("Panning:CancelOverride", this);
GeckoAppShell.unregisterGeckoEventListener("Gesture:ScrollAck", this);
super.finalize();
GeckoAppShell.registerGeckoEventListener(MESSAGE_ZOOM_RECT, this);
GeckoAppShell.registerGeckoEventListener(MESSAGE_ZOOM_PAGE, this);
}
public void handleMessage(String event, JSONObject message) {
Log.i(LOGTAG, "Got message: " + event);
try {
if ("Panning:Override".equals(event)) {
mOverridePanning = true;
mOverrideScrollAck = true;
} else if ("Panning:CancelOverride".equals(event)) {
mOverridePanning = false;
} else if ("Gesture:ScrollAck".equals(event)) {
if (MESSAGE_ZOOM_RECT.equals(event)) {
float scale = mController.getZoomFactor();
float x = (float)message.getDouble("x");
float y = (float)message.getDouble("y");
final RectF zoomRect = new RectF(x, y,
x + (float)message.getDouble("w"),
y + (float)message.getDouble("h"));
mController.post(new Runnable() {
public void run() {
mOverrideScrollAck = true;
if (mOverridePanning && mOverrideScrollPending)
updatePosition();
animatedZoomTo(zoomRect);
}
});
} else if (event.equals("Browser:ZoomToRect")) {
if (mController != null) {
float scale = mController.getZoomFactor();
float x = (float)message.getDouble("x");
float y = (float)message.getDouble("y");
final RectF zoomRect = new RectF(x, y,
x + (float)message.getDouble("w"),
y + (float)message.getDouble("h"));
mController.post(new Runnable() {
public void run() {
animatedZoomTo(zoomRect);
}
});
}
} else if (event.equals("Browser:ZoomToPageWidth")) {
if (mController != null) {
float scale = mController.getZoomFactor();
FloatSize pageSize = mController.getPageSize();
} else if (MESSAGE_ZOOM_PAGE.equals(event)) {
float scale = mController.getZoomFactor();
FloatSize pageSize = mController.getPageSize();
RectF viewableRect = mController.getViewport();
float y = viewableRect.top;
// attempt to keep zoom keep focused on the center of the viewport
float dh = viewableRect.height()*(1 - pageSize.width/viewableRect.width()); // increase in the height
final RectF r = new RectF(0.0f,
y + dh/2,
pageSize.width,
(y + pageSize.width * viewableRect.height()/viewableRect.width()));
mController.post(new Runnable() {
public void run() {
animatedZoomTo(r);
}
});
}
RectF viewableRect = mController.getViewport();
float y = viewableRect.top;
// attempt to keep zoom keep focused on the center of the viewport
float dh = viewableRect.height()*(1 - pageSize.width/viewableRect.width()); // increase in the height
final RectF r = new RectF(0.0f,
y + dh/2,
pageSize.width,
(y + pageSize.width * viewableRect.height()/viewableRect.width()));
mController.post(new Runnable() {
public void run() {
animatedZoomTo(r);
}
});
}
} catch (Exception e) {
Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
@ -249,13 +204,14 @@ public class PanZoomController
// anything special.
switch (mState) {
case FLING:
mX.velocity = mY.velocity = 0.0f;
mX.stopFling();
mY.stopFling();
mState = PanZoomState.NOTHING;
// fall through
case ANIMATED_ZOOM:
// the zoom that's in progress likely makes no sense any more (such as if
// the screen orientation changed) so abort it and start a new one to
// ensure the viewport doesn't contain out-of-bounds areas
// the screen orientation changed) so abort it
// fall through
case NOTHING:
// Don't do animations here; they're distracting and can cause flashes on page
// transitions.
@ -274,19 +230,14 @@ public class PanZoomController
// user is taking control of movement, so stop
// any auto-movement we have going
stopAnimationTimer();
mOverridePanning = false;
mSubscroller.cancel();
switch (mState) {
case ANIMATED_ZOOM:
return false;
case FLING:
case NOTHING:
mState = PanZoomState.TOUCHING;
mX.velocity = mY.velocity = 0.0f;
mX.locked = mY.locked = false;
mX.lastTouchPos = mX.firstTouchPos = mX.touchPos = event.getX(0);
mY.lastTouchPos = mY.firstTouchPos = mY.touchPos = event.getY(0);
mLastEventTime = event.getEventTime();
startTouch(event.getX(0), event.getY(0), event.getEventTime());
return false;
case TOUCHING:
case PANNING:
@ -294,7 +245,7 @@ public class PanZoomController
case PANNING_HOLD:
case PANNING_HOLD_LOCKED:
case PINCHING:
mState = PanZoomState.PINCHING;
Log.e(LOGTAG, "Received impossible touch down while in " + mState);
return false;
}
Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchStart");
@ -312,8 +263,9 @@ public class PanZoomController
Log.e(LOGTAG, "Received impossible touch move while in " + mState);
return false;
case TOUCHING:
if (panDistance(event) < PAN_THRESHOLD * GeckoAppShell.getDpi())
if (panDistance(event) < PAN_THRESHOLD * GeckoAppShell.getDpi()) {
return false;
}
cancelTouch();
// fall through
case PANNING_HOLD_LOCKED:
@ -363,19 +315,7 @@ public class PanZoomController
fling();
return true;
case PINCHING:
int points = event.getPointerCount();
if (points == 1) {
// last touch up
mState = PanZoomState.NOTHING;
} else if (points == 2) {
int pointRemovedIndex = event.getActionIndex();
int pointRemainingIndex = 1 - pointRemovedIndex; // kind of a hack
mState = PanZoomState.TOUCHING;
mX.firstTouchPos = mX.touchPos = event.getX(pointRemainingIndex);
mX.firstTouchPos = mY.touchPos = event.getY(pointRemainingIndex);
} else {
// still pinching, do nothing
}
mState = PanZoomState.NOTHING;
return true;
case ANIMATED_ZOOM:
return false;
@ -393,89 +333,63 @@ public class PanZoomController
return false;
}
private void startTouch(float x, float y, long time) {
mX.startTouch(x);
mY.startTouch(y);
mState = PanZoomState.TOUCHING;
mLastEventTime = time;
}
private float panDistance(MotionEvent move) {
float dx = mX.firstTouchPos - move.getX(0);
float dy = mY.firstTouchPos - move.getY(0);
return (float)Math.sqrt(dx * dx + dy * dy);
float dx = mX.panDistance(move.getX(0));
float dy = mY.panDistance(move.getY(0));
return FloatMath.sqrt(dx * dx + dy * dy);
}
private float clampByFactor(float oldValue, float newValue, float factor) {
float maxChange = Math.abs(oldValue * factor);
return Math.min(oldValue + maxChange, Math.max(oldValue - maxChange, newValue));
}
private void track(float x, float y, float lastX, float lastY, float timeDelta) {
private void track(float x, float y, long time) {
float timeDelta = (float)(time - mLastEventTime);
if (FloatUtils.fuzzyEquals(timeDelta, 0)) {
// probably a duplicate event, ignore it. using a zero timeDelta will mess
// up our velocity
return;
}
mLastEventTime = time;
if (mState == PanZoomState.PANNING_LOCKED) {
// check to see if we should break the axis lock
double angle = Math.atan2(y - mY.firstTouchPos, x - mX.firstTouchPos); // range [-pi, pi]
double angle = Math.atan2(mY.panDistance(y), mX.panDistance(x)); // range [-pi, pi]
angle = Math.abs(angle); // range [0, pi]
if (angle < AXIS_LOCK_ANGLE || angle > (Math.PI - AXIS_LOCK_ANGLE)) {
// lock to x-axis
mX.locked = false;
mY.locked = true;
mX.setLocked(false);
mY.setLocked(true);
} else if (Math.abs(angle - (Math.PI / 2)) < AXIS_LOCK_ANGLE) {
// lock to y-axis
mX.locked = true;
mY.locked = false;
mX.setLocked(true);
mY.setLocked(false);
} else {
// break axis lock but log the angle so we can fine-tune this when people complain
mState = PanZoomState.PANNING;
mX.locked = mY.locked = false;
mX.setLocked(false);
mY.setLocked(false);
angle = Math.abs(angle - (Math.PI / 2)); // range [0, pi/2]
Log.i(LOGTAG, "Breaking axis lock at " + (angle * 180.0 / Math.PI) + " degrees");
}
}
float newVelocityX = ((lastX - x) / timeDelta) * (1000.0f/60.0f);
float newVelocityY = ((lastY - y) / timeDelta) * (1000.0f/60.0f);
float maxChange = MAX_EVENT_ACCELERATION * timeDelta;
// If there's a direction change, or current velocity is very low,
// allow setting of the velocity outright. Otherwise, use the current
// velocity and a maximum change factor to set the new velocity.
if (Math.abs(mX.velocity) < 1.0f ||
(((mX.velocity > 0) != (newVelocityX > 0)) &&
!FloatUtils.fuzzyEquals(newVelocityX, 0.0f)))
mX.velocity = newVelocityX;
else
mX.velocity = clampByFactor(mX.velocity, newVelocityX, maxChange);
if (Math.abs(mY.velocity) < 1.0f ||
(((mY.velocity > 0) != (newVelocityY > 0)) &&
!FloatUtils.fuzzyEquals(newVelocityY, 0.0f)))
mY.velocity = newVelocityY;
else
mY.velocity = clampByFactor(mY.velocity, newVelocityY, maxChange);
mX.updateWithTouchAt(x, timeDelta);
mY.updateWithTouchAt(y, timeDelta);
}
private void track(MotionEvent event) {
mX.lastTouchPos = mX.touchPos;
mY.lastTouchPos = mY.touchPos;
mX.saveTouchPos();
mY.saveTouchPos();
for (int i = 0; i < event.getHistorySize(); i++) {
float x = event.getHistoricalX(0, i);
float y = event.getHistoricalY(0, i);
long time = event.getHistoricalEventTime(i);
float timeDelta = (float)(time - mLastEventTime);
mLastEventTime = time;
track(x, y, mX.touchPos, mY.touchPos, timeDelta);
mX.touchPos = x; mY.touchPos = y;
track(event.getHistoricalX(0, i),
event.getHistoricalY(0, i),
event.getHistoricalEventTime(i));
}
float timeDelta = (float)(event.getEventTime() - mLastEventTime);
mLastEventTime = event.getEventTime();
track(event.getX(0), event.getY(0), mX.touchPos, mY.touchPos, timeDelta);
mX.touchPos = event.getX(0);
mY.touchPos = event.getY(0);
track(event.getX(0), event.getY(0), event.getEventTime());
if (stopped()) {
if (mState == PanZoomState.PANNING) {
@ -489,24 +403,19 @@ public class PanZoomController
}
}
mX.setFlingState(Axis.FlingStates.PANNING); mY.setFlingState(Axis.FlingStates.PANNING);
mX.displace(mOverridePanning); mY.displace(mOverridePanning);
mX.startPan();
mY.startPan();
updatePosition();
}
private void fling() {
if (mState != PanZoomState.FLING)
mX.velocity = mY.velocity = 0.0f;
mX.disableSnap = mY.disableSnap = mOverridePanning;
mX.displace(mOverridePanning); mY.displace(mOverridePanning);
updatePosition();
stopAnimationTimer();
boolean stopped = stopped();
mX.startFling(stopped); mY.startFling(stopped);
mX.startFling(stopped);
mY.startFling(stopped);
startAnimationTimer(new FlingRunnable());
}
@ -522,7 +431,6 @@ public class PanZoomController
}
mState = PanZoomState.FLING;
mX.setFlingState(Axis.FlingStates.SNAPPING); mY.setFlingState(Axis.FlingStates.SNAPPING);
Log.d(LOGTAG, "end bounce at " + metrics);
startAnimationTimer(new BounceRunnable(bounceStartMetrics, metrics));
@ -560,39 +468,29 @@ public class PanZoomController
}
}
private float getVelocity() {
float xvel = mX.getRealVelocity();
float yvel = mY.getRealVelocity();
return FloatMath.sqrt(xvel * xvel + yvel * yvel);
}
private boolean stopped() {
float absVelocity = (float)Math.sqrt(mX.velocity * mX.velocity +
mY.velocity * mY.velocity);
return absVelocity < STOPPED_THRESHOLD;
return getVelocity() < STOPPED_THRESHOLD;
}
PointF getDisplacement() {
return new PointF(mX.resetDisplacement(), mY.resetDisplacement());
}
private void updatePosition() {
if (mOverridePanning) {
if (!mOverrideScrollAck) {
mOverrideScrollPending = true;
return;
}
mOverrideScrollPending = false;
JSONObject json = new JSONObject();
try {
json.put("x", mX.displacement);
json.put("y", mY.displacement);
} catch (JSONException e) {
Log.e(LOGTAG, "Error forming Gesture:Scroll message: " + e);
}
GeckoEvent e = new GeckoEvent("Gesture:Scroll", json.toString());
GeckoAppShell.sendEventToGecko(e);
mOverrideScrollAck = false;
} else {
mX.displace();
mY.displace();
PointF displacement = getDisplacement();
if (! mSubscroller.scrollBy(displacement)) {
synchronized (mController) {
mController.scrollBy(new PointF(mX.displacement, mY.displacement));
mController.scrollBy(displacement);
}
}
mX.displacement = mY.displacement = 0;
}
private abstract class AnimationRunnable implements Runnable {
@ -694,37 +592,36 @@ public class PanZoomController
}
/* Advance flings, if necessary. */
boolean flingingX = mX.getFlingState() == Axis.FlingStates.FLINGING;
boolean flingingY = mY.getFlingState() == Axis.FlingStates.FLINGING;
if (flingingX)
mX.advanceFling();
if (flingingY)
mY.advanceFling();
boolean flingingX = mX.advanceFling();
boolean flingingY = mY.advanceFling();
boolean overscrolled = ((mX.overscrolled() || mY.overscrolled()) && !mSubscroller.scrolling());
/* If we're still flinging in any direction, update the origin. */
if (flingingX || flingingY) {
mX.displace(mOverridePanning); mY.displace(mOverridePanning);
updatePosition();
/*
* If we're still flinging with an appreciable velocity, stop here. The threshold is
* Check to see if we're still flinging with an appreciable velocity. The threshold is
* higher in the case of overscroll, so we bounce back eagerly when overscrolling but
* coast smoothly to a stop when not.
* coast smoothly to a stop when not. In other words, require a greater velocity to
* maintain the fling once we enter overscroll.
*/
float excess = PointUtils.distance(new PointF(mX.getExcess(), mY.getExcess()));
PointF velocityVector = new PointF(mX.getRealVelocity(), mY.getRealVelocity());
float threshold = (excess >= 1.0f) ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD;
if (PointUtils.distance(velocityVector) >= threshold)
float threshold = (overscrolled ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD);
if (getVelocity() >= threshold) {
// we're still flinging
return;
}
mX.stopFling();
mY.stopFling();
}
/*
* Perform a bounce-back animation if overscrolled, unless panning is being overridden
* (which happens e.g. when the user is panning an iframe).
* Perform a bounce-back animation if overscrolled, unless panning is being
* handled by the subwindow scroller.
*/
boolean overscrolledX = mX.getOverscroll() != Axis.Overscroll.NONE;
boolean overscrolledY = mY.getOverscroll() != Axis.Overscroll.NONE;
if (!mOverridePanning && (overscrolledX || overscrolledY)) {
if (overscrolled) {
bounce();
} else {
finishAnimation();
@ -742,157 +639,6 @@ public class PanZoomController
mController.notifyLayerClientOfGeometryChange();
}
private float computeElasticity(float excess, float viewportLength) {
return 1.0f - excess / (viewportLength * SNAP_LIMIT);
}
// Physics information for one axis (X or Y).
private abstract static class Axis {
public enum FlingStates {
STOPPED,
PANNING,
FLINGING,
WAITING_TO_SNAP,
SNAPPING,
}
public enum Overscroll {
NONE,
MINUS, // Overscrolled in the negative direction
PLUS, // Overscrolled in the positive direction
BOTH, // Overscrolled in both directions (page is zoomed to smaller than screen)
}
public float firstTouchPos; /* Position of the first touch event on the current drag. */
public float touchPos; /* Position of the most recent touch event on the current drag. */
public float lastTouchPos; /* Position of the touch event before touchPos. */
public float velocity; /* Velocity in this direction. */
public boolean locked; /* Whether movement on this axis is locked. */
public boolean disableSnap; /* Whether overscroll snapping is disabled. */
private FlingStates mFlingState; /* The fling state we're in on this axis. */
public abstract float getOrigin();
protected abstract float getViewportLength();
protected abstract float getPageLength();
public float displacement;
private int mSnapFrame;
private float mSnapPos, mSnapEndPos;
public Axis() { mSnapFrame = -1; }
public FlingStates getFlingState() { return mFlingState; }
public void setFlingState(FlingStates aFlingState) {
mFlingState = aFlingState;
}
private float getViewportEnd() { return getOrigin() + getViewportLength(); }
public Overscroll getOverscroll() {
boolean minus = (getOrigin() < 0.0f);
boolean plus = (getViewportEnd() > getPageLength());
if (minus && plus)
return Overscroll.BOTH;
else if (minus)
return Overscroll.MINUS;
else if (plus)
return Overscroll.PLUS;
else
return Overscroll.NONE;
}
// Returns the amount that the page has been overscrolled. If the page hasn't been
// overscrolled on this axis, returns 0.
public float getExcess() {
switch (getOverscroll()) {
case MINUS: return -getOrigin();
case PLUS: return getViewportEnd() - getPageLength();
case BOTH: return getViewportEnd() - getPageLength() - getOrigin();
default: return 0.0f;
}
}
/*
* Returns true if the page is zoomed in to some degree along this axis such that scrolling
* is possible. Otherwise, returns false.
*/
private boolean scrollable() {
return getViewportLength() <= getPageLength() - MIN_SCROLLABLE_DISTANCE;
}
/*
* Returns the resistance, as a multiplier, that should be taken into account when
* tracking or pinching.
*/
public float getEdgeResistance() {
float excess = getExcess();
return (excess > 0.0f) ? SNAP_LIMIT - excess / getViewportLength() : 1.0f;
}
/* Returns the velocity. If the axis is locked, returns 0. */
public float getRealVelocity() {
return locked ? 0.0f : velocity;
}
public void startFling(boolean stopped) {
if (!stopped) {
setFlingState(FlingStates.FLINGING);
return;
}
if (disableSnap || FloatUtils.fuzzyEquals(getExcess(), 0.0f))
setFlingState(FlingStates.STOPPED);
else
setFlingState(FlingStates.WAITING_TO_SNAP);
}
/* Advances a fling animation by one step. */
public void advanceFling() {
// If we aren't overscrolled, just apply friction.
float excess = getExcess();
if (disableSnap || FloatUtils.fuzzyEquals(excess, 0.0f)) {
if (Math.abs(velocity) >= VELOCITY_THRESHOLD) {
velocity *= FRICTION_FAST;
} else {
float t = velocity / VELOCITY_THRESHOLD;
velocity *= FloatUtils.interpolate(FRICTION_SLOW, FRICTION_FAST, t);
}
if (Math.abs(velocity) < FLING_STOPPED_THRESHOLD) {
velocity = 0.0f;
setFlingState(FlingStates.STOPPED);
}
return;
}
// Otherwise, decrease the velocity linearly.
float elasticity = 1.0f - excess / (getViewportLength() * SNAP_LIMIT);
if (getOverscroll() == Overscroll.MINUS)
velocity = Math.min((velocity + OVERSCROLL_DECEL_RATE) * elasticity, 0.0f);
else // must be Overscroll.PLUS
velocity = Math.max((velocity - OVERSCROLL_DECEL_RATE) * elasticity, 0.0f);
if (Math.abs(velocity) < 0.3f) {
velocity = 0.0f;
setFlingState(FlingStates.WAITING_TO_SNAP);
}
}
// Performs displacement of the viewport position according to the current velocity.
public void displace(boolean panningOverridden) {
if (!panningOverridden && (locked || !scrollable()))
return;
if (mFlingState == FlingStates.PANNING)
displacement += (lastTouchPos - touchPos) * getEdgeResistance();
else
displacement += velocity;
}
}
/* Returns the nearest viewport metrics with no overscroll visible. */
private ViewportMetrics getValidViewportMetrics() {
ViewportMetrics viewportMetrics = new ViewportMetrics(mController.getViewportMetrics());
@ -929,6 +675,7 @@ public class PanZoomController
}
private class AxisX extends Axis {
AxisX(SubdocumentScrollHelper subscroller) { super(subscroller); }
@Override
public float getOrigin() { return mController.getOrigin().x; }
@Override
@ -938,6 +685,7 @@ public class PanZoomController
}
private class AxisY extends Axis {
AxisY(SubdocumentScrollHelper subscroller) { super(subscroller); }
@Override
public float getOrigin() { return mController.getOrigin().y; }
@Override
@ -949,6 +697,22 @@ public class PanZoomController
/*
* Zooming
*/
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
Log.d(LOGTAG, "onScaleBegin in " + mState);
if (mState == PanZoomState.ANIMATED_ZOOM)
return false;
mState = PanZoomState.PINCHING;
mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY());
GeckoApp.mAppContext.hidePluginViews();
GeckoApp.mAppContext.mAutoCompletePopup.hide();
cancelTouch();
return true;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
Log.d(LOGTAG, "onScale in state " + mState);
@ -996,22 +760,6 @@ public class PanZoomController
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
Log.d(LOGTAG, "onScaleBegin in " + mState);
if (mState == PanZoomState.ANIMATED_ZOOM)
return false;
mState = PanZoomState.PINCHING;
mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY());
GeckoApp.mAppContext.hidePluginViews();
GeckoApp.mAppContext.mAutoCompletePopup.hide();
cancelTouch();
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
Log.d(LOGTAG, "onScaleEnd in " + mState);
@ -1019,23 +767,20 @@ public class PanZoomController
if (mState == PanZoomState.ANIMATED_ZOOM)
return;
mState = PanZoomState.PANNING_HOLD_LOCKED;
mX.firstTouchPos = mX.lastTouchPos = mX.touchPos = detector.getFocusX();
mY.firstTouchPos = mY.lastTouchPos = mY.touchPos = detector.getFocusY();
// switch back to the touching state
startTouch(detector.getFocusX(), detector.getFocusY(), detector.getEventTime());
// Force a viewport synchronisation
mController.setForceRedraw();
mController.notifyLayerClientOfGeometryChange();
GeckoApp.mAppContext.showPluginViews();
mState = PanZoomState.TOUCHING;
mX.velocity = mY.velocity = 0.0f;
mX.locked = mY.locked = false;
mLastEventTime = detector.getEventTime();
}
@Override
public void onLongPress(MotionEvent motionEvent) {
public boolean getRedrawHint() {
return (mState == PanZoomState.NOTHING || mState == PanZoomState.FLING);
}
private void sendPointToGecko(String event, MotionEvent motionEvent) {
String json;
try {
PointF point = new PointF(motionEvent.getX(), motionEvent.getY());
@ -1044,50 +789,35 @@ public class PanZoomController
return;
}
json = PointUtils.toJSON(point).toString();
} catch(Exception ex) {
Log.w(LOGTAG, "Error building return: " + ex);
return; // json would be null
} catch (Exception e) {
Log.e(LOGTAG, "Unable to convert point to JSON for " + event, e);
return;
}
GeckoEvent e = new GeckoEvent("Gesture:LongPress", json);
GeckoAppShell.sendEventToGecko(e);
GeckoAppShell.sendEventToGecko(new GeckoEvent(event, json));
}
public boolean getRedrawHint() {
return (mState == PanZoomState.NOTHING || mState == PanZoomState.FLING);
@Override
public void onLongPress(MotionEvent motionEvent) {
sendPointToGecko("Gesture:LongPress", motionEvent);
}
@Override
public boolean onDown(MotionEvent motionEvent) {
String json;
try {
PointF point = new PointF(motionEvent.getX(), motionEvent.getY());
point = mController.convertViewPointToLayerPoint(point);
json = PointUtils.toJSON(point).toString();
} catch(Exception ex) {
throw new RuntimeException(ex);
}
GeckoEvent e = new GeckoEvent("Gesture:ShowPress", json);
GeckoAppShell.sendEventToGecko(e);
sendPointToGecko("Gesture:ShowPress", motionEvent);
return false;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
String json;
try {
PointF point = new PointF(motionEvent.getX(), motionEvent.getY());
point = mController.convertViewPointToLayerPoint(point);
json = PointUtils.toJSON(point).toString();
} catch(Exception ex) {
throw new RuntimeException(ex);
}
GeckoApp.mAppContext.mAutoCompletePopup.hide();
sendPointToGecko("Gesture:SingleTap", motionEvent);
return true;
}
GeckoEvent e = new GeckoEvent("Gesture:SingleTap", json);
GeckoAppShell.sendEventToGecko(e);
@Override
public boolean onDoubleTap(MotionEvent motionEvent) {
sendPointToGecko("Gesture:DoubleTap", motionEvent);
return true;
}
@ -1096,22 +826,6 @@ public class PanZoomController
GeckoAppShell.sendEventToGecko(e);
}
@Override
public boolean onDoubleTap(MotionEvent motionEvent) {
String json;
try {
PointF point = new PointF(motionEvent.getX(), motionEvent.getY());
point = mController.convertViewPointToLayerPoint(point);
json = PointUtils.toJSON(point).toString();
} catch(Exception ex) {
throw new RuntimeException(ex);
}
GeckoEvent e = new GeckoEvent("Gesture:DoubleTap", json);
GeckoAppShell.sendEventToGecko(e);
return true;
}
private boolean animatedZoomTo(RectF zoomToRect) {
GeckoApp.mAppContext.hidePluginViews();
GeckoApp.mAppContext.mAutoCompletePopup.hide();

View File

@ -0,0 +1,133 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* ***** 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 Android code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2012
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Kartikaya Gupta <kgupta@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 ***** */
package org.mozilla.gecko.ui;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.GeckoEventListener;
import org.json.JSONObject;
import org.json.JSONException;
import android.graphics.PointF;
import android.os.Handler;
import android.util.Log;
class SubdocumentScrollHelper implements GeckoEventListener {
private static final String LOGTAG = "GeckoSubdocumentScrollHelper";
private static String MESSAGE_PANNING_OVERRIDE = "Panning:Override";
private static String MESSAGE_CANCEL_OVERRIDE = "Panning:CancelOverride";
private static String MESSAGE_SCROLL = "Gesture:Scroll";
private static String MESSAGE_SCROLL_ACK = "Gesture:ScrollAck";
private final PanZoomController mPanZoomController;
private final Handler mUiHandler;
private boolean mOverridePanning;
private boolean mOverrideScrollAck;
private boolean mOverrideScrollPending;
SubdocumentScrollHelper(PanZoomController controller) {
mPanZoomController = controller;
// mUiHandler will be bound to the UI thread since that's where this constructor runs
mUiHandler = new Handler();
GeckoAppShell.registerGeckoEventListener(MESSAGE_PANNING_OVERRIDE, this);
GeckoAppShell.registerGeckoEventListener(MESSAGE_CANCEL_OVERRIDE, this);
GeckoAppShell.registerGeckoEventListener(MESSAGE_SCROLL_ACK, this);
}
boolean scrollBy(PointF displacement) {
if (! mOverridePanning) {
return false;
}
if (! mOverrideScrollAck) {
mOverrideScrollPending = true;
return true;
}
mOverrideScrollAck = false;
mOverrideScrollPending = false;
JSONObject json = new JSONObject();
try {
json.put("x", displacement.x);
json.put("y", displacement.y);
} catch (JSONException e) {
Log.e(LOGTAG, "Error forming subwindow scroll message: ", e);
}
GeckoAppShell.sendEventToGecko(new GeckoEvent(MESSAGE_SCROLL, json.toString()));
return true;
}
void cancel() {
mOverridePanning = false;
}
boolean scrolling() {
return mOverridePanning;
}
// GeckoEventListener implementation
public void handleMessage(final String event, final JSONObject message) {
// this comes in on the gecko thread; hand off the handling to the UI thread
mUiHandler.post(new Runnable() {
public void run() {
Log.i(LOGTAG, "Got message: " + event);
try {
if (MESSAGE_PANNING_OVERRIDE.equals(event)) {
mOverridePanning = true;
mOverrideScrollAck = true;
mOverrideScrollPending = false;
} else if (MESSAGE_CANCEL_OVERRIDE.equals(event)) {
mOverridePanning = false;
} else if (MESSAGE_SCROLL_ACK.equals(event)) {
mOverrideScrollAck = true;
if (mOverridePanning && mOverrideScrollPending) {
scrollBy(mPanZoomController.getDisplacement());
}
}
} catch (Exception e) {
Log.e(LOGTAG, "Exception handling message", e);
}
}
});
}
}

View File

@ -262,8 +262,13 @@ var BrowserApp = {
Cc["@mozilla.org/satchel/form-history;1"].getService(Ci.nsIFormHistory2);
let url = "about:home";
if ("arguments" in window && window.arguments[0])
url = window.arguments[0];
let restoreSession = false;
if ("arguments" in window) {
if (window.arguments[0])
url = window.arguments[0];
if (window.arguments[1])
restoreSession = window.arguments[1];
}
// XXX maybe we don't do this if the launch was kicked off from external
Services.io.offline = false;
@ -275,7 +280,7 @@ var BrowserApp = {
// restore the previous session
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
if (ss.shouldRestore()) {
if (restoreSession || ss.shouldRestore()) {
// A restored tab should not be active if we are loading a URL
let restoreToFront = false;
@ -2568,8 +2573,7 @@ var FormAssistant = {
Services.obs.addObserver(this, "FormAssist:AutoComplete", false);
Services.obs.addObserver(this, "FormAssist:Closed", false);
BrowserApp.deck.addEventListener("compositionstart", this, false);
BrowserApp.deck.addEventListener("compositionupdate", this, false);
BrowserApp.deck.addEventListener("input", this, false);
},
uninit: function() {
@ -2595,9 +2599,8 @@ var FormAssistant = {
},
handleEvent: function(aEvent) {
switch (aEvent.type) {
case "compositionstart":
case "compositionupdate":
switch (aEvent.type) {
case "input":
let currentElement = aEvent.target;
if (!this._isAutocomplete(currentElement))
break;
@ -2605,7 +2608,7 @@ var FormAssistant = {
// Keep track of input element so we can fill it in if the user
// selects an autocomplete suggestion
this._currentInputElement = currentElement;
let suggestions = this._getAutocompleteSuggestions(aEvent.data, currentElement);
let suggestions = this._getAutocompleteSuggestions(currentElement.value, currentElement);
let rect = currentElement.getBoundingClientRect();
let zoom = BrowserApp.selectedTab.viewport.zoom;

View File

@ -11,13 +11,19 @@ function dump(a) {
}
function openWindow(aParent, aURL, aTarget, aFeatures, aArgs) {
let argString = null;
if (aArgs && !(aArgs instanceof Ci.nsISupportsArray)) {
argString = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
argString.data = aArgs;
}
let argsArray = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
let urlString = null;
let restoreSessionBool = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
return Services.ww.openWindow(aParent, aURL, aTarget, aFeatures, argString || aArgs);
if ("url" in aArgs) {
urlString = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
urlString.data = aArgs.url;
}
restoreSessionBool.data = "restoreSession" in aArgs ? aArgs.restoreSession : false;
argsArray.AppendElement(urlString, false);
argsArray.AppendElement(restoreSessionBool, false);
return Services.ww.openWindow(aParent, aURL, aTarget, aFeatures, argsArray);
}
@ -41,8 +47,12 @@ function BrowserCLH() {}
BrowserCLH.prototype = {
handle: function fs_handle(aCmdLine) {
let urlParam = "about:home";
let restoreSession = false;
try {
urlParam = aCmdLine.handleFlagWithParam("remote", false);
urlParam = aCmdLine.handleFlagWithParam("remote", false);
} catch (e) { /* Optional */ }
try {
restoreSession = aCmdLine.handleFlag("restoresession", false);
} catch (e) { /* Optional */ }
try {
@ -54,7 +64,11 @@ BrowserCLH.prototype = {
if (browserWin) {
browserWin.browserDOMWindow.openURI(uri, null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB, Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
} else {
browserWin = openWindow(null, "chrome://browser/content/browser.xul", "_blank", "chrome,dialog=no,all", urlParam);
let args = {
url: urlParam,
restoreSession: restoreSession
};
browserWin = openWindow(null, "chrome://browser/content/browser.xul", "_blank", "chrome,dialog=no,all", args);
}
aCmdLine.preventDefault = true;

View File

@ -26,6 +26,7 @@
<string name="sync_subtitle_fail">&sync.subtitle.fail.label;</string>
<string name="sync_button_tryagain">&sync.button.tryagain.label;</string>
<string name="sync_button_manual">&sync.button.manual.label;</string>
<string name="sync_subtitle_nointernet">&sync.subtitle.nointernet.label;</string>
<!-- Setup Success -->
<string name="sync_title_success">&sync.title.success.label;</string>
@ -39,6 +40,7 @@
<!-- Common text -->
<string name="sync_button_cancel">&sync.button.cancel.label;</string>
<string name="sync_button_connect">&sync.button.connect.label;</string>
<string name="sync_button_ok">&sync.button.ok.label;</string>
<!-- Account strings -->
<string name="sync_account_label">&sync.account.label.label;</string>

View File

@ -43,10 +43,17 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS =
ifdef MOZ_LINKER
DIRS += linker
endif
ifeq (android,$(MOZ_WIDGET_TOOLKIT))
DIRS += android
endif
DIRS += build
TEST_DIRS = tests
include $(topsrcdir)/config/rules.mk

View File

@ -61,17 +61,13 @@
#include "APKOpen.h"
#include <sys/time.h>
#include <sys/resource.h>
#include "Zip.h"
/* Android headers don't define RUSAGE_THREAD */
#ifndef RUSAGE_THREAD
#define RUSAGE_THREAD 1
#endif
/* compression methods */
#define STORE 0
#define DEFLATE 8
#define LZMA 14
enum StartupEvent {
#define mozilla_StartupTimeline_Event(ev, z) ev,
#include "StartupTimeline.h"
@ -85,58 +81,6 @@ void StartupTimeline_Record(StartupEvent ev, struct timeval *tm)
sStartupTimeline[ev] = (((uint64_t)tm->tv_sec * 1000000LL) + (uint64_t)tm->tv_usec);
}
struct local_file_header {
uint32_t signature;
uint16_t min_version;
uint16_t general_flag;
uint16_t compression;
uint16_t lastmod_time;
uint16_t lastmod_date;
uint32_t crc32;
uint32_t compressed_size;
uint32_t uncompressed_size;
uint16_t filename_size;
uint16_t extra_field_size;
char data[0];
} __attribute__((__packed__));
struct cdir_entry {
uint32_t signature;
uint16_t creator_version;
uint16_t min_version;
uint16_t general_flag;
uint16_t compression;
uint16_t lastmod_time;
uint16_t lastmod_date;
uint32_t crc32;
uint32_t compressed_size;
uint32_t uncompressed_size;
uint16_t filename_size;
uint16_t extra_field_size;
uint16_t file_comment_size;
uint16_t disk_num;
uint16_t internal_attr;
uint32_t external_attr;
uint32_t offset;
char data[0];
} __attribute__((__packed__));
#define CDIR_END_SIG 0x06054b50
struct cdir_end {
uint32_t signature;
uint16_t disk_num;
uint16_t cdir_disk;
uint16_t disk_entries;
uint16_t cdir_entries;
uint32_t cdir_size;
uint32_t cdir_offset;
uint16_t comment_size;
char comment[0];
} __attribute__((__packed__));
static size_t zip_size;
static int zip_fd;
static struct mapping_info * lib_mapping = NULL;
NS_EXPORT const struct mapping_info *
@ -164,52 +108,6 @@ createAshmem(size_t bytes, const char *name)
return -1;
}
static void * map_file (const char *file)
{
int fd = open(file, O_RDONLY);
if (fd == -1) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoMapFile", "map_file open %s", strerror(errno));
return NULL;
}
zip_fd = fd;
struct stat s;
if (fstat(fd, &s) == -1) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoMapFile", "map_file fstat %s", strerror(errno));
return NULL;
}
zip_size = s.st_size;
void *addr = mmap(NULL, zip_size, PROT_READ, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoMapFile", "map_file mmap %s", strerror(errno));
return NULL;
}
return addr;
}
static uint32_t cdir_entry_size (struct cdir_entry *entry)
{
return sizeof(*entry) +
letoh16(entry->filename_size) +
letoh16(entry->extra_field_size) +
letoh16(entry->file_comment_size);
}
static struct cdir_entry *
find_cdir_entry (struct cdir_entry *entry, int count, const char *name)
{
size_t name_size = strlen(name);
while (count--) {
if (letoh16(entry->filename_size) == name_size &&
!memcmp(entry->data, name, name_size))
return entry;
entry = (struct cdir_entry *)((char *)entry + cdir_entry_size(entry));
}
return NULL;
}
#define SHELL_WRAPPER0(name) \
typedef void (*name ## _t)(JNIEnv *, jclass); \
static name ## _t f_ ## name; \
@ -296,7 +194,6 @@ SHELL_WRAPPER1(onChangeNetworkLinkStatus, jstring)
SHELL_WRAPPER1(reportJavaCrash, jstring)
SHELL_WRAPPER0(executeNextRunnable)
SHELL_WRAPPER1(cameraCallbackBridge, jbyteArray)
SHELL_WRAPPER1(notifyUriVisited, jstring)
SHELL_WRAPPER3(notifyBatteryChange, jdouble, jboolean, jdouble);
SHELL_WRAPPER3(notifySmsReceived, jstring, jstring, jlong);
SHELL_WRAPPER0(bindWidgetTexture);
@ -311,9 +208,9 @@ extern "C" int extractLibs = 0;
#endif
static void
extractFile(const char * path, const struct cdir_entry *entry, void * data)
extractFile(const char * path, Zip::Stream &s)
{
uint32_t size = letoh32(entry->uncompressed_size);
uint32_t size = s.GetUncompressedSize();
struct stat status;
if (!stat(path, &status) &&
@ -342,8 +239,8 @@ extractFile(const char * path, const struct cdir_entry *entry, void * data)
}
z_stream strm = {
next_in: (Bytef *)data,
avail_in: letoh32(entry->compressed_size),
next_in: (Bytef *)s.GetBuffer(),
avail_in: s.GetSize(),
total_in: 0,
next_out: (Bytef *)buf,
@ -376,15 +273,15 @@ extractFile(const char * path, const struct cdir_entry *entry, void * data)
}
static void
extractLib(const struct cdir_entry *entry, void * data, void * dest)
extractLib(Zip::Stream &s, void * dest)
{
z_stream strm = {
next_in: (Bytef *)data,
avail_in: letoh32(entry->compressed_size),
next_in: (Bytef *)s.GetBuffer(),
avail_in: s.GetSize(),
total_in: 0,
next_out: (Bytef *)dest,
avail_out: letoh32(entry->uncompressed_size),
avail_out: s.GetUncompressedSize(),
total_out: 0
};
@ -401,8 +298,8 @@ extractLib(const struct cdir_entry *entry, void * data, void * dest)
if (ret != Z_OK)
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "inflateEnd failed: %s", strm.msg);
if (strm.total_out != letoh32(entry->uncompressed_size))
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "File not fully uncompressed! %d / %d", strm.total_out, letoh32(entry->uncompressed_size));
if (strm.total_out != s.GetUncompressedSize())
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "File not fully uncompressed! %d / %d", strm.total_out, s.GetUncompressedSize());
}
static int cache_count = 0;
@ -481,24 +378,23 @@ addLibCacheFd(const char *libName, int fd, uint32_t lib_size = 0, void* buffer =
info->buffer = buffer;
}
static void * mozload(const char * path, void *zip,
struct cdir_entry *cdir_start, uint16_t cdir_entries)
static void * mozload(const char * path, Zip *zip)
{
#ifdef DEBUG
struct timeval t0, t1;
gettimeofday(&t0, 0);
#endif
struct cdir_entry *entry = find_cdir_entry(cdir_start, cdir_entries, path);
struct local_file_header *file = (struct local_file_header *)((char *)zip + letoh32(entry->offset));
void * data = ((char *)&file->data) + letoh16(file->filename_size) + letoh16(file->extra_field_size);
void * handle;
void *handle;
Zip::Stream s;
if (!zip->GetStream(path, &s))
return NULL;
if (extractLibs) {
char fullpath[PATH_MAX];
snprintf(fullpath, PATH_MAX, "%s/%s", getenv("CACHE_PATH"), path);
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "resolved %s to %s", path, fullpath);
extractFile(fullpath, entry, data);
extractFile(fullpath, s);
handle = __wrap_dlopen(fullpath, RTLD_LAZY);
if (!handle)
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't load %s because %s", fullpath, __wrap_dlerror());
@ -513,13 +409,12 @@ static void * mozload(const char * path, void *zip,
return handle;
}
size_t offset = letoh32(entry->offset) + sizeof(*file) + letoh16(file->filename_size) + letoh16(file->extra_field_size);
bool skipLibCache = false;
int fd = zip_fd;
int fd;
void * buf = NULL;
uint32_t lib_size = letoh32(entry->uncompressed_size);
uint32_t lib_size = s.GetUncompressedSize();
int cache_fd = 0;
if (letoh16(file->compression) == DEFLATE) {
if (s.GetType() == Zip::Stream::DEFLATE) {
cache_fd = lookupLibCacheFd(path);
fd = cache_fd;
if (fd < 0)
@ -541,30 +436,27 @@ static void * mozload(const char * path, void *zip,
return NULL;
}
offset = 0;
if (cache_fd < 0) {
extractLib(entry, data, buf);
extractLib(s, buf);
#ifdef ANDROID_ARM_LINKER
/* We just extracted data that is going to be executed in the future.
* We thus need to ensure Instruction and Data cache coherency. */
cacheflush((unsigned) buf, (unsigned) buf + entry->uncompressed_size, 0);
cacheflush((unsigned) buf, (unsigned) buf + s.GetUncompressedSize(), 0);
#endif
addLibCacheFd(path, fd, lib_size, buf);
}
// preload libxul, to avoid slowly demand-paging it
if (!strcmp(path, "libxul.so"))
madvise(buf, entry->uncompressed_size, MADV_WILLNEED);
data = buf;
madvise(buf, s.GetUncompressedSize(), MADV_WILLNEED);
}
#ifdef DEBUG
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loading %s with len %d (0x%08x) and offset %d (0x%08x)", path, lib_size, lib_size, offset, offset);
#endif
handle = moz_mapped_dlopen(path, RTLD_LAZY, fd, data,
lib_size, offset);
handle = moz_mapped_dlopen(path, RTLD_LAZY, fd, buf,
lib_size, 0);
if (!handle)
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't load %s because %s", path, __wrap_dlerror());
@ -581,22 +473,21 @@ static void * mozload(const char * path, void *zip,
}
static void *
extractBuf(const char * path, void *zip,
struct cdir_entry *cdir_start, uint16_t cdir_entries)
extractBuf(const char * path, Zip *zip)
{
struct cdir_entry *entry = find_cdir_entry(cdir_start, cdir_entries, path);
struct local_file_header *file = (struct local_file_header *)((char *)zip + letoh32(entry->offset));
void * data = ((char *)&file->data) + letoh16(file->filename_size) + letoh16(file->extra_field_size);
Zip::Stream s;
if (!zip->GetStream(path, &s))
return NULL;
void * buf = malloc(letoh32(entry->uncompressed_size));
void * buf = malloc(s.GetUncompressedSize());
if (buf == (void *)-1) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't alloc decompression buffer for %s", path);
return NULL;
}
if (letoh16(file->compression) == DEFLATE)
extractLib(entry, data, buf);
if (s.GetType() == Zip::Stream::DEFLATE)
extractLib(s, buf);
else
memcpy(buf, data, letoh32(entry->uncompressed_size));
memcpy(buf, s.GetBuffer(), s.GetUncompressedSize());
return buf;
}
@ -641,27 +532,14 @@ loadLibs(const char *apkName)
struct rusage usage1;
getrusage(RUSAGE_THREAD, &usage1);
void *zip = map_file(apkName);
struct cdir_end *dirend = (struct cdir_end *)((char *)zip + zip_size - sizeof(*dirend));
while ((void *)dirend > zip &&
letoh32(dirend->signature) != CDIR_END_SIG)
dirend = (struct cdir_end *)((char *)dirend - 1);
if (letoh32(dirend->signature) != CDIR_END_SIG) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't find end of central directory record");
return;
}
uint32_t cdir_offset = letoh32(dirend->cdir_offset);
uint16_t cdir_entries = letoh16(dirend->cdir_entries);
struct cdir_entry *cdir_start = (struct cdir_entry *)((char *)zip + cdir_offset);
Zip *zip = new Zip(apkName);
lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping));
#ifdef MOZ_CRASHREPORTER
file_ids = (char *)extractBuf("lib.id", zip, cdir_start, cdir_entries);
file_ids = (char *)extractBuf("lib.id", zip);
#endif
#define MOZLOAD(name) mozload("lib" name ".so", zip, cdir_start, cdir_entries)
#define MOZLOAD(name) mozload("lib" name ".so", zip)
MOZLOAD("mozalloc");
MOZLOAD("nspr4");
MOZLOAD("plc4");
@ -678,7 +556,7 @@ loadLibs(const char *apkName)
MOZLOAD("softokn3");
#undef MOZLOAD
close(zip_fd);
delete zip;
#ifdef MOZ_CRASHREPORTER
free(file_ids);
@ -703,7 +581,6 @@ loadLibs(const char *apkName)
GETFUNC(reportJavaCrash);
GETFUNC(executeNextRunnable);
GETFUNC(cameraCallbackBridge);
GETFUNC(notifyUriVisited);
GETFUNC(notifyBatteryChange);
GETFUNC(notifySmsReceived);
GETFUNC(bindWidgetTexture);

View File

@ -55,11 +55,14 @@ CPPSRCS = \
APKOpen.cpp \
$(NULL)
LOCAL_INCLUDES += -I$(srcdir)/../linker
LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/components/startup
ifdef MOZ_OLD_LINKER
LOCAL_INCLUDES += -I$(topsrcdir)/other-licenses/android
ifeq ($(CPU_ARCH),arm)
DEFINES += -DANDROID_ARM_LINKER
endif
endif
EXPORTS = APKOpen.h

View File

@ -81,12 +81,17 @@ endif
endif
ifeq (android, $(MOZ_WIDGET_TOOLKIT))
# Add Android linker
# Add Android specific code
EXTRA_DSO_LDOPTS += $(ZLIB_LIBS)
SHARED_LIBRARY_LIBS += $(call EXPAND_LIBNAME_PATH,android,$(DEPTH)/other-licenses/android)
SHARED_LIBRARY_LIBS += $(call EXPAND_LIBNAME_PATH,android,../android)
endif
ifdef MOZ_LINKER
# Add custom dynamic linker
SHARED_LIBRARY_LIBS += $(call EXPAND_LIBNAME_PATH,linker,../linker)
endif
ifeq (Android, $(OS_TARGET))
WRAP_LDFLAGS =
endif

22
mozglue/linker/Logging.h Normal file
View File

@ -0,0 +1,22 @@
/* 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 Logging_h
#define Logging_h
#ifdef ANDROID
#include <android/log.h>
#define log(...) __android_log_print(ANDROID_LOG_ERROR, "GeckoLinker", __VA_ARGS__)
#else
#include <cstdio>
#define log(format, ...) fprintf(stderr, format "\n", ##__VA_ARGS__)
#endif
#ifdef MOZ_DEBUG_LINKER
#define debug log
#else
#define debug(...)
#endif
#endif /* Logging_h */

View File

@ -0,0 +1,21 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = mozglue
LIBRARY_NAME = linker
FORCE_STATIC_LIB= 1
STL_FLAGS =
CPPSRCS = \
Zip.cpp \
$(NULL)
include $(topsrcdir)/config/rules.mk

93
mozglue/linker/Utils.h Normal file
View File

@ -0,0 +1,93 @@
/* 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 Utils_h
#define Utils_h
#include <stdint.h>
/**
* On architectures that are little endian and that support unaligned reads,
* we can use direct type, but on others, we want to have a special class
* to handle conversion and alignment issues.
*/
#if defined(__i386__) || defined(__x86_64__)
typedef uint16_t le_uint16;
typedef uint32_t le_uint32;
#else
/**
* Template that allows to find an unsigned int type from a (computed) bit size
*/
template <int s> struct UInt { };
template <> struct UInt<16> { typedef uint16_t Type; };
template <> struct UInt<32> { typedef uint32_t Type; };
/**
* Template to read 2 n-bit sized words as a 2*n-bit sized word, doing
* conversion from little endian and avoiding alignment issues.
*/
template <typename T>
class le_to_cpu
{
public:
operator typename UInt<16 * sizeof(T)>::Type() const
{
return (b << (sizeof(T) * 8)) | a;
}
private:
T a, b;
};
/**
* Type definitions
*/
typedef le_to_cpu<unsigned char> le_uint16;
typedef le_to_cpu<le_uint16> le_uint32;
#endif
/**
* AutoCloseFD is a RAII wrapper for POSIX file descriptors
*/
class AutoCloseFD
{
public:
AutoCloseFD(): fd(-1) { }
AutoCloseFD(int fd): fd(fd) { }
~AutoCloseFD()
{
if (fd != -1)
close(fd);
}
operator int() const
{
return fd;
}
int forget()
{
int _fd = fd;
fd = -1;
return _fd;
}
bool operator ==(int other) const
{
return fd == other;
}
int operator =(int other)
{
if (fd != -1)
close(fd);
fd = other;
return fd;
}
private:
int fd;
};
#endif /* Utils_h */

180
mozglue/linker/Zip.cpp Normal file
View File

@ -0,0 +1,180 @@
/* 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 <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <cstdlib>
#include <algorithm>
#include "Logging.h"
#include "Zip.h"
Zip::Zip(const char *filename, ZipCollection *collection)
: name(strdup(filename))
, mapped(MAP_FAILED)
, nextDir(NULL)
, entries(NULL)
, parent(collection)
{
/* Open and map the file in memory */
AutoCloseFD fd(open(name, O_RDONLY));
if (fd == -1) {
log("Error opening %s: %s", filename, strerror(errno));
return;
}
struct stat st;
if (fstat(fd, &st) == -1) {
log("Error stating %s: %s", filename, strerror(errno));
return;
}
size = st.st_size;
if (size <= sizeof(CentralDirectoryEnd)) {
log("Error reading %s: too short", filename);
return;
}
mapped = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
if (mapped == MAP_FAILED) {
log("Error mmapping %s: %s", filename, strerror(errno));
return;
}
debug("Mapped %s @%p", filename, mapped);
/* Store the first Local File entry */
nextFile = LocalFile::validate(mapped);
}
Zip::~Zip()
{
if (parent)
parent->Forget(this);
if (mapped != MAP_FAILED) {
munmap(mapped, size);
debug("Unmapped %s @%p", name, mapped);
}
free(name);
}
bool
Zip::GetStream(const char *path, Zip::Stream *out) const
{
debug("%s - GetFile %s", name, path);
/* Fast path: if the Local File header on store matches, we can return the
* corresponding stream right away.
* However, the Local File header may not contain enough information, in
* which case the 3rd bit on the generalFlag is set. Unfortunately, this
* bit is also set in some archives even when we do have the data (most
* notably the android packages as built by the Mozilla build system).
* So instead of testing the generalFlag bit, only use the fast path when
* we haven't read the central directory entries yet, and when the
* compressed size as defined in the header is not filled (which is a
* normal condition for the bit to be set). */
if (nextFile && nextFile->GetName().Equals(path) &&
!entries && (nextFile->compressedSize != 0)) {
debug("%s - %s was next file: fast path", name, path);
/* Fill Stream info from Local File header content */
const char *data = reinterpret_cast<const char *>(nextFile->GetData());
out->compressedBuf = data;
out->compressedSize = nextFile->compressedSize;
out->uncompressedSize = nextFile->uncompressedSize;
out->type = static_cast<Stream::Type>(uint16_t(nextFile->compression));
/* Find the next Local File header. It is usually simply following the
* compressed stream, but in cases where the 3rd bit of the generalFlag
* is set, there is a Data Descriptor header before. */
data += nextFile->compressedSize;
if ((nextFile->generalFlag & 0x8) && DataDescriptor::validate(data)) {
data += sizeof(DataDescriptor);
}
nextFile = LocalFile::validate(data);
return true;
}
/* If the directory entry we have in store doesn't match, scan the Central
* Directory for the entry corresponding to the given path */
if (!nextDir || !nextDir->GetName().Equals(path)) {
const DirectoryEntry *entry = GetFirstEntry();
debug("%s - Scan directory entries in search for %s", name, path);
while (entry && !entry->GetName().Equals(path)) {
entry = entry->GetNext();
}
nextDir = entry;
}
if (!nextDir) {
debug("%s - Couldn't find %s", name, path);
return false;
}
/* Find the Local File header corresponding to the Directory entry that
* was found. */
nextFile = LocalFile::validate(static_cast<const char *>(mapped)
+ nextDir->offset);
if (!nextFile) {
log("%s - Couldn't find the Local File header for %s", name, path);
return false;
}
/* Fill Stream info from Directory entry content */
const char *data = reinterpret_cast<const char *>(nextFile->GetData());
out->compressedBuf = data;
out->compressedSize = nextDir->compressedSize;
out->uncompressedSize = nextDir->uncompressedSize;
out->type = static_cast<Stream::Type>(uint16_t(nextDir->compression));
/* Store the next directory entry */
nextDir = nextDir->GetNext();
nextFile = NULL;
return true;
}
const Zip::DirectoryEntry *
Zip::GetFirstEntry() const
{
if (entries || mapped == MAP_FAILED)
return entries; // entries is NULL in the second case above
const CentralDirectoryEnd *end = NULL;
const char *_end = static_cast<const char *>(mapped) + size
- sizeof(CentralDirectoryEnd);
/* Scan for the Central Directory End */
for (; _end > mapped && !end; _end--)
end = CentralDirectoryEnd::validate(_end);
if (!end) {
log("%s - Couldn't find end of central directory record", name);
return NULL;
}
entries = DirectoryEntry::validate(static_cast<const char *>(mapped)
+ end->offset);
if (!entries) {
log("%s - Couldn't find central directory record", name);
}
return entries;
}
mozilla::TemporaryRef<Zip>
ZipCollection::GetZip(const char *path)
{
/* Search the list of Zips we already have for a match */
for (std::vector<Zip *>::iterator it = zips.begin(); it < zips.end(); ++it) {
if (strcmp((*it)->GetName(), path) == 0)
return *it;
}
Zip *zip = new Zip(path, this);
zips.push_back(zip);
return zip;
}
void
ZipCollection::Forget(Zip *zip)
{
debug("ZipCollection::Forget(\"%s\")", zip->GetName());
std::vector<Zip *>::iterator it = std::find(zips.begin(), zips.end(), zip);
if (*it == zip)
zips.erase(it);
else
debug("ZipCollection::Forget: didn't find \"%s\" in bookkeeping", zip->GetName());
}

311
mozglue/linker/Zip.h Normal file
View File

@ -0,0 +1,311 @@
/* 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 Zip_h
#define Zip_h
#include <cstring>
#include <stdint.h>
#include <vector>
#include "Utils.h"
#include "mozilla/RefPtr.h"
/**
* Forward declaration
*/
class ZipCollection;
/**
* Class to handle access to Zip archive streams. The Zip archive is mapped
* in memory, and streams are direct references to that mapped memory.
* Zip files are assumed to be correctly formed. No boundary checks are
* performed, which means hand-crafted malicious Zip archives can make the
* code fail in bad ways. However, since the only intended use is to load
* libraries from Zip archives, there is no interest in making this code
* safe, since the libraries could contain malicious code anyways.
*/
class Zip: public mozilla::RefCounted<Zip>
{
public:
/**
* Create a Zip instance for the given file name. In case of error, the
* Zip instance is still created but methods will error out.
*/
Zip(const char *filename, ZipCollection *collection = NULL);
/**
* Destructor
*/
~Zip();
/**
* Class used to access Zip archive item streams
*/
class Stream
{
public:
/**
* Stream types
*/
enum Type {
STORE = 0,
DEFLATE = 8
};
/**
* Constructor
*/
Stream(): compressedBuf(NULL), compressedSize(0), uncompressedSize(0)
, type(STORE) { }
const void *GetBuffer() { return compressedBuf; }
size_t GetSize() { return compressedSize; }
size_t GetUncompressedSize() { return uncompressedSize; }
Type GetType() { return type; }
protected:
friend class Zip;
const void *compressedBuf;
size_t compressedSize;
size_t uncompressedSize;
Type type;
};
/**
* Returns a stream from the Zip archive.
*/
bool GetStream(const char *path, Stream *out) const;
/**
* Returns the file name of the archive
*/
const char *GetName() const
{
return name;
}
private:
/* File name of the archive */
char *name;
/* Address where the Zip archive is mapped */
void *mapped;
/* Size of the archive */
size_t size;
/**
* Strings (file names, comments, etc.) in the Zip headers are NOT zero
* terminated. This class is a helper around them.
*/
class StringBuf
{
public:
/**
* Constructor
*/
StringBuf(const char *buf, size_t length): buf(buf), length(length) { }
/**
* Returns whether the string has the same content as the given zero
* terminated string.
*/
bool Equals(const char *str) const
{
return strncmp(str, buf, length) == 0;
}
private:
const char *buf;
size_t length;
};
/* All the following types need to be packed */
#pragma pack(1)
/**
* A Zip archive is an aggregate of entities which all start with a
* signature giving their type. This template is to be used as a base
* class for these entities.
*/
template <typename T>
class SignedEntity
{
public:
/**
* Equivalent to reinterpret_cast<const T *>(buf), with an additional
* check of the signature.
*/
static const T *validate(const void *buf)
{
const T *ret = static_cast<const T *>(buf);
if (ret->signature == T::magic)
return ret;
return NULL;
}
private:
le_uint32 signature;
};
/**
* Header used to describe a Local File entry. The header is followed by
* the file name and an extra field, then by the data stream.
*/
struct LocalFile: public SignedEntity<LocalFile>
{
/* Signature for a Local File header */
static const uint32_t magic = 0x04034b50;
/**
* Returns the file name
*/
StringBuf GetName() const
{
return StringBuf(reinterpret_cast<const char *>(this) + sizeof(*this),
filenameSize);
}
/**
* Returns a pointer to the data associated with this header
*/
const void *GetData() const
{
return reinterpret_cast<const char *>(this) + sizeof(*this)
+ filenameSize + extraFieldSize;
}
le_uint16 minVersion;
le_uint16 generalFlag;
le_uint16 compression;
le_uint16 lastModifiedTime;
le_uint16 lastModifiedDate;
le_uint32 CRC32;
le_uint32 compressedSize;
le_uint32 uncompressedSize;
le_uint16 filenameSize;
le_uint16 extraFieldSize;
};
/**
* In some cases, when a zip archive is created, compressed size and CRC
* are not known when writing the Local File header. In these cases, the
* 3rd bit of the general flag in the Local File header is set, and there
* is an additional header following the compressed data.
*/
struct DataDescriptor: public SignedEntity<DataDescriptor>
{
/* Signature for a Data Descriptor header */
static const uint32_t magic = 0x08074b50;
le_uint32 CRC32;
le_uint32 compressedSize;
le_uint32 uncompressedSize;
};
/**
* Header used to describe a Central Directory Entry. The header is
* followed by the file name, an extra field, and a comment.
*/
struct DirectoryEntry: public SignedEntity<DirectoryEntry>
{
/* Signature for a Central Directory Entry header */
static const uint32_t magic = 0x02014b50;
/**
* Returns the file name
*/
StringBuf GetName() const
{
return StringBuf(reinterpret_cast<const char *>(this) + sizeof(*this),
filenameSize);
}
/**
* Returns the Central Directory Entry following this one.
*/
const DirectoryEntry *GetNext() const
{
return validate(reinterpret_cast<const char *>(this) + sizeof(*this)
+ filenameSize + extraFieldSize + fileCommentSize);
}
le_uint16 creatorVersion;
le_uint16 minVersion;
le_uint16 generalFlag;
le_uint16 compression;
le_uint16 lastModifiedTime;
le_uint16 lastModifiedDate;
le_uint32 CRC32;
le_uint32 compressedSize;
le_uint32 uncompressedSize;
le_uint16 filenameSize;
le_uint16 extraFieldSize;
le_uint16 fileCommentSize;
le_uint16 diskNum;
le_uint16 internalAttributes;
le_uint32 externalAttributes;
le_uint32 offset;
};
/**
* Header used to describe the End of Central Directory Record.
*/
struct CentralDirectoryEnd: public SignedEntity<CentralDirectoryEnd>
{
/* Signature for the End of Central Directory Record */
static const uint32_t magic = 0x06054b50;
le_uint16 diskNum;
le_uint16 startDisk;
le_uint16 recordsOnDisk;
le_uint16 records;
le_uint32 size;
le_uint32 offset;
le_uint16 commentSize;
};
#pragma pack()
/**
* Returns the first Directory entry
*/
const DirectoryEntry *GetFirstEntry() const;
/* Pointer to the Local File Entry following the last one GetStream() used.
* This is used by GetStream to avoid scanning the Directory Entries when the
* requested entry is that one. */
mutable const LocalFile *nextFile;
/* Likewise for the next Directory entry */
mutable const DirectoryEntry *nextDir;
/* Pointer to the Directory entries */
mutable const DirectoryEntry *entries;
/* ZipCollection containing this Zip */
mutable ZipCollection *parent;
};
/**
* Class for bookkeeping Zip instances
*/
class ZipCollection
{
public:
/**
* Get a Zip instance for the given path. If there is an existing one
* already, return that one, otherwise create a new one.
*/
mozilla::TemporaryRef<Zip> GetZip(const char *path);
protected:
/**
* Forget about the given Zip instance. This method is meant to be called
* by the Zip destructor.
*/
friend Zip::~Zip();
void Forget(Zip *zip);
private:
/* Zip instances bookkept in this collection */
std::vector<Zip *> zips;
};
#endif /* Zip_h */

33
mozglue/tests/Makefile.in Normal file
View File

@ -0,0 +1,33 @@
# 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/.
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
ifdef MOZ_LINKER
CPPSRCS = \
TestZip.cpp \
$(NULL)
SIMPLE_PROGRAMS := $(CPPSRCS:.cpp=$(BIN_SUFFIX))
NO_DIST_INSTALL = 1
STL_FLAGS =
LOCAL_INCLUDES += -I$(srcdir)/../linker
# Only link against the linker, not mozglue
MOZ_GLUE_PROGRAM_LDFLAGS =
MOZ_GLUE_LDFLAGS =
LIBS += $(call EXPAND_LIBNAME_PATH,linker,../linker)
endif
include $(topsrcdir)/config/rules.mk
ifdef MOZ_LINKER
check::
@$(EXIT_ON_ERROR) ./TestZip$(BIN_SUFFIX) $(srcdir)
endif

65
mozglue/tests/TestZip.cpp Normal file
View File

@ -0,0 +1,65 @@
/* 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 <cstdio>
#include <unistd.h>
#include "Zip.h"
/**
* test.zip is a basic test zip file with a central directory. It contains
* four entries, in the following order:
* "foo", "bar", "baz", "qux".
* The entries are going to be read out of order.
*/
const char *test_entries[] = {
"baz", "foo", "bar", "qux"
};
/**
* no_central_dir.zip is a hand crafted test zip with no central directory
* entries. The Zip reader is expected to be able to traverse these entries
* if requested in order, without reading a central directory
* - First entry is a file "a", STOREd.
* - Second entry is a file "b", STOREd, using a data descriptor. CRC is
* unknown, but compressed and uncompressed sizes are known in the local
* file header.
* - Third entry is a file "c", DEFLATEd, using a data descriptor. CRC,
* compressed and uncompressed sizes are known in the local file header.
* This is the kind of entry that can be found in a zip that went through
* zipalign if it had a data descriptor originally.
* - Fourth entry is a file "d", STOREd.
*/
const char *no_central_dir_entries[] = {
"a", "b", "c", "d"
};
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "TEST-FAIL | TestZip | Expecting the directory containing test Zips\n");
return 1;
}
chdir(argv[1]);
Zip::Stream s;
Zip z("test.zip");
for (size_t i = 0; i < sizeof(test_entries) / sizeof(*test_entries); i++) {
if (!z.GetStream(test_entries[i], &s)) {
fprintf(stderr, "TEST-UNEXPECTED-FAIL | TestZip | test.zip: Couldn't get entry \"%s\"\n", test_entries[i]);
return 1;
}
}
fprintf(stderr, "TEST-PASS | TestZip | test.zip could be accessed fully\n");
Zip z2("no_central_dir.zip");
for (size_t i = 0; i < sizeof(no_central_dir_entries)
/ sizeof(*no_central_dir_entries); i++) {
if (!z2.GetStream(no_central_dir_entries[i], &s)) {
fprintf(stderr, "TEST-UNEXPECTED-FAIL | TestZip | no_central_dir.zip: Couldn't get entry \"%s\"\n", no_central_dir_entries[i]);
return 1;
}
}
fprintf(stderr, "TEST-PASS | TestZip | no_central_dir.zip could be accessed in order\n");
return 0;
}

Binary file not shown.

BIN
mozglue/tests/test.zip Normal file

Binary file not shown.

View File

@ -393,44 +393,42 @@ FTPChannelChild::DoOnStopRequest(const nsresult& statusCode)
Send__delete__(this);
}
class FTPCancelEarlyEvent : public ChannelEvent
class FTPFailedAsyncOpenEvent : public ChannelEvent
{
public:
FTPCancelEarlyEvent(FTPChannelChild* aChild, nsresult aStatus)
FTPFailedAsyncOpenEvent(FTPChannelChild* aChild, nsresult aStatus)
: mChild(aChild), mStatus(aStatus) {}
void Run() { mChild->DoCancelEarly(mStatus); }
void Run() { mChild->DoFailedAsyncOpen(mStatus); }
private:
FTPChannelChild* mChild;
nsresult mStatus;
};
bool
FTPChannelChild::RecvCancelEarly(const nsresult& statusCode)
FTPChannelChild::RecvFailedAsyncOpen(const nsresult& statusCode)
{
if (mEventQ.ShouldEnqueue()) {
mEventQ.Enqueue(new FTPCancelEarlyEvent(this, statusCode));
mEventQ.Enqueue(new FTPFailedAsyncOpenEvent(this, statusCode));
} else {
DoCancelEarly(statusCode);
DoFailedAsyncOpen(statusCode);
}
return true;
}
void
FTPChannelChild::DoCancelEarly(const nsresult& statusCode)
FTPChannelChild::DoFailedAsyncOpen(const nsresult& statusCode)
{
if (mCanceled)
return;
mCanceled = true;
mStatus = statusCode;
mIsPending = false;
if (mLoadGroup)
mLoadGroup->RemoveRequest(this, nsnull, statusCode);
if (mListener) {
mListener->OnStartRequest(this, mListenerContext);
mIsPending = false;
mListener->OnStopRequest(this, mListenerContext, statusCode);
} else {
mIsPending = false;
}
mListener = nsnull;
@ -492,6 +490,27 @@ FTPChannelChild::Suspend()
return NS_OK;
}
nsresult
FTPChannelChild::AsyncCall(void (FTPChannelChild::*funcPtr)(),
nsRunnableMethod<FTPChannelChild> **retval)
{
nsresult rv;
nsRefPtr<nsRunnableMethod<FTPChannelChild> > event = NS_NewRunnableMethod(this, funcPtr);
rv = NS_DispatchToCurrentThread(event);
if (NS_SUCCEEDED(rv) && retval) {
*retval = event;
}
return rv;
}
void
FTPChannelChild::CompleteResume()
{
mEventQ.Resume();
}
NS_IMETHODIMP
FTPChannelChild::Resume()
{
@ -499,7 +518,7 @@ FTPChannelChild::Resume()
if (!--mSuspendCount) {
SendResume();
mEventQ.Resume(); // TODO: make this async: see HttpChannelChild::Resume
AsyncCall(&FTPChannelChild::CompleteResume);
}
return NS_OK;
}

View File

@ -111,7 +111,7 @@ protected:
const PRUint32& offset,
const PRUint32& count);
NS_OVERRIDE bool RecvOnStopRequest(const nsresult& statusCode);
NS_OVERRIDE bool RecvCancelEarly(const nsresult& statusCode);
NS_OVERRIDE bool RecvFailedAsyncOpen(const nsresult& statusCode);
NS_OVERRIDE bool RecvDeleteSelf();
void DoOnStartRequest(const PRInt32& aContentLength,
@ -123,16 +123,21 @@ protected:
const PRUint32& offset,
const PRUint32& count);
void DoOnStopRequest(const nsresult& statusCode);
void DoCancelEarly(const nsresult& statusCode);
void DoFailedAsyncOpen(const nsresult& statusCode);
void DoDeleteSelf();
friend class FTPStartRequestEvent;
friend class FTPDataAvailableEvent;
friend class FTPStopRequestEvent;
friend class FTPCancelEarlyEvent;
friend class FTPFailedAsyncOpenEvent;
friend class FTPDeleteSelfEvent;
private:
// Called asynchronously from Resume: continues any pending calls into client.
void CompleteResume();
nsresult AsyncCall(void (FTPChannelChild::*funcPtr)(),
nsRunnableMethod<FTPChannelChild> **retval = nsnull);
nsCOMPtr<nsIInputStream> mUploadStream;
bool mIPCOpen;

View File

@ -105,12 +105,12 @@ FTPChannelParent::RecvAsyncOpen(const IPC::URI& aURI,
nsresult rv;
nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
if (NS_FAILED(rv))
return SendCancelEarly(rv);
return SendFailedAsyncOpen(rv);
nsCOMPtr<nsIChannel> chan;
rv = NS_NewChannel(getter_AddRefs(chan), uri, ios);
if (NS_FAILED(rv))
return SendCancelEarly(rv);
return SendFailedAsyncOpen(rv);
mChannel = static_cast<nsFtpChannel*>(chan.get());
@ -119,16 +119,16 @@ FTPChannelParent::RecvAsyncOpen(const IPC::URI& aURI,
// contentType and contentLength are ignored
rv = mChannel->SetUploadStream(upload, EmptyCString(), 0);
if (NS_FAILED(rv))
return SendCancelEarly(rv);
return SendFailedAsyncOpen(rv);
}
rv = mChannel->ResumeAt(aStartPos, aEntityID);
if (NS_FAILED(rv))
return SendCancelEarly(rv);
return SendFailedAsyncOpen(rv);
rv = mChannel->AsyncOpen(this, nsnull);
if (NS_FAILED(rv))
return SendCancelEarly(rv);
return SendFailedAsyncOpen(rv);
return true;
}
@ -153,21 +153,24 @@ FTPChannelParent::RecvConnectChannel(const PRUint32& channelId)
bool
FTPChannelParent::RecvCancel(const nsresult& status)
{
mChannel->Cancel(status);
if (mChannel)
mChannel->Cancel(status);
return true;
}
bool
FTPChannelParent::RecvSuspend()
{
mChannel->Suspend();
if (mChannel)
mChannel->Suspend();
return true;
}
bool
FTPChannelParent::RecvResume()
{
mChannel->Resume();
if (mChannel)
mChannel->Resume();
return true;
}

View File

@ -69,7 +69,7 @@ child:
PRTime aLastModified, nsCString aEntityID, URI aURI);
OnDataAvailable(nsCString data, PRUint32 offset, PRUint32 count);
OnStopRequest(nsresult statusCode);
CancelEarly(nsresult statusCode);
FailedAsyncOpen(nsresult statusCode);
DeleteSelf();
};

View File

@ -1217,7 +1217,7 @@ HttpChannelChild::ResumeAt(PRUint64 startPos, const nsACString& entityID)
NS_IMETHODIMP
HttpChannelChild::SetPriority(PRInt32 aPriority)
{
PRInt16 newValue = CLAMP(aPriority, PR_INT16_MIN, PR_INT16_MAX);
PRInt16 newValue = clamped(aPriority, PR_INT16_MIN, PR_INT16_MAX);
if (mPriority == newValue)
return NS_OK;
mPriority = newValue;

View File

@ -234,10 +234,6 @@ PRTimeToSeconds(PRTime t_usec)
#define NowInSeconds() PRTimeToSeconds(PR_Now())
// ripped from glib.h
#undef CLAMP
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
// round q-value to one decimal place; return most significant digit as uint.
#define QVAL_TO_UINT(q) ((unsigned int) ((q + 0.05) * 10.0))

View File

@ -61,7 +61,6 @@ HTTP_ATOM(Authentication, "Authentication")
HTTP_ATOM(Authorization, "Authorization")
HTTP_ATOM(Cache_Control, "Cache-Control")
HTTP_ATOM(Connection, "Connection")
HTTP_ATOM(Content_Base, "Content-Base")
HTTP_ATOM(Content_Disposition, "Content-Disposition")
HTTP_ATOM(Content_Encoding, "Content-Encoding")
HTTP_ATOM(Content_Language, "Content-Language")
@ -69,24 +68,20 @@ HTTP_ATOM(Content_Length, "Content-Length")
HTTP_ATOM(Content_Location, "Content-Location")
HTTP_ATOM(Content_MD5, "Content-MD5")
HTTP_ATOM(Content_Range, "Content-Range")
HTTP_ATOM(Content_Transfer_Encoding, "Content-Transfer-Encoding")
HTTP_ATOM(Content_Type, "Content-Type")
HTTP_ATOM(Cookie, "Cookie")
HTTP_ATOM(Date, "Date")
HTTP_ATOM(DAV, "DAV")
HTTP_ATOM(Depth, "Depth")
HTTP_ATOM(Derived_From, "Derived-From")
HTTP_ATOM(Destination, "Destination")
HTTP_ATOM(DoNotTrack, "DNT")
HTTP_ATOM(ETag, "Etag")
HTTP_ATOM(Expect, "Expect")
HTTP_ATOM(Expires, "Expires")
HTTP_ATOM(Forwarded, "Forwarded")
HTTP_ATOM(From, "From")
HTTP_ATOM(Host, "Host")
HTTP_ATOM(If, "If")
HTTP_ATOM(If_Match, "If-Match")
HTTP_ATOM(If_Match_Any, "If-Match-Any")
HTTP_ATOM(If_Modified_Since, "If-Modified-Since")
HTTP_ATOM(If_None_Match, "If-None-Match")
HTTP_ATOM(If_None_Match_Any, "If-None-Match-Any")
@ -98,8 +93,6 @@ HTTP_ATOM(Lock_Token, "Lock-Token")
HTTP_ATOM(Link, "Link")
HTTP_ATOM(Location, "Location")
HTTP_ATOM(Max_Forwards, "Max-Forwards")
HTTP_ATOM(Message_Id, "Message-Id")
HTTP_ATOM(Mime, "Mime")
HTTP_ATOM(Overwrite, "Overwrite")
HTTP_ATOM(Pragma, "Pragma")
HTTP_ATOM(Proxy_Authenticate, "Proxy-Authenticate")

View File

@ -46,6 +46,9 @@ MODULE = android
LIBRARY_NAME = android
FORCE_STATIC_LIB = 1
CSRCS =
ifdef MOZ_OLD_LINKER
DEFINES += \
-DLINKER_DEBUG=0 \
-DMOZ_LINKER \
@ -61,15 +64,20 @@ DEFINES += -DANDROID_X86_LINKER
endif
endif
CSRCS = \
CSRCS += \
ba.c \
debugger.c \
dlfcn.c \
linker.c \
linker_format.c \
rt.c \
$(NULL)
endif
CSRCS += \
ev_streams.c \
ev_timers.c \
getaddrinfo.c \
linker.c \
linker_format.c \
ns_name.c \
ns_netint.c \
ns_parse.c \
@ -83,7 +91,6 @@ CSRCS = \
res_mkquery.c \
res_send.c \
res_state.c \
rt.c \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -115,7 +115,11 @@ CSRCS += md4.c
EXTRA_DEPS = $(NSS_DEP_LIBS)
DEFINES += -DNSS_ENABLE_ECC
DEFINES += \
-DNSS_ENABLE_ECC \
-DDLL_PREFIX=\"$(DLL_PREFIX)\" \
-DDLL_SUFFIX=\"$(DLL_SUFFIX)\" \
$(NULL)
# Use local includes because they are inserted before INCLUDES
# so that Mozilla's nss.h is used, not glibc's

View File

@ -828,7 +828,10 @@ nsNSSComponent::InstallLoadableRoots()
if (!directoryService)
return;
static const char nss_lib[] = "nss3";
const char *possible_ckbi_locations[] = {
nss_lib, // This special value means: search for ckbi in the directory
// where nss3 is.
NS_XPCOM_CURRENT_PROCESS_DIR,
NS_GRE_DIR,
0 // This special value means:
@ -846,9 +849,30 @@ nsNSSComponent::InstallLoadableRoots()
}
else
{
directoryService->Get( possible_ckbi_locations[il],
NS_GET_IID(nsILocalFile),
getter_AddRefs(mozFile));
if (possible_ckbi_locations[il] == nss_lib) {
// Get the location of the nss3 library.
char *nss_path = PR_GetLibraryFilePathname(DLL_PREFIX "nss3" DLL_SUFFIX,
(PRFuncPtr) NSS_Initialize);
if (!nss_path) {
continue;
}
// Get the directory containing the nss3 library.
nsCOMPtr<nsILocalFile> nssLib(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
if (NS_SUCCEEDED(rv)) {
rv = nssLib->InitWithNativePath(nsDependentCString(nss_path));
}
PR_Free(nss_path);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIFile> file;
if (NS_SUCCEEDED(nssLib->GetParent(getter_AddRefs(file)))) {
mozFile = do_QueryInterface(file);
}
}
} else {
directoryService->Get( possible_ckbi_locations[il],
NS_GET_IID(nsILocalFile),
getter_AddRefs(mozFile));
}
if (!mozFile) {
continue;

View File

@ -194,6 +194,20 @@ TestRunner._makeIframe = function (url, retry) {
return iframe;
};
/**
* Returns the current test URL.
* We use this to tell whether the test has navigated to another test without
* being finished first.
*/
TestRunner.getLoadedTestURL = function () {
var prefix = "";
// handle mochitest-chrome URIs
if ($('testframe').contentWindow.location.protocol == "chrome:") {
prefix = "chrome://mochitests";
}
return prefix + $('testframe').contentWindow.location.pathname;
};
/**
* TestRunner entry point.
*
@ -367,9 +381,17 @@ TestRunner.testFinished = function(tests) {
}
function runNextTest() {
if (TestRunner.currentTestURL != TestRunner.getLoadedTestURL()) {
TestRunner.log("TEST-UNEXPECTED-FAIL | " +
TestRunner.currentTestURL +
" | finished in a non-clean fashion (in " +
TestRunner.getLoadedTestURL() + ")");
tests.push({ result: false });
}
var runtime = new Date().valueOf() - TestRunner._currentTestStartTime;
TestRunner.log("TEST-END | " +
TestRunner._urls[TestRunner._currentTest] +
TestRunner.currentTestURL +
" | finished in " + runtime + "ms");
TestRunner.updateUI(tests);

View File

@ -50,7 +50,6 @@
// we explicitly check something.
ok(true);
SimpleTest.finish();
]]>
</script>
</window>

View File

@ -1449,17 +1449,17 @@ var PlacesUtils = {
try {
// Create a fake faviconURI to use (FIXME: bug 523932)
let faviconURI = this._uri("fake-favicon-uri:" + aData.uri);
this.favicons.setFaviconUrlForPage(this._uri(aData.uri), faviconURI);
this.favicons.setFaviconDataFromDataURL(faviconURI, aData.icon, 0);
this.favicons.replaceFaviconDataFromDataURL(faviconURI, aData.icon, 0);
this.favicons.setAndFetchFaviconForPage(this._uri(aData.uri), faviconURI, false);
} catch (ex) {
Components.utils.reportError("Failed to import favicon data:" + ex);
}
}
if (aData.iconUri) {
try {
this.favicons.setAndLoadFaviconForPage(this._uri(aData.uri),
this._uri(aData.iconUri),
false);
this.favicons.setAndFetchFaviconForPage(this._uri(aData.uri),
this._uri(aData.iconUri),
false);
} catch (ex) {
Components.utils.reportError("Failed to import favicon URI:" + ex);
}
@ -1818,7 +1818,7 @@ var PlacesUtils = {
get folder() {
let bookmarksBackupDir = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
bookmarksBackupDir.append("bookmarkbackups");
bookmarksBackupDir.append(this.profileRelativeFolderPath);
if (!bookmarksBackupDir.exists()) {
bookmarksBackupDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
if (!bookmarksBackupDir.exists())
@ -1828,6 +1828,8 @@ var PlacesUtils = {
return this.folder = bookmarksBackupDir;
},
get profileRelativeFolderPath() "bookmarkbackups",
/**
* Cache current backups in a sorted (by date DESC) array.
*/
@ -2217,7 +2219,7 @@ XPCOMUtils.defineLazyGetter(PlacesUtils, "ghistory2", function() {
XPCOMUtils.defineLazyServiceGetter(PlacesUtils, "favicons",
"@mozilla.org/browser/favicon-service;1",
"nsIFaviconService");
"mozIAsyncFavicons");
XPCOMUtils.defineLazyServiceGetter(PlacesUtils, "bookmarks",
"@mozilla.org/browser/nav-bookmarks-service;1",

View File

@ -80,6 +80,12 @@ Form History test: form field autocomplete
<button type="submit">Submit</button>
</form>
<!-- normal, basic form (with fieldname='searchbar-history') -->
<form id="form12" onsubmit="return false;">
<input type="text" name="searchbar-history">
<button type="submit">Submit</button>
</form>
</div>
<pre id="test">
@ -121,6 +127,7 @@ fh.addEntry("field6", "value");
fh.addEntry("field7", "value");
fh.addEntry("field8", "value");
fh.addEntry("field9", "value");
fh.addEntry("searchbar-history", "blacklist test");
// All these non-implemeted types might need autocomplete tests in the future.
var todoTypes = [ "datetime", "date", "month", "week", "time", "datetime-local",
@ -720,14 +727,29 @@ function runTest(testNum) {
ok(true, "oninput should have been received");
ok(event.bubbles, "input event should bubble");
ok(event.cancelable, "input event should be cancelable");
SimpleTest.finish();
}, false);
doKey("down");
checkForm("");
doKey("return");
checkForm("value1");
return;
testNum = 599;
break;
case 600:
// check we don't show autocomplete for searchbar-history
input = $_(12, "searchbar-history");
// Trigger autocomplete popup
checkForm("");
restoreForm();
doKey("down");
break;
case 601:
checkMenuEntries([]);
SimpleTest.finish();
return;
default:
ok(false, "Unexpected invocation of test #" + testNum);

View File

@ -165,6 +165,12 @@
<button type='submit'>Submit</button>
</form>
<!-- Don't save values if the input name is 'searchbar-history' -->
<form id="form24" onsubmit="return checkSubmit(24);">
<input type='text' name='searchbar-history'>
<button type='submit'>Submit</button>
</form>
<!-- ===== Things that should be saved ===== -->
<!-- Form 100 is submitted into an iframe, not declared here. -->
@ -328,6 +334,7 @@ function startTest() {
$_(20, "test1").value = "dontSaveThis";
$_(22, "test1").value = "dontSaveThis";
$_(23, "test1").value = "dontSaveThis";
$_(24, "searchbar-history").value = "dontSaveThis";
$_(101, "test1").value = "savedValue";
$_(102, "test2").value = "savedValue";
@ -365,7 +372,7 @@ function checkSubmit(formNum) {
// Check for expected storage state.
switch (formNum) {
// Test 1-23 should not save anything.
// Test 1-24 should not save anything.
case 1:
case 2:
case 3:
@ -389,6 +396,7 @@ function checkSubmit(formNum) {
case 21:
case 22:
case 23:
case 24:
ok(!fh.hasEntries, "checking for empty storage");
break;
case 100:
@ -470,7 +478,7 @@ function checkSubmit(formNum) {
// End the test at the last form.
if (formNum == 110) {
is(numSubmittedForms, 34, "Ensuring all forms were submitted.");
is(numSubmittedForms, 35, "Ensuring all forms were submitted.");
Services.obs.removeObserver(checkObserver, "satchel-storage-changed");
SimpleTest.finish();
return false; // return false to cancel current form submission
@ -488,7 +496,7 @@ function checkSubmit(formNum) {
checkObserver.waitForChecks(function() {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var nextFormNum = formNum == 23 ? 100 : (formNum + 1);
var nextFormNum = formNum == 24 ? 100 : (formNum + 1);
// Submit the next form. Special cases are Forms 21 and 100, which happen
// from an HTTPS domain in an iframe.

View File

@ -223,7 +223,6 @@ ReflectHistogramSnapshot(JSContext *cx, JSObject *obj, Histogram *h)
h->SnapshotSample(&ss);
JSObject *counts_array;
JSObject *rarray;
jsval static_histogram = h->flags() && Histogram::kUmaTargetedHistogramFlag ? JSVAL_TRUE : JSVAL_FALSE;
const size_t count = h->bucket_count();
if (!(JS_DefineProperty(cx, obj, "min", INT_TO_JSVAL(h->declared_min()), NULL, NULL, JSPROP_ENUMERATE)
&& JS_DefineProperty(cx, obj, "max", INT_TO_JSVAL(h->declared_max()), NULL, NULL, JSPROP_ENUMERATE)
@ -330,7 +329,7 @@ mHashMutex("Telemetry::mHashMutex")
};
mTrackedDBs.Init();
for (int i = 0; i < sizeof(trackedDBs)/sizeof(const char*); i++)
for (size_t i = 0; i < ArrayLength(trackedDBs); i++)
mTrackedDBs.PutEntry(nsDependentCString(trackedDBs[i]));
#ifdef DEBUG

View File

@ -197,7 +197,7 @@
<!-- - - - - - - - - - - - - - - - - - - - - -->
<h2 class="major-section">
&aboutSupport.modifiedPrefsTitle;
&aboutSupport.modifiedKeyPrefsTitle;
</h2>
<table class="prefs-table">

View File

@ -30,7 +30,7 @@ variant of aboutSupport.show.label. This allows us to use the preferred
"Finder" terminology on Mac. -->
<!ENTITY aboutSupport.showMac.label "Show in Finder">
<!ENTITY aboutSupport.modifiedPrefsTitle "Modified Preferences">
<!ENTITY aboutSupport.modifiedKeyPrefsTitle "Important Modified Preferences">
<!ENTITY aboutSupport.modifiedPrefsName "Name">
<!ENTITY aboutSupport.modifiedPrefsValue "Value">

Some files were not shown because too many files have changed in this diff Show More