Merge last PGO-green changeset of mozilla-inbound to mozilla-central

This commit is contained in:
Ed Morley 2012-12-05 22:10:26 +00:00
Родитель 2023fa6683 1d8ec56f57
Коммит aac68a2eb6
38 изменённых файлов: 459 добавлений и 521 удалений

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

@ -16,7 +16,6 @@ var gPrevCharset = null;
var gProxyFavIcon = null;
var gLastValidURLStr = "";
var gInPrintPreviewMode = false;
var gDownloadMgr = null;
var gContextMenu = null; // nsContextMenu instance
var gStartupRan = false;
@ -1339,8 +1338,7 @@ var gBrowserInit = {
// If the user manually opens the download manager before the timeout, the
// downloads will start right away, and getting the service again won't hurt.
setTimeout(function() {
gDownloadMgr = Cc["@mozilla.org/download-manager;1"].
getService(Ci.nsIDownloadManager);
Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
#ifdef XP_WIN
if (Win7Features) {
@ -6871,140 +6869,6 @@ var gIdentityHandler = {
}
};
let DownloadMonitorPanel = {
//////////////////////////////////////////////////////////////////////////////
//// DownloadMonitorPanel Member Variables
_panel: null,
_activeStr: null,
_pausedStr: null,
_lastTime: Infinity,
_listening: false,
get DownloadUtils() {
delete this.DownloadUtils;
Cu.import("resource://gre/modules/DownloadUtils.jsm", this);
return this.DownloadUtils;
},
//////////////////////////////////////////////////////////////////////////////
//// DownloadMonitorPanel Public Methods
/**
* Initialize the status panel and member variables
*/
init: function DMP_init() {
// Initialize "private" member variables
this._panel = document.getElementById("download-monitor");
// Cache the status strings
this._activeStr = gNavigatorBundle.getString("activeDownloads1");
this._pausedStr = gNavigatorBundle.getString("pausedDownloads1");
gDownloadMgr.addListener(this);
this._listening = true;
this.updateStatus();
},
uninit: function DMP_uninit() {
if (this._listening)
gDownloadMgr.removeListener(this);
},
inited: function DMP_inited() {
return this._panel != null;
},
/**
* Update status based on the number of active and paused downloads
*/
updateStatus: function DMP_updateStatus() {
if (!this.inited())
return;
let numActive = gDownloadMgr.activeDownloadCount;
// Hide the panel and reset the "last time" if there's no downloads
if (numActive == 0) {
this._panel.hidden = true;
this._lastTime = Infinity;
return;
}
// Find the download with the longest remaining time
let numPaused = 0;
let maxTime = -Infinity;
let dls = gDownloadMgr.activeDownloads;
while (dls.hasMoreElements()) {
let dl = dls.getNext();
if (dl.state == gDownloadMgr.DOWNLOAD_DOWNLOADING) {
// Figure out if this download takes longer
if (dl.speed > 0 && dl.size > 0)
maxTime = Math.max(maxTime, (dl.size - dl.amountTransferred) / dl.speed);
else
maxTime = -1;
}
else if (dl.state == gDownloadMgr.DOWNLOAD_PAUSED)
numPaused++;
}
// Get the remaining time string and last sec for time estimation
let timeLeft;
[timeLeft, this._lastTime] =
this.DownloadUtils.getTimeLeft(maxTime, this._lastTime);
// Figure out how many downloads are currently downloading
let numDls = numActive - numPaused;
let status = this._activeStr;
// If all downloads are paused, show the paused message instead
if (numDls == 0) {
numDls = numPaused;
status = this._pausedStr;
}
// Get the correct plural form and insert the number of downloads and time
// left message if necessary
status = PluralForm.get(numDls, status);
status = status.replace("#1", numDls);
status = status.replace("#2", timeLeft);
// Update the panel and show it
this._panel.label = status;
this._panel.hidden = false;
},
//////////////////////////////////////////////////////////////////////////////
//// nsIDownloadProgressListener
/**
* Update status for download progress changes
*/
onProgressChange: function() {
this.updateStatus();
},
/**
* Update status for download state changes
*/
onDownloadStateChange: function() {
this.updateStatus();
},
onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus, aDownload) {
},
onSecurityChange: function(aWebProgress, aRequest, aState, aDownload) {
},
//////////////////////////////////////////////////////////////////////////////
//// nsISupports
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadProgressListener]),
};
function getNotificationBox(aWindow) {
var foundBrowser = gBrowser.getBrowserForDocument(aWindow.document);
if (foundBrowser)

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

@ -24,10 +24,8 @@ MOCHITEST_BROWSER_FILES = \
browser_privatebrowsing_searchbar.js \
browser_privatebrowsing_sslsite_transition.js \
$(filter disabled-since-it-no-longer-makes-sense, browser_privatebrowsing_transition.js) \
$(filter disabled--bug-564934, browser_privatebrowsing_downloadmonitor.js) \
browser_privatebrowsing_urlbarundo.js \
browser_privatebrowsing_viewsource.js \
staller.sjs \
$(NULL)
# Turn off private browsing tests that perma-timeout on Linux.

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

@ -1,153 +0,0 @@
/* 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/. */
// This test makes sure that the download monitor status bar panel is correctly
// cleared when switching the private browsing mode on or off.
function test() {
// initialization
let pb = Cc["@mozilla.org/privatebrowsing;1"].
getService(Ci.nsIPrivateBrowsingService);
let dm = Cc["@mozilla.org/download-manager;1"].
getService(Ci.nsIDownloadManager);
if (!gDownloadMgr)
gDownloadMgr = dm;
let panel = document.getElementById("download-monitor");
waitForExplicitFinish();
let acceptDialog = 0;
let confirmCalls = 0;
function promptObserver(aSubject, aTopic, aData) {
let dialogWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
confirmCalls++;
if (acceptDialog-- > 0)
dialogWin.document.documentElement.getButton("accept").click();
}
Services.obs.addObserver(promptObserver, "common-dialog-loaded", false);
// Add a new download
let [file, persist] = addDownload(dm, {
isPrivate: PrivateBrowsingUtils.isWindowPrivate(window),
resultFileName: "pbtest-1",
downloadName: "PB Test 1"
});
// Make sure that the download is being displayed in the monitor panel
if (!DownloadMonitorPanel.inited())
DownloadMonitorPanel.init();
else
DownloadMonitorPanel.updateStatus();
ok(!panel.hidden, "The download panel should be successfully added initially");
// Enter the private browsing mode
acceptDialog = 1;
pb.privateBrowsingEnabled = true;
is(confirmCalls, 1, "One prompt was accepted");
ok(pb.privateBrowsingEnabled, "The private browsing transition was successful");
executeSoon(function () {
ok(panel.hidden, "The download panel should be hidden when entering the private browsing mode");
// Add a new download
let [file2, persist2] = addDownload(dm, {
isPrivate: PrivateBrowsingUtils.isWindowPrivate(window),
resultFileName: "pbtest-2",
downloadName: "PB Test 2"
});
// Update the panel
DownloadMonitorPanel.updateStatus();
// Make sure that the panel is visible
ok(!panel.hidden, "The download panel should show up when a new download is added");
// Exit the private browsing mode
acceptDialog = 1;
pb.privateBrowsingEnabled = false;
is(confirmCalls, 2, "One prompt was accepted");
ok(!pb.privateBrowsingEnabled, "The private browsing transition was successful");
executeSoon(function () {
ok(panel.hidden, "The download panel should be hidden when leaving the private browsing mode");
// cleanup
let dls = dm.activeDownloads;
while (dls.hasMoreElements()) {
let dl = dls.getNext().QueryInterface(Ci.nsIDownload);
dm.removeDownload(dl.id);
let file = dl.targetFile;
if (file.exists())
file.remove(false);
}
if (file.exists())
file.remove(false);
Services.obs.removeObserver(promptObserver, "common-dialog-loaded", false);
finish();
});
});
}
/**
* Adds a download to the DM, and starts it.
* (Copied from toolkit/componentns/downloads/test/unit/head_download_manager.js)
* @param aParams (optional): an optional object which contains the function
* parameters:
* resultFileName: leaf node for the target file
* targetFile: nsIFile for the target (overrides resultFileName)
* sourceURI: the download source URI
* downloadName: the display name of the download
* runBeforeStart: a function to run before starting the download
* isPrivate: whether the download is private
*/
function addDownload(dm, aParams)
{
if (!aParams)
aParams = {};
if (!("resultFileName" in aParams))
aParams.resultFileName = "download.result";
if (!("targetFile" in aParams)) {
let dirSvc = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
aParams.targetFile = dirSvc.get("ProfD", Ci.nsIFile);
aParams.targetFile.append(aParams.resultFileName);
}
if (!("sourceURI" in aParams))
aParams.sourceURI = "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/global/staller.sjs";
if (!("downloadName" in aParams))
aParams.downloadName = null;
if (!("runBeforeStart" in aParams))
aParams.runBeforeStart = function () {};
const nsIWBP = Ci.nsIWebBrowserPersist;
let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
.createInstance(Ci.nsIWebBrowserPersist);
persist.persistFlags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
let dl = dm.addDownload(Ci.nsIDownloadManager.DOWNLOAD_TYPE_DOWNLOAD,
createURI(aParams.sourceURI),
createURI(aParams.targetFile), aParams.downloadName, null,
Math.round(Date.now() * 1000), null, persist, aParams.isPrivate);
// This will throw if it isn't found, and that would mean test failure, so no
// try catch block
let test = dm.getDownload(dl.id);
aParams.runBeforeStart.call(undefined, dl);
persist.progressListener = dl.QueryInterface(Ci.nsIWebProgressListener);
persist.savePrivacyAwareURI(dl.source, null, null, null, null, dl.targetFile,
aParams.isPrivate);
return [dl.targetFile, persist];
}
function createURI(aObj) {
let ios = Services.io;
return (aObj instanceof Ci.nsIFile) ? ios.newFileURI(aObj) :
ios.newURI(aObj, null, null);
}

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

@ -1,27 +0,0 @@
/* 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/. */
// This provides the tests with a download URL which never finishes.
var timer;
function handleRequest(request, response) {
response.setStatusLine(request.httpVersion, 200, "OK");
response.processAsync();
const nsITimer = Components.interfaces.nsITimer;
function stall() {
timer = null;
// This write will throw if the connection has been closed by the browser.
response.write("stalling...\n");
timer = Components.classes["@mozilla.org/timer;1"]
.createInstance(nsITimer);
timer.initWithCallback(stall, 500, nsITimer.TYPE_ONE_SHOT);
}
response.setHeader("Content-Type", "text/plain", false);
response.setHeader("Accept-Ranges", "none", false);
stall();
}

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

@ -18,7 +18,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "CommandUtils",
// DO NOT put Require.jsm or gcli.jsm into lazy getters as this breaks the
// requisition import a few lines down.
Cu.import("resource://gre/modules/devtools/gcli.jsm");
Cu.import("resource:///modules/devtools/gcli.jsm");
Cu.import("resource://gre/modules/devtools/Require.jsm");
let Requisition = require('gcli/cli').Requisition;

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

@ -5,7 +5,7 @@
let tempScope = {};
Components.utils.import("resource:///modules/devtools/Target.jsm", tempScope);
let TargetFactory = tempScope.TargetFactory;
Components.utils.import("resource:///modules/devtools/Console.jsm", tempScope);
Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
let console = tempScope.console;
/**

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

@ -13,7 +13,7 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/devtools/EventEmitter.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MarkupView",
"resource://gre/modules/devtools/MarkupView.jsm");
"resource:///modules/devtools/MarkupView.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Selection",
"resource:///modules/devtools/Selection.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "HTMLBreadcrumbs",

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

@ -586,8 +586,6 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY identity.moreInfoLinkText "More Information…">
<!ENTITY downloadMonitor2.tooltip "Click to open downloads window">
<!ENTITY allTabs.filter.emptyText "Search Tabs">
<!-- Name for the tabs toolbar as spoken by screen readers.
The word "toolbar" is appended automatically and should not be contained below! -->

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

@ -232,14 +232,6 @@ identity.unknown.tooltip=This website does not supply identity information.
identity.ownerUnknown2=(unknown)
# Downloads Monitor Panel
# LOCALIZATION NOTE (activeDownloads1, pausedDownloads1): Semi-colon list of plural
# forms. See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 number of downloads; #2 time left
# examples: 1 active download (2 minutes remaining); 11 paused downloads
activeDownloads1=1 active download (#2);#1 active downloads (#2)
pausedDownloads1=1 paused download;#1 paused downloads
# Edit Bookmark UI
editBookmarkPanel.pageBookmarkedTitle=Page Bookmarked
editBookmarkPanel.pageBookmarkedDescription=%S will always remember this page for you.

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

@ -1791,12 +1791,6 @@ toolbar[mode="text"] toolbarbutton.chevron > .toolbarbutton-icon {
display: -moz-box; /* display chevron icon in text mode */
}
#download-monitor {
list-style-image: url("chrome://browser/skin/Toolbar-small.png");
-moz-image-region: rect(0px 16px 16px 0px);
}
/* ::::: Keyboard UI Panel ::::: */
.KUI-panel-closebutton {

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

@ -3130,10 +3130,6 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
@hudButtonPressed@
}
#download-monitor {
list-style-image: url("chrome://mozapps/skin/downloads/downloadStatusIcon.png");
}
/* ::::: Keyboard UI Panel ::::: */
.KUI-panel {

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

@ -2408,11 +2408,6 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
min-width: 280px;
}
#download-monitor {
list-style-image: url("chrome://browser/skin/Toolbar.png");
-moz-image-region: rect(0, 108px, 18px, 90px);
}
/* Bookmarks roots menu-items */
#appmenu_subscribeToPage:not([disabled]),
#appmenu_subscribeToPageMenu,

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

@ -5284,6 +5284,7 @@ dnl enable once PeerConnection lands
fi
AC_SUBST(MOZ_WEBRTC)
AC_SUBST(MOZ_WEBRTC_LEAKING_TESTS)
AC_SUBST(MOZ_WEBRTC_SIGNALING)
AC_SUBST(MOZ_PEERCONNECTION)
AC_SUBST(MOZ_WEBRTC_IN_LIBXUL)

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

@ -222,7 +222,8 @@ static uint32_t CountNewlinesInNativeLength(nsIContent* aContent,
}
#endif
static uint32_t GetNativeTextLength(nsIContent* aContent, uint32_t aMaxLength = UINT32_MAX)
/* static */ uint32_t
nsContentEventHandler::GetNativeTextLength(nsIContent* aContent, uint32_t aMaxLength)
{
if (aContent->IsNodeOfType(nsINode::eTEXT)) {
uint32_t textLengthDifference =
@ -369,7 +370,7 @@ nsContentEventHandler::SetRangeFromFlatTextOffset(
uint32_t aNativeLength,
bool aExpandToClusterBoundaries)
{
nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
nsresult rv = iter->Init(mRootContent);
NS_ENSURE_SUCCESS(rv, rv);

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

@ -80,6 +80,9 @@ public:
static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent,
nsRange* aRange,
uint32_t* aOffset);
// Get the native text length of a content node excluding any children
static uint32_t GetNativeTextLength(nsIContent* aContent,
uint32_t aMaxLength = UINT32_MAX);
protected:
// Make the DOM range from the offset of FlatText and the text length.
// If aExpandToClusterBoundaries is true, the start offset and the end one are

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

@ -62,6 +62,8 @@ public:
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
void Init(nsIWidget* aWidget,
nsPresContext* aPresContext,
@ -79,6 +81,7 @@ private:
void ObserveEditableNode();
bool mObserving;
uint32_t mPreAttrChangeLength;
};
/******************************************************************/
@ -976,6 +979,50 @@ nsTextStateManager::ContentRemoved(nsIDocument* aDocument,
new TextChangeEvent(this, offset, offset + childOffset, offset));
}
static nsIContent*
GetContentBR(mozilla::dom::Element *aElement) {
if (!aElement->IsNodeOfType(nsINode::eCONTENT)) {
return nullptr;
}
nsIContent *content = static_cast<nsIContent*>(aElement);
return content->IsHTML(nsGkAtoms::br) ? content : nullptr;
}
void
nsTextStateManager::AttributeWillChange(nsIDocument* aDocument,
mozilla::dom::Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType)
{
nsIContent *content = GetContentBR(aElement);
mPreAttrChangeLength = content ?
nsContentEventHandler::GetNativeTextLength(content) : 0;
}
void
nsTextStateManager::AttributeChanged(nsIDocument* aDocument,
mozilla::dom::Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType)
{
nsIContent *content = GetContentBR(aElement);
if (!content) {
return;
}
uint32_t postAttrChangeLength =
nsContentEventHandler::GetNativeTextLength(content);
if (postAttrChangeLength != mPreAttrChangeLength) {
uint32_t start;
if (NS_SUCCEEDED(nsContentEventHandler::GetFlatTextOffsetOfRange(
mRootContent, content, 0, &start))) {
nsContentUtils::AddScriptRunner(new TextChangeEvent(this, start,
start + mPreAttrChangeLength, start + postAttrChangeLength));
}
}
}
bool
nsIMEStateManager::IsEditable(nsINode* node)
{

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

@ -43,7 +43,7 @@ MOCHITEST_FILES = test_bug1682.html \
test_viewport.html \
test_documentAll.html \
test_document-element-inserted.html \
test_bug445004.html \
$(filter disabled-temporarily--bug-559932, test_bug445004.html) \
bug445004-inner.js \
bug445004-outer-rel.html \
bug445004-outer-abs.html \

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

@ -1087,7 +1087,7 @@ nsSVGSVGElement::GetCurrentViewElement() const
nsIDocument* doc = GetCurrentDoc();
if (doc) {
Element *element = doc->GetElementById(*mCurrentViewID);
if (element && element->Tag() == nsGkAtoms::view) {
if (element && element->IsSVG(nsGkAtoms::view)) {
return static_cast<nsSVGViewElement*>(element);
}
}

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

@ -44,6 +44,11 @@ XPCOMUtils.defineLazyGetter(this, "msgmgr", function() {
.getService(Ci.nsISystemMessagesInternal);
});
XPCOMUtils.defineLazyGetter(this, "updateSvc", function() {
return Cc["@mozilla.org/offlinecacheupdate-service;1"]
.getService(Ci.nsIOfflineCacheUpdateService);
});
#ifdef MOZ_WIDGET_GONK
const DIRECTORY_NAME = "webappsDir";
#elifdef ANDROID
@ -931,8 +936,6 @@ this.DOMApplicationRegistry = {
// if the manifest has an appcache_path property, use it to populate the appcache
if (aManifest.appcache_path) {
let appcacheURI = Services.io.newURI(aManifest.fullAppcachePath(), null, null);
let updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"]
.getService(Ci.nsIOfflineCacheUpdateService);
let docURI = Services.io.newURI(aManifest.fullLaunchPath(), null, null);
// We determine the app's 'installState' according to its previous
// state. Cancelled download should remain as 'pending'. Successfully
@ -943,8 +946,9 @@ this.DOMApplicationRegistry = {
// We set the 'downloading' flag right before starting the app
// download/update.
aApp.downloading = true;
let cacheUpdate = aProfileDir ? updateService.scheduleCustomProfileUpdate(appcacheURI, docURI, aProfileDir)
: updateService.scheduleAppUpdate(appcacheURI, docURI, aApp.localId, false);
let cacheUpdate = aProfileDir
? updateSvc.scheduleCustomProfileUpdate(appcacheURI, docURI, aProfileDir)
: updateSvc.scheduleAppUpdate(appcacheURI, docURI, aApp.localId, false);
cacheUpdate.addObserver(new AppcacheObserver(aApp), false);
if (aOfflineCacheObserver) {
cacheUpdate.addObserver(aOfflineCacheObserver, false);
@ -1038,9 +1042,23 @@ this.DOMApplicationRegistry = {
this._saveApps(function() {
aData.app = app;
aData.event = manifest.appcache_path ? "downloadavailable"
: "downloadapplied";
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData);
if (!manifest.appcache_path) {
aData.event = "downloadapplied";
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData);
} else {
// Check if the appcache is updatable, and send "downloadavailable" or
// "downloadapplied".
let updateObserver = {
observe: function(aSubject, aTopic, aData) {
aData.event =
aTopic == "offline-cache-update-available" ? "downloadavailable"
: "downloadapplied";
aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData);
}
}
updateSvc.checkForUpdate(Services.io.newURI(aData.manifestURL, null, null),
app.localId, false, updateObserver);
}
});
// Update the permissions for this app.

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

@ -10,14 +10,20 @@ relativesrcdir = @relativesrcdir@
include $(DEPTH)/config/autoconf.mk
# TODO: When bug 814718 and bug 814721 are fixed, add the following files:
# test_getUserMedia_basicVideo.html
# test_getUserMedia_basicAudio.thml
# test_getUserMedia_basicVideoAudio.html
# mediaStreamPlayback.js
MOCHITEST_FILES = \
test_getUserMedia_exceptions.html \
head.js \
$(NULL)
# The following tests are leaking and cannot be run by default yet
ifdef MOZ_WEBRTC_LEAKING_TESTS
MOCHITEST_FILES += \
# Bug 814718 and bug 818466 prevent us from running the following tests:
# test_getUserMedia_basicVideo.html \
# test_getUserMedia_basicAudio.html \
# test_getUserMedia_basicVideoAudio.html \
# mediaStreamPlayback.js \
$(NULL)
endif
include $(topsrcdir)/config/rules.mk

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

@ -1417,6 +1417,17 @@ RadioInterfaceLayer.prototype = {
timestamp,
true);
gSystemMessenger.broadcastMessage("sms-sent",
{id: id,
delivery: DOM_SMS_DELIVERY_SENT,
deliveryStatus: RIL.GECKO_SMS_DELIVERY_STATUS_PENDING,
sender: message.sender || null,
receiver: options.number,
body: options.fullBody,
messageClass: messageClass,
timestamp: timestamp,
read: true});
if (!options.requestStatusReport) {
// No more used if STATUS-REPORT not requested.
delete this._sentSmsEnvelopes[message.envelopeId];

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

@ -135,9 +135,11 @@ bool ImageContainerChild::CopyDataIntoSharedImage(Image* src, SharedImage* dest)
ShmemYCbCrImage shmemImage(yuv.data(), yuv.offset());
MOZ_ASSERT(data->mCbSkip == data->mCrSkip);
if (!shmemImage.CopyData(data->mYChannel, data->mCbChannel, data->mCrChannel,
data->mYSize, data->mYStride,
data->mCbCrSize, data->mCbCrStride)) {
data->mCbCrSize, data->mCbCrStride,
data->mYSkip, data->mCbSkip)) {
NS_WARNING("Failed to copy image data!");
return false;
}
@ -515,10 +517,11 @@ public:
mSize = mData.mPicSize;
ShmemYCbCrImage shmImg(mShmem);
MOZ_ASSERT(aData.mCbSkip == aData.mCrSkip);
if (!shmImg.CopyData(aData.mYChannel, aData.mCbChannel, aData.mCrChannel,
aData.mYSize, aData.mYStride,
aData.mCbCrSize, aData.mCbCrStride)) {
aData.mCbCrSize, aData.mCbCrStride,
aData.mYSkip, aData.mCbSkip)) {
NS_WARNING("Failed to copy image data!");
}
mData.mYChannel = shmImg.GetYData();

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

@ -150,25 +150,54 @@ bool ShmemYCbCrImage::IsValid()
return true;
}
bool ShmemYCbCrImage::CopyData(uint8_t* aYData, uint8_t* aCbData, uint8_t* aCrData,
static void CopyLineWithSkip(const uint8_t* src, uint8_t* dst, uint32_t len, uint32_t skip) {
for (uint32_t i = 0; i < len; ++i) {
*dst = *src;
src += 1 + skip;
++dst;
}
}
bool ShmemYCbCrImage::CopyData(const uint8_t* aYData,
const uint8_t* aCbData, const uint8_t* aCrData,
gfxIntSize aYSize, uint32_t aYStride,
gfxIntSize aCbCrSize, uint32_t aCbCrStride)
gfxIntSize aCbCrSize, uint32_t aCbCrStride,
uint32_t aYSkip, uint32_t aCbCrSkip)
{
if (!IsValid() || GetYSize() != aYSize || GetCbCrSize() != aCbCrSize) {
return false;
}
for (int i = 0; i < aYSize.height; i++) {
memcpy(GetYData() + i * GetYStride(),
aYData + i * aYStride,
aYSize.width);
for (int i = 0; i < aYSize.height; ++i) {
if (aYSkip == 0) {
// fast path
memcpy(GetYData() + i * GetYStride(),
aYData + i * aYStride,
aYSize.width);
} else {
// slower path
CopyLineWithSkip(aYData + i * aYStride,
GetYData() + i * GetYStride(),
aYSize.width, aYSkip);
}
}
for (int i = 0; i < aCbCrSize.height; i++) {
memcpy(GetCbData() + i * GetCbCrStride(),
aCbData + i * aCbCrStride,
aCbCrSize.width);
memcpy(GetCrData() + i * GetCbCrStride(),
aCrData + i * aCbCrStride,
aCbCrSize.width);
for (int i = 0; i < aCbCrSize.height; ++i) {
if (aCbCrSkip == 0) {
// fast path
memcpy(GetCbData() + i * GetCbCrStride(),
aCbData + i * aCbCrStride,
aCbCrSize.width);
memcpy(GetCrData() + i * GetCbCrStride(),
aCrData + i * aCbCrStride,
aCbCrSize.width);
} else {
// slower path
CopyLineWithSkip(aCbData + i * aCbCrStride,
GetCbData() + i * GetCbCrStride(),
aCbCrSize.width, aCbCrSkip);
CopyLineWithSkip(aCrData + i * aCbCrStride,
GetCrData() + i * GetCbCrStride(),
aCbCrSize.width, aCbCrSkip);
}
}
return true;
}

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

@ -96,9 +96,11 @@ public:
/**
* Copies the data passed in parameter into the shmem.
*/
bool CopyData(uint8_t* aYData, uint8_t* aCbData, uint8_t* aCrData,
bool CopyData(const uint8_t* aYData,
const uint8_t* aCbData, const uint8_t* aCrData,
gfxIntSize aYSize, uint32_t aYStride,
gfxIntSize aCbCrSize, uint32_t aCbCrStride);
gfxIntSize aCbCrSize, uint32_t aCbCrStride,
uint32_t aYSkip, uint32_t aCbCrSkip);
private:
bool Open(Shmem& aShmem, size_t aOffset = 0);

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

@ -1183,7 +1183,7 @@ SequentialCompileContext::compile(IonBuilder *builder, MIRGraph *graph,
// incremental read barriers.
if (js_IonOptions.parallelCompilation &&
OffThreadCompilationAvailable(cx) &&
!IsIncrementalGCInProgress(cx->runtime))
cx->runtime->gcIncrementalState == gc::NO_INCREMENTAL)
{
builder->script()->ion = ION_COMPILING_SCRIPT;

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

@ -107,7 +107,7 @@ SVGViewFrame::AttributeChanged(int32_t aNameSpaceID,
aAttribute == nsGkAtoms::viewTarget)) {
nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(this);
NS_ASSERTION(outerSVGFrame->GetContent()->Tag() == nsGkAtoms::svg,
NS_ASSERTION(outerSVGFrame->GetContent()->IsSVG(nsGkAtoms::svg),
"Expecting an <svg> element");
nsSVGSVGElement* svgElement =

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

@ -364,13 +364,13 @@ public class AwesomeBar extends GeckoActivity {
private void cancelAndFinish() {
setResult(Activity.RESULT_CANCELED);
finish();
overridePendingTransition(0, 0);
overridePendingTransition(R.anim.awesomebar_hold_still, R.anim.awesomebar_fade_out);
}
private void finishWithResult(Intent intent) {
setResult(Activity.RESULT_OK, intent);
finish();
overridePendingTransition(0, 0);
overridePendingTransition(R.anim.awesomebar_hold_still, R.anim.awesomebar_fade_out);
}
private void openUrlAndFinish(String url) {

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

@ -12,6 +12,7 @@ import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Selection;
import android.text.TextUtils;
import android.text.style.UnderlineSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.BackgroundColorSpan;
@ -71,6 +72,7 @@ final class GeckoEditable
private InputFilter[] mFilters;
private final SpannableStringBuilder mText;
private final SpannableStringBuilder mChangedText;
private final Editable mProxy;
private GeckoEditableListener mListener;
private final ActionQueue mActionQueue;
@ -244,6 +246,7 @@ final class GeckoEditable
mUpdateGecko = true;
mText = new SpannableStringBuilder();
mChangedText = new SpannableStringBuilder();
final Class<?>[] PROXY_INTERFACES = { Editable.class };
mProxy = (Editable)Proxy.newProxyInstance(
@ -531,6 +534,15 @@ final class GeckoEditable
}
final int seqnoWhenPosted = ++mGeckoUpdateSeqno;
/* An event (keypress, etc.) has potentially changed the selection,
synchronize the selection here. There is not a race with the UI thread
because the UI thread should be blocked on the event action */
if (!mActionQueue.isEmpty() &&
mActionQueue.peek().mType == Action.TYPE_EVENT) {
Selection.setSelection(mText, start, end);
return;
}
geckoPostToUI(new Runnable() {
public void run() {
mActionQueue.syncWithGecko();
@ -551,6 +563,13 @@ final class GeckoEditable
});
}
private void geckoReplaceText(int start, int oldEnd, CharSequence newText) {
// Don't use replace() because Gingerbread has a bug where if the replaced text
// has the same spans as the original text, the spans will end up being deleted
mText.delete(start, oldEnd);
mText.insert(start, newText);
}
@Override
public void onTextChange(final String text, final int start,
final int unboundedOldEnd, final int unboundedNewEnd) {
@ -565,24 +584,61 @@ final class GeckoEditable
number to denote "end of the text". Fix that here */
final int oldEnd = unboundedOldEnd > mText.length() ? mText.length() : unboundedOldEnd;
// new end should always match text
if (unboundedNewEnd < (start + text.length())) {
if (start != 0 && unboundedNewEnd != (start + text.length())) {
throw new IllegalArgumentException("newEnd does not match text");
}
final int newEnd = start + text.length();
/* Text changes affect the selection as well, and we may not receive another selection
update as a result of selection notification masking on the Gecko side; therefore,
in order to prevent previous stale selection notifications from occurring, we need
to increment the seqno here as well */
++mGeckoUpdateSeqno;
mChangedText.clearSpans();
mChangedText.replace(0, mChangedText.length(), text);
// Preserve as many spans as possible
TextUtils.copySpansFrom(mText, start, Math.min(oldEnd, newEnd),
Object.class, mChangedText, 0);
if (!mActionQueue.isEmpty()) {
final Action action = mActionQueue.peek();
if (action.mType == Action.TYPE_REPLACE_TEXT &&
action.mStart == start &&
text.equals(action.mSequence.toString())) {
// Replace using saved text to preserve spans
mText.replace(start, oldEnd, action.mSequence,
0, action.mSequence.length());
start <= action.mStart &&
action.mStart + action.mSequence.length() <= newEnd) {
// actionNewEnd is the new end of the original replacement action
final int actionNewEnd = action.mStart + action.mSequence.length();
int selStart = Selection.getSelectionStart(mText);
int selEnd = Selection.getSelectionEnd(mText);
// Replace old spans with new spans
mChangedText.replace(action.mStart - start, actionNewEnd - start,
action.mSequence);
geckoReplaceText(start, oldEnd, mChangedText);
// delete/insert above might have moved our selection to somewhere else
// this happens when the Gecko text change covers a larger range than
// the original replacement action. Fix selection here
if (selStart >= start && selStart <= oldEnd) {
selStart = selStart < action.mStart ? selStart :
selStart < action.mEnd ? actionNewEnd :
selStart + actionNewEnd - action.mEnd;
mText.setSpan(Selection.SELECTION_START, selStart, selStart,
Spanned.SPAN_POINT_POINT);
}
if (selEnd >= start && selEnd <= oldEnd) {
selEnd = selEnd < action.mStart ? selEnd :
selEnd < action.mEnd ? actionNewEnd :
selEnd + actionNewEnd - action.mEnd;
mText.setSpan(Selection.SELECTION_END, selEnd, selEnd,
Spanned.SPAN_POINT_POINT);
}
} else {
mText.replace(start, oldEnd, text, 0, text.length());
geckoReplaceText(start, oldEnd, mChangedText);
}
} else {
mText.replace(start, oldEnd, text, 0, text.length());
geckoReplaceText(start, oldEnd, mChangedText);
}
geckoPostToUI(new Runnable() {
public void run() {

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

@ -29,8 +29,6 @@ import android.view.inputmethod.InputMethodManager;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Timer;
import java.util.TimerTask;
class GeckoInputConnection
extends BaseInputConnection
@ -41,8 +39,6 @@ class GeckoInputConnection
private static final int INLINE_IME_MIN_DISPLAY_SIZE = 480;
private static final Timer mIMETimer = new Timer("GeckoInputConnection Timer");
private static int mIMEState;
private static String mIMETypeHint = "";
private static String mIMEModeHint = "";
@ -172,12 +168,6 @@ class GeckoInputConnection
return extract;
}
private static void postToUiThread(Runnable runnable) {
// postToUiThread() is called by the Gecko and TimerTask threads.
// The UI thread does not need to post Runnables to itself.
GeckoApp.mAppContext.mMainHandler.post(runnable);
}
private static View getView() {
return GeckoApp.mAppContext.getLayerView();
}
@ -191,6 +181,35 @@ class GeckoInputConnection
return InputMethods.getInputMethodManager(context);
}
private static void showSoftInput() {
final InputMethodManager imm = getInputMethodManager();
if (imm != null) {
final View v = getView();
imm.showSoftInput(v, 0);
}
}
private static void hideSoftInput() {
final InputMethodManager imm = getInputMethodManager();
if (imm != null) {
final View v = getView();
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
private void restartInput() {
final InputMethodManager imm = getInputMethodManager();
if (imm != null) {
final View v = getView();
final Editable editable = getEditable();
// Fake a selection change, so that when we restart the input,
// the IME will make sure that any old composition string is cleared
notifySelectionChange(Selection.getSelectionStart(editable),
Selection.getSelectionEnd(editable));
imm.restartInput(v);
}
}
public void onTextChange(String text, int start, int oldEnd, int newEnd) {
if (mUpdateRequest == null) {
@ -251,17 +270,6 @@ class GeckoInputConnection
getComposingSpanEnd(editable));
}
protected void resetCompositionState() {
if (mBatchEditCount > 0) {
Log.d(LOGTAG, "resetCompositionState: resetting mBatchEditCount "
+ mBatchEditCount + " -> 0");
mBatchEditCount = 0;
}
removeComposingSpans(getEditable());
mUpdateRequest = null;
}
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE;
@ -342,7 +350,9 @@ class GeckoInputConnection
}
}
resetCompositionState();
// We don't know the selection
outAttrs.initialSelStart = -1;
outAttrs.initialSelEnd = -1;
return this;
}
@ -450,41 +460,23 @@ class GeckoInputConnection
}
public void notifyIME(final int type, final int state) {
final View v = getView();
if (v == null)
return;
switch (type) {
case NOTIFY_IME_RESETINPUTSTATE:
if (DEBUG) Log.d(LOGTAG, ". . . notifyIME: reset");
resetCompositionState();
// Don't use IMEStateUpdater for reset.
// Because IME may not work showSoftInput()
// after calling restartInput() immediately.
// So we have to call showSoftInput() delay.
InputMethodManager imm = getInputMethodManager();
if (imm == null) {
// no way to reset IME status directly
IMEStateUpdater.resetIME();
} else {
imm.restartInput(v);
}
// keep current enabled state
IMEStateUpdater.enableIME();
break;
case NOTIFY_IME_CANCELCOMPOSITION:
if (DEBUG) Log.d(LOGTAG, ". . . notifyIME: cancel");
removeComposingSpans(getEditable());
// Set composition to empty and end composition
setComposingText("", 0);
// Fall through
case NOTIFY_IME_RESETINPUTSTATE:
// Commit and end composition
finishComposingText();
restartInput();
break;
case NOTIFY_IME_FOCUSCHANGE:
if (DEBUG) Log.d(LOGTAG, ". . . notifyIME: focus");
IMEStateUpdater.resetIME();
// Showing/hiding vkb is done in notifyIMEEnabled
mBatchEditCount = 0;
mUpdateRequest = null;
break;
default:
@ -497,82 +489,34 @@ class GeckoInputConnection
public void notifyIMEEnabled(final int state, final String typeHint,
final String modeHint, final String actionHint) {
// For some input type we will use a widget to display the ui, for those we must not
// For some input type we will use a widget to display the ui, for those we must not
// display the ime. We can display a widget for date and time types and, if the sdk version
// is greater than 11, for datetime/month/week as well.
if (typeHint.equals("date") || typeHint.equals("time") ||
(Build.VERSION.SDK_INT > 10 &&
(typeHint.equals("datetime") || typeHint.equals("month") ||
typeHint.equals("week") || typeHint.equals("datetime-local")))) {
mIMEState = IME_STATE_DISABLED;
return;
}
final View v = getView();
if (v == null)
return;
/* When IME is 'disabled', IME processing is disabled.
In addition, the IME UI is hidden */
mIMEState = state;
mIMETypeHint = (typeHint == null) ? "" : typeHint;
mIMEModeHint = (modeHint == null) ? "" : modeHint;
mIMEActionHint = (actionHint == null) ? "" : actionHint;
IMEStateUpdater.enableIME();
}
/* Delay updating IME states (see bug 573800) */
private static final class IMEStateUpdater extends TimerTask {
private static IMEStateUpdater instance;
private boolean mEnable;
private boolean mReset;
private static IMEStateUpdater getInstance() {
if (instance == null) {
instance = new IMEStateUpdater();
mIMETimer.schedule(instance, 200);
}
return instance;
}
public static synchronized void enableIME() {
getInstance().mEnable = true;
}
public static synchronized void resetIME() {
getInstance().mReset = true;
}
public void run() {
if (DEBUG) Log.d(LOGTAG, "IME: IMEStateUpdater.run()");
synchronized (IMEStateUpdater.class) {
instance = null;
}
// TimerTask.run() is running on a random background thread, so post to UI thread.
postToUiThread(new Runnable() {
public void run() {
final View v = getView();
if (v == null)
return;
final InputMethodManager imm = getInputMethodManager();
if (imm == null)
return;
if (mReset)
imm.restartInput(v);
if (!mEnable)
return;
if (mIMEState != IME_STATE_DISABLED) {
imm.showSoftInput(v, 0);
} else if (imm.isActive(v)) {
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
restartInput();
GeckoApp.mAppContext.mMainHandler.postDelayed(new Runnable() {
public void run() {
if (mIMEState == IME_STATE_DISABLED) {
hideSoftInput();
} else {
showSoftInput();
}
});
}
}
}, 200); // Delay 200ms to prevent repeated IME showing/hiding
}
}

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

@ -486,6 +486,7 @@ RES_XML = \
RES_ANIM = \
res/anim/awesomebar_fade_in.xml \
res/anim/awesomebar_fade_out.xml \
res/anim/awesomebar_hold_still.xml \
res/anim/grow_fade_in.xml \
res/anim/grow_fade_in_center.xml \

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

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:interpolator="@android:anim/accelerate_interpolator"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="200"/>
</set>

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

@ -120,6 +120,7 @@
"content/xul/content/test/test_bug486990.xul": "TIMED_OUT",
"docshell/test/navigation/test_bug13871.html": "RANDOM",
"docshell/test/navigation/test_bug430723.html": "TIMED_OUT",
"docshell/test/navigation/test_popup-navigates-children.html": "bug 783589",
"docshell/test/navigation/test_sessionhistory.html": "RANDOM",
"docshell/test/test_bug344861.html": "",
"docshell/test/test_bug94514.html": "TIMED_OUT",
@ -127,6 +128,7 @@
"docshell/test/test_bug598895.html": "",
"docshell/test/test_bug637644.html": "",
"docshell/test/test_bug668513.html": "RANDOM",
"docshell/test/test_framedhistoryframes.html": "bug 784321",
"dom/browser-element/mochitest/test_browserElement_oop_SecurityChange.html": "TIMED_OUT, bug 766586",
"dom/browser-element/mochitest/test_browserElement_inproc_AppFramePermission.html": "",
"dom/browser-element/mochitest/test_browserElement_inproc_AppWindowNamespace.html": "TIMED_OUT, bug 783509",

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 544 B

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

@ -6,7 +6,6 @@ toolkit.jar:
% skin mozapps classic/1.0 %skin/classic/mozapps/
skin/classic/mozapps/downloads/buttons.png (downloads/buttons.png)
skin/classic/mozapps/downloads/downloadIcon.png (downloads/downloadIcon.png)
skin/classic/mozapps/downloads/downloadStatusIcon.png (downloads/downloadStatusIcon.png)
* skin/classic/mozapps/downloads/downloads.css (downloads/downloads.css)
skin/classic/mozapps/downloads/unknownContentType.css (downloads/unknownContentType.css)
skin/classic/mozapps/extensions/category-search.png (extensions/category-search.png)

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

@ -665,6 +665,13 @@ AndroidGeckoEvent::Init(int aType)
mType = aType;
}
void
AndroidGeckoEvent::Init(int aType, int aAction)
{
mType = aType;
mAction = aAction;
}
void
AndroidGeckoEvent::Init(int x1, int y1, int x2, int y2)
{

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

@ -633,6 +633,9 @@ public:
AndroidGeckoEvent(int aType) {
Init(aType);
}
AndroidGeckoEvent(int aType, int aAction) {
Init(aType, aAction);
}
AndroidGeckoEvent(int x1, int y1, int x2, int y2) {
Init(x1, y1, x2, y2);
}
@ -648,6 +651,7 @@ public:
void Init(JNIEnv *jenv, jobject jobj);
void Init(int aType);
void Init(int aType, int aAction);
void Init(int x1, int y1, int x2, int y2);
void Init(int aType, const nsIntRect &aRect);
void Init(AndroidGeckoEvent *aResizeEvent);
@ -816,7 +820,8 @@ public:
IME_ADD_COMPOSITION_RANGE = 3,
IME_UPDATE_COMPOSITION = 4,
IME_REMOVE_COMPOSITION = 5,
IME_ACKNOWLEDGE_FOCUS = 6
IME_ACKNOWLEDGE_FOCUS = 6,
IME_FLUSH_CHANGES = 7
};
};

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

@ -1763,8 +1763,14 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
return;
}
switch (ae->Action()) {
case AndroidGeckoEvent::IME_FLUSH_CHANGES:
{
FlushIMEChanges();
}
break;
case AndroidGeckoEvent::IME_SYNCHRONIZE:
{
FlushIMEChanges();
AndroidBridge::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT, 0);
}
break;
@ -1807,6 +1813,7 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
event.data = ae->Characters();
DispatchEvent(&event);
}
FlushIMEChanges();
AndroidBridge::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT, 0);
}
break;
@ -2046,13 +2053,18 @@ nsWindow::OnIMEFocusChange(bool aFocus)
ALOGIME("IME: OnIMEFocusChange: f=%d", aFocus);
if (aFocus) {
mIMETextChanges.Clear();
mIMESelectionChange = IMEChange();
// OnIMETextChange also notifies selection
OnIMETextChange(0, INT32_MAX, INT32_MAX);
OnIMESelectionChange();
FlushIMEChanges();
} else {
// Mask events because we lost focus. On the next focus event, Gecko will notify
// Java, and Java will send an acknowledge focus event back to Gecko. That is
// where we unmask event handling
mIMEMaskEventsCount++;
mIMEComposing = false;
mIMEComposingText.Truncate();
}
AndroidBridge::NotifyIME(AndroidBridge::NOTIFY_IME_FOCUSCHANGE,
@ -2061,6 +2073,51 @@ nsWindow::OnIMEFocusChange(bool aFocus)
return NS_OK;
}
void
nsWindow::PostFlushIMEChanges()
{
if (!mIMETextChanges.IsEmpty() || !mIMESelectionChange.IsEmpty()) {
// Already posted
return;
}
AndroidGeckoEvent *event = new AndroidGeckoEvent(
AndroidGeckoEvent::IME_EVENT, AndroidGeckoEvent::IME_FLUSH_CHANGES);
nsAppShell::gAppShell->PostEvent(event);
}
void
nsWindow::FlushIMEChanges()
{
nsRefPtr<nsWindow> kungFuDeathGrip(this);
for (uint32_t i = 0; i < mIMETextChanges.Length(); i++) {
IMEChange &change = mIMETextChanges[i];
MOZ_ASSERT(change.IsTextChange());
nsQueryContentEvent event(true, NS_QUERY_TEXT_CONTENT, this);
InitEvent(event, nullptr);
event.InitForQueryTextContent(change.mStart,
change.mNewEnd - change.mStart);
DispatchEvent(&event);
if (!event.mSucceeded)
return;
AndroidBridge::NotifyIMEChange(event.mReply.mString.get(),
event.mReply.mString.Length(),
change.mStart,
change.mOldEnd,
change.mNewEnd);
}
mIMETextChanges.Clear();
if (!mIMESelectionChange.IsEmpty()) {
MOZ_ASSERT(!mIMESelectionChange.IsTextChange());
AndroidBridge::NotifyIMEChange(nullptr, 0,
mIMESelectionChange.mStart,
mIMESelectionChange.mOldEnd, -1);
mIMESelectionChange = IMEChange();
}
}
NS_IMETHODIMP
nsWindow::OnIMETextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd)
{
@ -2070,21 +2127,67 @@ nsWindow::OnIMETextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd)
ALOGIME("IME: OnIMETextChange: s=%d, oe=%d, ne=%d",
aStart, aOldEnd, aNewEnd);
nsRefPtr<nsWindow> kungFuDeathGrip(this);
nsQueryContentEvent event(true, NS_QUERY_TEXT_CONTENT, this);
InitEvent(event, nullptr);
event.InitForQueryTextContent(aStart, aNewEnd - aStart);
DispatchEvent(&event);
if (!event.mSucceeded)
return NS_OK;
AndroidBridge::NotifyIMEChange(event.mReply.mString.get(),
event.mReply.mString.Length(),
aStart, aOldEnd, aNewEnd);
/* Make sure Java's selection is up-to-date */
mIMESelectionChange = IMEChange();
OnIMESelectionChange();
PostFlushIMEChanges();
mIMETextChanges.AppendElement(IMEChange(aStart, aOldEnd, aNewEnd));
// Now that we added a new range we need to go back and
// update all the ranges before that.
// Ranges that have offsets which follow this new range
// need to be updated to reflect new offsets
int32_t delta = (int32_t)(aNewEnd - aOldEnd);
for (int32_t i = mIMETextChanges.Length() - 2; i >= 0; i--) {
IMEChange &previousChange = mIMETextChanges[i];
if (previousChange.mStart > (int32_t)aOldEnd) {
previousChange.mStart += delta;
previousChange.mOldEnd += delta;
previousChange.mNewEnd += delta;
}
}
// Now go through all ranges to merge any ranges that are connected
// srcIndex is the index of the range to merge from
// dstIndex is the index of the range to potentially merge into
int32_t srcIndex = mIMETextChanges.Length() - 1;
int32_t dstIndex = srcIndex;
while (--dstIndex >= 0) {
IMEChange &src = mIMETextChanges[srcIndex];
IMEChange &dst = mIMETextChanges[dstIndex];
// When merging a more recent change into an older
// change, we need to compare recent change's (start, oldEnd)
// range to the older change's (start, newEnd)
if (src.mOldEnd < dst.mStart || dst.mNewEnd < src.mStart) {
// No overlap between ranges
continue;
}
// When merging two ranges, there are generally four posibilities:
// [----(----]----), (----[----]----),
// [----(----)----], (----[----)----]
// where [----] is the first range and (----) is the second range
// As seen above, the start of the merged range is always the lesser
// of the two start offsets. OldEnd and NewEnd then need to be
// adjusted separately depending on the case. In any case, the change
// in text length of the merged range should be the sum of text length
// changes of the two original ranges, i.e.,
// newNewEnd - newOldEnd == newEnd1 - oldEnd1 + newEnd2 - oldEnd2
dst.mStart = std::min(dst.mStart, src.mStart);
if (src.mOldEnd < dst.mNewEnd) {
// New range overlaps or is within previous range; merge
dst.mNewEnd += src.mNewEnd - src.mOldEnd;
} else { // src.mOldEnd >= dst.mNewEnd
// New range overlaps previous range; merge
dst.mOldEnd += src.mOldEnd - dst.mNewEnd;
dst.mNewEnd = src.mNewEnd;
}
// src merged to dst; delete src.
mIMETextChanges.RemoveElementAt(srcIndex);
// Any ranges that we skip over between src and dst are not mergeable
// so we can safely continue the merge starting at dst
srcIndex = dstIndex;
}
return NS_OK;
}
@ -2104,8 +2207,9 @@ nsWindow::OnIMESelectionChange(void)
if (!event.mSucceeded)
return NS_OK;
AndroidBridge::NotifyIMEChange(nullptr, 0, int(event.GetSelectionStart()),
int(event.GetSelectionEnd()), -1);
PostFlushIMEChanges();
mIMESelectionChange = IMEChange((int32_t)event.GetSelectionStart(),
(int32_t)event.GetSelectionEnd());
return NS_OK;
}

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

@ -167,6 +167,8 @@ protected:
bool DrawTo(gfxASurface *targetSurface, const nsIntRect &aRect);
bool IsTopLevel();
void RemoveIMEComposition();
void PostFlushIMEChanges();
void FlushIMEChanges();
// Call this function when the users activity is the direct cause of an
// event (like a keypress or mouse click).
@ -188,6 +190,33 @@ protected:
nsString mIMEComposingText;
nsAutoTArray<nsTextRange, 4> mIMERanges;
struct IMEChange {
int32_t mStart, mOldEnd, mNewEnd;
IMEChange() :
mStart(-1), mOldEnd(-1), mNewEnd(-1)
{
}
IMEChange(int32_t start, int32_t oldEnd, int32_t newEnd) :
mStart(start), mOldEnd(oldEnd), mNewEnd(newEnd)
{
}
IMEChange(int32_t start, int32_t end) :
mStart(start), mOldEnd(end), mNewEnd(-1)
{
}
bool IsEmpty()
{
return mStart < 0;
}
bool IsTextChange()
{
return mNewEnd >= 0;
}
};
nsAutoTArray<IMEChange, 4> mIMETextChanges;
IMEChange mIMESelectionChange;
InputContext mInputContext;
static void DumpWindows();