mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Merge m-c to m-i.
This commit is contained in:
commit
096afeb104
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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}
|
||||
|
@ -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);
|
||||
|
404
browser/components/migration/src/FirefoxProfileMigrator.js
Normal file
404
browser/components/migration/src/FirefoxProfileMigrator.js
Normal 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]);
|
@ -65,6 +65,7 @@ endif
|
||||
|
||||
EXTRA_PP_COMPONENTS = \
|
||||
ChromeProfileMigrator.js \
|
||||
FirefoxProfileMigrator.js \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_COMPONENTS = \
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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">
|
||||
|
||||
|
@ -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
|
||||
|
@ -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@
|
||||
|
27
configure.in
27
configure.in
@ -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
|
||||
|
||||
|
@ -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()->
|
||||
|
@ -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;
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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>",
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
{
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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()) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
*/
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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
237
gfx/2d/HelpersCairo.h
Normal 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_ */
|
@ -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
363
gfx/2d/PathCairo.cpp
Normal 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
194
gfx/2d/PathCairo.h
Normal 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_ */
|
97
gfx/2d/ScaledFontCairo.cpp
Normal file
97
gfx/2d/ScaledFontCairo.cpp
Normal 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
67
gfx/2d/ScaledFontCairo.h
Normal 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_ */
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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 };
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <io.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#if defined(ANDROID)
|
||||
#if defined(ANDROID) || defined(OS_POSIX)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
|
1
js/src/jit-test/tests/basic/bug716713.js
Normal file
1
js/src/jit-test/tests/basic/bug716713.js
Normal file
@ -0,0 +1 @@
|
||||
uneval(function() { @o() })
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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 = \
|
||||
|
@ -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\'t have the device with me…</a>'>
|
||||
<!ENTITY sync.link.show.label 'Show me how.'>
|
||||
<!ENTITY sync.link.nodevice.label 'I don\'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;'>
|
||||
|
259
mobile/android/base/ui/Axis.java
Normal file
259
mobile/android/base/ui/Axis.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
133
mobile/android/base/ui/SubdocumentScrollHelper.java
Normal file
133
mobile/android/base/ui/SubdocumentScrollHelper.java
Normal 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
22
mozglue/linker/Logging.h
Normal 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 */
|
21
mozglue/linker/Makefile.in
Normal file
21
mozglue/linker/Makefile.in
Normal 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
93
mozglue/linker/Utils.h
Normal 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
180
mozglue/linker/Zip.cpp
Normal 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
311
mozglue/linker/Zip.h
Normal 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
33
mozglue/tests/Makefile.in
Normal 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
65
mozglue/tests/TestZip.cpp
Normal 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;
|
||||
}
|
BIN
mozglue/tests/no_central_dir.zip
Normal file
BIN
mozglue/tests/no_central_dir.zip
Normal file
Binary file not shown.
BIN
mozglue/tests/test.zip
Normal file
BIN
mozglue/tests/test.zip
Normal file
Binary file not shown.
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -50,7 +50,6 @@
|
||||
// we explicitly check something.
|
||||
ok(true);
|
||||
|
||||
SimpleTest.finish();
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -197,7 +197,7 @@
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - -->
|
||||
|
||||
<h2 class="major-section">
|
||||
&aboutSupport.modifiedPrefsTitle;
|
||||
&aboutSupport.modifiedKeyPrefsTitle;
|
||||
</h2>
|
||||
|
||||
<table class="prefs-table">
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user