mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central and inbound
This commit is contained in:
commit
20148ab4c5
@ -3973,16 +3973,6 @@ var XULBrowserWindow = {
|
||||
}
|
||||
} else
|
||||
disableFindCommands(false);
|
||||
|
||||
if (gFindBarInitialized) {
|
||||
if (gFindBar.findMode != gFindBar.FIND_NORMAL) {
|
||||
// Close the Find toolbar if we're in old-style TAF mode
|
||||
gFindBar.close();
|
||||
}
|
||||
|
||||
// fix bug 253793 - turn off highlight when page changes
|
||||
gFindBar.getElement("highlight").checked = false;
|
||||
}
|
||||
}
|
||||
UpdateBackForwardCommands(gBrowser.webNavigation);
|
||||
|
||||
|
@ -687,6 +687,17 @@
|
||||
// previous location.
|
||||
this.mBrowser.missingPlugins = null;
|
||||
|
||||
if (this.mTabBrowser.isFindBarInitialized(this.mTab)) {
|
||||
let findBar = this.mTabBrowser.getFindBar(this.mTab);
|
||||
|
||||
// Close the Find toolbar if we're in old-style TAF mode
|
||||
if (findBar.findMode != findBar.FIND_NORMAL)
|
||||
findBar.close();
|
||||
|
||||
// fix bug 253793 - turn off highlight when page changes
|
||||
findBar.getElement("highlight").checked = false;
|
||||
}
|
||||
|
||||
// Don't clear the favicon if this onLocationChange was
|
||||
// triggered by a pushState or a replaceState. See bug 550565.
|
||||
if (!gMultiProcessBrowser) {
|
||||
|
@ -39,6 +39,8 @@ function test() {
|
||||
gBrowser.selectedTab = tabs[0];
|
||||
|
||||
setFindString(texts[0]);
|
||||
// Turn on highlight for testing bug 891638
|
||||
gFindBar.getElement("highlight").checked = true;
|
||||
|
||||
// Make sure the second tab is correct, then set it up
|
||||
gBrowser.selectedTab = tabs[1];
|
||||
@ -52,6 +54,17 @@ function test() {
|
||||
gBrowser.selectedTab = tabs[0];
|
||||
ok(!gFindBar.hidden, "First tab shows find bar!");
|
||||
is(gFindBar._findField.value, texts[0], "First tab persists find value!");
|
||||
ok(gFindBar.getElement("highlight").checked,
|
||||
"Highlight button state persists!");
|
||||
|
||||
// While we're here, let's test bug 253793
|
||||
gBrowser.reload();
|
||||
gBrowser.addEventListener("DOMContentLoaded", continueTests, true);
|
||||
}
|
||||
|
||||
function continueTests() {
|
||||
gBrowser.removeEventListener("DOMContentLoaded", continueTests, true);
|
||||
ok(!gFindBar.getElement("highlight").checked, "Highlight button reset!");
|
||||
gFindBar.close();
|
||||
ok(gFindBar.hidden, "First tab doesn't show find bar!");
|
||||
gBrowser.selectedTab = tabs[1];
|
||||
|
@ -610,6 +610,16 @@ var SelectionHelperUI = {
|
||||
this.startMark.position(targetMark.xPos, targetMark.yPos);
|
||||
this.endMark.position(targetMark.xPos, targetMark.yPos);
|
||||
|
||||
// We delay transitioning until we know which direction the user is dragging
|
||||
// based on a hysteresis value in the drag marker code. Down in our caller, we
|
||||
// cache the first drag position in _cachedCaretPos so we can select from the
|
||||
// initial caret drag position. Use those values if we have them. (Note
|
||||
// _cachedCaretPos has already been translated in _getMarkerBaseMessage.)
|
||||
let xpos = this._cachedCaretPos ? this._cachedCaretPos.xPos :
|
||||
this._msgTarget.ctobx(targetMark.xPos, true);
|
||||
let ypos = this._cachedCaretPos ? this._cachedCaretPos.yPos :
|
||||
this._msgTarget.ctoby(targetMark.yPos, true);
|
||||
|
||||
// Start the selection monocle drag. SelectionHandler relies on this
|
||||
// for getting initialized. This will also trigger a message back for
|
||||
// monocle positioning. Note, markerDragMove is still on the stack in
|
||||
@ -617,8 +627,8 @@ var SelectionHelperUI = {
|
||||
this._sendAsyncMessage("Browser:SelectionSwitchMode", {
|
||||
newMode: "selection",
|
||||
change: targetMark.tag,
|
||||
xPos: this._msgTarget.ctobx(targetMark.xPos, true),
|
||||
yPos: this._msgTarget.ctoby(targetMark.yPos, true),
|
||||
xPos: xpos,
|
||||
yPos: ypos,
|
||||
});
|
||||
},
|
||||
|
||||
@ -1097,6 +1107,7 @@ var SelectionHelperUI = {
|
||||
markerDragStart: function markerDragStart(aMarker) {
|
||||
let json = this._getMarkerBaseMessage(aMarker.tag);
|
||||
if (aMarker.tag == "caret") {
|
||||
this._cachedCaretPos = null;
|
||||
this._sendAsyncMessage("Browser:CaretMove", json);
|
||||
return;
|
||||
}
|
||||
@ -1123,8 +1134,13 @@ var SelectionHelperUI = {
|
||||
this._transitionFromCaretToSelection(aDirection);
|
||||
return false;
|
||||
}
|
||||
// Cache for when we start the drag in _transitionFromCaretToSelection.
|
||||
if (!this._cachedCaretPos) {
|
||||
this._cachedCaretPos = this._getMarkerBaseMessage(aMarker.tag).caret;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
this._cachedCaretPos = null;
|
||||
|
||||
// We'll re-display these after the drag is complete.
|
||||
this._hideMonocles();
|
||||
|
@ -38,7 +38,7 @@ gTests.push({
|
||||
let div = gWindow.document.getElementById("testdiv");
|
||||
ok(div, "have the div");
|
||||
|
||||
sendElementTap(gWindow, div, 295); // end of 'outlook.com'
|
||||
sendElementTap(gWindow, div, 287); // end of 'outlook.com'
|
||||
|
||||
yield waitForCondition(function () {
|
||||
return SelectionHelperUI.isCaretUIVisible;
|
||||
|
@ -372,8 +372,13 @@ nsContentEventHandler::SetRangeFromFlatTextOffset(
|
||||
nsRange* aRange,
|
||||
uint32_t aNativeOffset,
|
||||
uint32_t aNativeLength,
|
||||
bool aExpandToClusterBoundaries)
|
||||
bool aExpandToClusterBoundaries,
|
||||
uint32_t* aNewNativeOffset)
|
||||
{
|
||||
if (aNewNativeOffset) {
|
||||
*aNewNativeOffset = aNativeOffset;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
|
||||
nsresult rv = iter->Init(mRootContent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -404,8 +409,12 @@ nsContentEventHandler::SetRangeFromFlatTextOffset(
|
||||
ConvertToXPOffset(content, aNativeOffset - nativeOffset) : 0;
|
||||
|
||||
if (aExpandToClusterBoundaries) {
|
||||
uint32_t oldXPOffset = xpOffset;
|
||||
rv = ExpandToClusterBoundary(content, false, &xpOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (aNewNativeOffset) {
|
||||
*aNewNativeOffset -= (oldXPOffset - xpOffset);
|
||||
}
|
||||
}
|
||||
|
||||
rv = aRange->SetStart(domNode, int32_t(xpOffset));
|
||||
@ -456,6 +465,9 @@ nsContentEventHandler::SetRangeFromFlatTextOffset(
|
||||
MOZ_ASSERT(!mRootContent->IsNodeOfType(nsINode::eTEXT));
|
||||
rv = aRange->SetStart(domNode, int32_t(mRootContent->GetChildCount()));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (aNewNativeOffset) {
|
||||
*aNewNativeOffset = nativeOffset;
|
||||
}
|
||||
}
|
||||
rv = aRange->SetEnd(domNode, int32_t(mRootContent->GetChildCount()));
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "nsIDOMRange::SetEnd failed");
|
||||
@ -517,7 +529,8 @@ nsContentEventHandler::OnQueryTextContent(nsQueryContentEvent* aEvent)
|
||||
|
||||
nsRefPtr<nsRange> range = new nsRange(mRootContent);
|
||||
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
|
||||
aEvent->mInput.mLength, false);
|
||||
aEvent->mInput.mLength, false,
|
||||
&aEvent->mReply.mOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = GenerateFlatTextContent(range, aEvent->mReply.mString);
|
||||
@ -574,7 +587,10 @@ nsContentEventHandler::OnQueryTextRect(nsQueryContentEvent* aEvent)
|
||||
|
||||
nsRefPtr<nsRange> range = new nsRange(mRootContent);
|
||||
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
|
||||
aEvent->mInput.mLength, true);
|
||||
aEvent->mInput.mLength, true,
|
||||
&aEvent->mReply.mOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = GenerateFlatTextContent(range, aEvent->mReply.mString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// used to iterate over all contents and their frames
|
||||
@ -717,6 +733,7 @@ nsContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
aEvent->mReply.mRect =
|
||||
rect.ToOutsidePixels(caretFrame->PresContext()->AppUnitsPerDevPixel());
|
||||
aEvent->mReply.mOffset = aEvent->mInput.mOffset;
|
||||
aEvent->mSucceeded = true;
|
||||
return NS_OK;
|
||||
}
|
||||
@ -724,7 +741,8 @@ nsContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
|
||||
|
||||
// Otherwise, we should set the guessed caret rect.
|
||||
nsRefPtr<nsRange> range = new nsRange(mRootContent);
|
||||
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 0, true);
|
||||
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 0, true,
|
||||
&aEvent->mReply.mOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
int32_t offsetInFrame;
|
||||
|
@ -91,7 +91,8 @@ protected:
|
||||
nsresult SetRangeFromFlatTextOffset(nsRange* aRange,
|
||||
uint32_t aNativeOffset,
|
||||
uint32_t aNativeLength,
|
||||
bool aExpandToClusterBoundaries);
|
||||
bool aExpandToClusterBoundaries,
|
||||
uint32_t* aNewNativeOffset = nullptr);
|
||||
// Find the first textframe for the range, and get the start offset in
|
||||
// the frame.
|
||||
nsresult GetStartFrameAndOffset(nsRange* aRange,
|
||||
|
@ -51,7 +51,7 @@ class nsTextStateManager MOZ_FINAL : public nsISelectionListener,
|
||||
{
|
||||
public:
|
||||
nsTextStateManager()
|
||||
: mObserving(false)
|
||||
: mObserving(nsIMEUpdatePreference::NOTIFY_NOTHING)
|
||||
{
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@ private:
|
||||
void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd);
|
||||
void ObserveEditableNode();
|
||||
|
||||
bool mObserving;
|
||||
nsIMEUpdatePreference::Notifications mObserving;
|
||||
uint32_t mPreAttrChangeLength;
|
||||
};
|
||||
|
||||
@ -736,9 +736,7 @@ nsTextStateManager::Init(nsIWidget* aWidget,
|
||||
return;
|
||||
}
|
||||
|
||||
if (mWidget->GetIMEUpdatePreference().mWantUpdates) {
|
||||
ObserveEditableNode();
|
||||
}
|
||||
ObserveEditableNode();
|
||||
}
|
||||
|
||||
void
|
||||
@ -747,18 +745,19 @@ nsTextStateManager::ObserveEditableNode()
|
||||
MOZ_ASSERT(mSel);
|
||||
MOZ_ASSERT(mRootContent);
|
||||
|
||||
// add selection change listener
|
||||
nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel));
|
||||
NS_ENSURE_TRUE_VOID(selPrivate);
|
||||
nsresult rv = selPrivate->AddSelectionListener(this);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
rv = selPrivate->AddSelectionListener(this);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
mObserving = mWidget->GetIMEUpdatePreference().mWantUpdates;
|
||||
if (mObserving & nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE) {
|
||||
// add selection change listener
|
||||
nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel));
|
||||
NS_ENSURE_TRUE_VOID(selPrivate);
|
||||
nsresult rv = selPrivate->AddSelectionListener(this);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
}
|
||||
|
||||
// add text change observer
|
||||
mRootContent->AddMutationObserver(this);
|
||||
|
||||
mObserving = true;
|
||||
if (mObserving & nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE) {
|
||||
// add text change observer
|
||||
mRootContent->AddMutationObserver(this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -776,13 +775,13 @@ nsTextStateManager::Destroy(void)
|
||||
}
|
||||
// Even if there are some pending notification, it'll never notify the widget.
|
||||
mWidget = nullptr;
|
||||
if (mObserving && mSel) {
|
||||
if ((mObserving & nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE) && mSel) {
|
||||
nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel));
|
||||
if (selPrivate)
|
||||
selPrivate->RemoveSelectionListener(this);
|
||||
}
|
||||
mSel = nullptr;
|
||||
if (mObserving && mRootContent) {
|
||||
if ((mObserving & nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE) && mRootContent) {
|
||||
mRootContent->RemoveMutationObserver(this);
|
||||
}
|
||||
mRootContent = nullptr;
|
||||
|
@ -748,7 +748,7 @@ TabParent::RecvNotifyIMEFocus(const bool& aFocus,
|
||||
{
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget) {
|
||||
aPreference->mWantUpdates = false;
|
||||
aPreference->mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING;
|
||||
aPreference->mWantHints = false;
|
||||
return true;
|
||||
}
|
||||
|
@ -23,7 +23,6 @@
|
||||
#define BOOKMARKS_FILE_50_NAME NS_LITERAL_CSTRING("bookmarks.html")
|
||||
#define DOWNLOADS_FILE_50_NAME NS_LITERAL_CSTRING("downloads.rdf")
|
||||
#define SEARCH_FILE_50_NAME NS_LITERAL_CSTRING("search.rdf" )
|
||||
#define STORAGE_FILE_50_NAME NS_LITERAL_CSTRING("storage.sdb")
|
||||
|
||||
//*****************************************************************************
|
||||
// nsProfileDirServiceProvider::nsProfileDirServiceProvider
|
||||
@ -226,11 +225,6 @@ nsProfileDirServiceProvider::GetFile(const char *prop, bool *persistant, nsIFile
|
||||
rv = EnsureProfileFileExists(localFile, domainDir);
|
||||
}
|
||||
}
|
||||
else if (strcmp(prop, NS_APP_STORAGE_50_FILE) == 0) {
|
||||
rv = domainDir->Clone(getter_AddRefs(localFile));
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = localFile->AppendNative(STORAGE_FILE_50_NAME);
|
||||
}
|
||||
|
||||
|
||||
if (localFile && NS_SUCCEEDED(rv))
|
||||
|
@ -76,7 +76,7 @@ interface mozIStorageService : nsISupports {
|
||||
* Get a connection to a named special database storage.
|
||||
*
|
||||
* @param aStorageKey a string key identifying the type of storage
|
||||
* requested. Valid values include: "profile", "memory".
|
||||
* requested. Valid values include: "memory".
|
||||
*
|
||||
* @see openDatabase for restrictions on how database connections may be
|
||||
* used. For the profile database, you should only access it from the main
|
||||
@ -189,6 +189,5 @@ interface mozIStorageService : nsISupports {
|
||||
%{C++
|
||||
|
||||
#define MOZ_STORAGE_MEMORY_STORAGE_KEY "memory"
|
||||
#define MOZ_STORAGE_PROFILE_STORAGE_KEY "profile"
|
||||
|
||||
%}
|
||||
|
@ -619,9 +619,6 @@ Service::getLocaleCollation()
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// mozIStorageService
|
||||
|
||||
#ifndef NS_APP_STORAGE_50_FILE
|
||||
#define NS_APP_STORAGE_50_FILE "UStor"
|
||||
#endif
|
||||
|
||||
NS_IMETHODIMP
|
||||
Service::OpenSpecialDatabase(const char *aStorageKey,
|
||||
@ -634,13 +631,6 @@ Service::OpenSpecialDatabase(const char *aStorageKey,
|
||||
// just fall through with NULL storageFile, this will cause the storage
|
||||
// connection to use a memory DB.
|
||||
}
|
||||
else if (::strcmp(aStorageKey, "profile") == 0) {
|
||||
rv = NS_GetSpecialDirectory(NS_APP_STORAGE_50_FILE,
|
||||
getter_AddRefs(storageFile));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// fall through to DB initialization
|
||||
}
|
||||
else {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
@ -31,6 +31,9 @@ var _nextPortId = 1;
|
||||
// new ClientPort.
|
||||
this.getFrameWorkerHandle =
|
||||
function getFrameWorkerHandle(url, clientWindow, name, origin, exposeLocalStorage = false) {
|
||||
// prevent data/about urls - see bug 891516
|
||||
if (['http', 'https'].indexOf(Services.io.newURI(url, null, null).scheme) < 0)
|
||||
throw new Error("getFrameWorkerHandle requires http/https urls");
|
||||
// first create the client port we are going to use. Later we will
|
||||
// message the worker to create the worker port.
|
||||
let portid = _nextPortId++;
|
||||
|
@ -16,6 +16,7 @@ ifdef MOZ_SOCIAL
|
||||
MOCHITEST_BROWSER_FILES = \
|
||||
head.js \
|
||||
data.json \
|
||||
echo.sjs \
|
||||
worker_xhr.js \
|
||||
browser_frameworker.js \
|
||||
worker_relative.js \
|
||||
|
@ -3,7 +3,11 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function makeWorkerUrl(runner) {
|
||||
return "data:application/javascript;charset=utf-8," + encodeURI("let run=" + runner.toSource()) + ";run();"
|
||||
let prefix = "http://example.com/browser/toolkit/components/social/test/browser/echo.sjs?";
|
||||
if (typeof runner == "function") {
|
||||
runner = "let run=" + runner.toSource() + ";run();";
|
||||
}
|
||||
return prefix + encodeURI(runner);
|
||||
}
|
||||
|
||||
var getFrameWorkerHandle;
|
||||
@ -124,7 +128,8 @@ let tests = {
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), fakeWindow, "testPrototypes");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "hello" && e.data.data.somextrafunction) {
|
||||
if (e.data.topic == "hello") {
|
||||
ok(e.data.data.somextrafunction, "have someextrafunction")
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
@ -455,7 +460,7 @@ let tests = {
|
||||
let ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService2);
|
||||
let oldManage = ioService.manageOfflineStatus;
|
||||
let oldOffline = ioService.offline;
|
||||
|
||||
|
||||
ioService.manageOfflineStatus = false;
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testNavigator");
|
||||
let expected_topic = "onoffline";
|
||||
@ -521,7 +526,7 @@ let tests = {
|
||||
},
|
||||
|
||||
testEmptyWorker: function(cbnext) {
|
||||
let worker = getFrameWorkerHandle("data:application/javascript;charset=utf-8,",
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(''),
|
||||
undefined, "testEmptyWorker");
|
||||
Services.obs.addObserver(function handleError(subj, topic, data) {
|
||||
Services.obs.removeObserver(handleError, "social:frameworker-error");
|
||||
|
@ -10,7 +10,8 @@ function ensureProvider(workerFunction, cb) {
|
||||
let manifest = {
|
||||
origin: TEST_PROVIDER_ORIGIN,
|
||||
name: "Example Provider",
|
||||
workerURL: "data:application/javascript;charset=utf-8," + encodeURI("let run=" + workerFunction.toSource()) + ";run();"
|
||||
workerURL: "http://example.com/browser/toolkit/components/social/test/browser/echo.sjs?"
|
||||
+ encodeURI("let run=" + workerFunction.toSource()) + ";run();"
|
||||
};
|
||||
|
||||
ensureSocialEnabled();
|
||||
|
9
toolkit/components/social/test/browser/echo.sjs
Normal file
9
toolkit/components/social/test/browser/echo.sjs
Normal file
@ -0,0 +1,9 @@
|
||||
// A server-side JS test file for frameworker testing. It exists only to
|
||||
// work-around a lack of data: URL support in the frameworker.
|
||||
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
// The query string is the javascript - we just write it back.
|
||||
response.setHeader("Content-Type", "application/javascript; charset=utf-8", false);
|
||||
response.write(unescape(request.queryString));
|
||||
}
|
@ -49,9 +49,20 @@ const BackgroundPageThumbs = {
|
||||
Services.tm.mainThread.dispatch(options.onDone.bind(options, url), 0);
|
||||
return;
|
||||
}
|
||||
let cap = new Capture(url, this._onCaptureOrTimeout.bind(this), options);
|
||||
this._captureQueue = this._captureQueue || [];
|
||||
this._capturesByURL = this._capturesByURL || new Map();
|
||||
// We want to avoid duplicate captures for the same URL. If there is an
|
||||
// existing one, we just add the callback to that one and we are done.
|
||||
let existing = this._capturesByURL.get(url);
|
||||
if (existing) {
|
||||
if (options.onDone)
|
||||
existing.doneCallbacks.push(options.onDone);
|
||||
// The queue is already being processed, so nothing else to do...
|
||||
return;
|
||||
}
|
||||
let cap = new Capture(url, this._onCaptureOrTimeout.bind(this), options);
|
||||
this._captureQueue.push(cap);
|
||||
this._capturesByURL.set(url, cap);
|
||||
this._processCaptureQueue();
|
||||
},
|
||||
|
||||
@ -185,6 +196,7 @@ const BackgroundPageThumbs = {
|
||||
if (idx < 0)
|
||||
throw new Error("The capture should be in the queue.");
|
||||
this._captureQueue.splice(idx, 1);
|
||||
this._capturesByURL.delete(capture.url);
|
||||
|
||||
// Start the destroy-browser timer *before* processing the capture queue.
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
@ -212,6 +224,9 @@ function Capture(url, captureCallback, options) {
|
||||
this.captureCallback = captureCallback;
|
||||
this.options = options;
|
||||
this.id = Capture.nextID++;
|
||||
this.doneCallbacks = [];
|
||||
if (options.onDone)
|
||||
this.doneCallbacks.push(options.onDone);
|
||||
|
||||
// The timeout starts when the consumer requests the capture, not when the
|
||||
// capture is dequeued and started.
|
||||
@ -280,22 +295,22 @@ Capture.prototype = {
|
||||
this.captureCallback(this);
|
||||
this.destroy();
|
||||
|
||||
let callOnDone = function callOnDoneFn() {
|
||||
if (!("onDone" in this.options))
|
||||
return;
|
||||
try {
|
||||
this.options.onDone(this.url);
|
||||
}
|
||||
catch (err) {
|
||||
Cu.reportError(err);
|
||||
let callOnDones = function callOnDonesFn() {
|
||||
for (let callback of this.doneCallbacks) {
|
||||
try {
|
||||
callback.call(this.options, this.url);
|
||||
}
|
||||
catch (err) {
|
||||
Cu.reportError(err);
|
||||
}
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
if (!data) {
|
||||
callOnDone();
|
||||
callOnDones();
|
||||
return;
|
||||
}
|
||||
PageThumbs._store(this.url, data.finalURL, data.imageData).then(callOnDone);
|
||||
PageThumbs._store(this.url, data.finalURL, data.imageData).then(callOnDones);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -168,6 +168,7 @@ let tests = [
|
||||
|
||||
yield capture(url1);
|
||||
ok(file1.exists(), "First file should exist after capture.");
|
||||
file1.remove(false);
|
||||
|
||||
yield wait(2000);
|
||||
is(imports.BackgroundPageThumbs._thumbBrowser, undefined,
|
||||
@ -175,6 +176,7 @@ let tests = [
|
||||
|
||||
yield capture(url2);
|
||||
ok(file2.exists(), "Second file should exist after capture.");
|
||||
file2.remove(false);
|
||||
|
||||
imports.BackgroundPageThumbs._destroyBrowserTimeout = defaultTimeout;
|
||||
isnot(imports.BackgroundPageThumbs._thumbBrowser, undefined,
|
||||
@ -264,6 +266,37 @@ let tests = [
|
||||
"Thumbnail file should exist even though it alerted.");
|
||||
file.remove(false);
|
||||
},
|
||||
|
||||
function noDuplicates() {
|
||||
let deferred = imports.Promise.defer();
|
||||
let url = "http://example.com/1";
|
||||
let file = fileForURL(url);
|
||||
ok(!file.exists(), "Thumbnail file should not already exist.");
|
||||
let numCallbacks = 0;
|
||||
let doneCallback = function(doneUrl) {
|
||||
is(doneUrl, url, "called back with correct url");
|
||||
numCallbacks += 1;
|
||||
// We will delete the file after the first callback, then check it
|
||||
// still doesn't exist on the second callback, which should give us
|
||||
// confidence that we didn't end up with 2 different captures happening
|
||||
// for the same url...
|
||||
if (numCallbacks == 1) {
|
||||
ok(file.exists(), "Thumbnail file should now exist.");
|
||||
file.remove(false);
|
||||
return;
|
||||
}
|
||||
if (numCallbacks == 2) {
|
||||
ok(!file.exists(), "Thumbnail file should still be deleted.");
|
||||
// and that's all we expect, so we are done...
|
||||
deferred.resolve();
|
||||
return;
|
||||
}
|
||||
ok(false, "only expecting 2 callbacks");
|
||||
}
|
||||
imports.BackgroundPageThumbs.capture(url, {onDone: doneCallback});
|
||||
imports.BackgroundPageThumbs.capture(url, {onDone: doneCallback});
|
||||
yield deferred.promise;
|
||||
}
|
||||
];
|
||||
|
||||
function capture(url, options) {
|
||||
|
@ -419,9 +419,6 @@ nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent,
|
||||
EnsureProfileFileExists(file);
|
||||
ensureFilePermissions = true;
|
||||
}
|
||||
else if (!strcmp(aProperty, NS_APP_STORAGE_50_FILE)) {
|
||||
rv = file->AppendNative(NS_LITERAL_CSTRING("storage.sdb"));
|
||||
}
|
||||
else if (!strcmp(aProperty, NS_APP_DOWNLOADS_50_FILE)) {
|
||||
rv = file->AppendNative(NS_LITERAL_CSTRING("downloads.rdf"));
|
||||
}
|
||||
|
@ -2367,7 +2367,9 @@ nsWindow::NotifyIMEOfTextChange(uint32_t aStart,
|
||||
nsIMEUpdatePreference
|
||||
nsWindow::GetIMEUpdatePreference()
|
||||
{
|
||||
return nsIMEUpdatePreference(true, true);
|
||||
int8_t notifications = (nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE |
|
||||
nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE);
|
||||
return nsIMEUpdatePreference(notifications, true);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -340,6 +340,16 @@ public:
|
||||
*/
|
||||
bool DispatchEvent(nsGUIEvent& aEvent);
|
||||
|
||||
/**
|
||||
* SetSelection() dispatches NS_SELECTION_SET event for the aRange.
|
||||
*
|
||||
* @param aRange The range which will be selected.
|
||||
* @return TRUE if setting selection is succeeded and
|
||||
* the widget hasn't been destroyed.
|
||||
* Otherwise, FALSE.
|
||||
*/
|
||||
bool SetSelection(NSRange& aRange);
|
||||
|
||||
/**
|
||||
* InitKeyEvent() initializes aKeyEvent for aNativeKeyEvent.
|
||||
*
|
||||
@ -368,6 +378,13 @@ public:
|
||||
const nsAString& aCharacters,
|
||||
const nsAString& aUnmodifiedCharacters);
|
||||
|
||||
/**
|
||||
* GetWindowLevel() returns the window level of current focused (in Gecko)
|
||||
* window. E.g., if an <input> element in XUL panel has focus, this returns
|
||||
* the XUL panel's window level.
|
||||
*/
|
||||
NSInteger GetWindowLevel();
|
||||
|
||||
/**
|
||||
* IsSpecialGeckoKey() checks whether aNativeKeyCode is mapped to a special
|
||||
* Gecko keyCode. A key is "special" if it isn't used for text input.
|
||||
@ -798,6 +815,8 @@ public:
|
||||
|
||||
virtual void OnFocusChangeInGecko(bool aFocus);
|
||||
|
||||
void OnSelectionChange() { mSelectedRange.location = NSNotFound; }
|
||||
|
||||
/**
|
||||
* DispatchTextEvent() dispatches a text event on mWidget.
|
||||
*
|
||||
@ -823,9 +842,12 @@ public:
|
||||
* create an NSAttributedString from it and pass
|
||||
* that instead.
|
||||
* @param aSelectedRange Current selected range (or caret position).
|
||||
* @param aReplacementRange The range which will be replaced with the
|
||||
* aAttrString instead of current marked range.
|
||||
*/
|
||||
void SetMarkedText(NSAttributedString* aAttrString,
|
||||
NSRange& aSelectedRange);
|
||||
NSRange& aSelectedRange,
|
||||
NSRange* aReplacementRange = nullptr);
|
||||
|
||||
/**
|
||||
* ConversationIdentifier() returns an ID for the current editor. The ID is
|
||||
@ -841,12 +863,15 @@ public:
|
||||
* which is allocated as autorelease for aRange.
|
||||
*
|
||||
* @param aRange The range of string which you want.
|
||||
* @param aActualRange The actual range of the result.
|
||||
* @return The string in aRange. If the string is empty,
|
||||
* this returns nil. If succeeded, this returns
|
||||
* an instance which is allocated as autorelease.
|
||||
* If this has some troubles, returns nil.
|
||||
*/
|
||||
NSAttributedString* GetAttributedSubstringFromRange(NSRange& aRange);
|
||||
NSAttributedString* GetAttributedSubstringFromRange(
|
||||
NSRange& aRange,
|
||||
NSRange* aActualRange = nullptr);
|
||||
|
||||
/**
|
||||
* SelectedRange() returns current selected range.
|
||||
@ -865,12 +890,15 @@ public:
|
||||
* @param aRange A range of text to examine. Its position is
|
||||
* an offset from the beginning of the focused
|
||||
* editor or document.
|
||||
* @param aActualRange If this is not null, this returns the actual
|
||||
* range used for computing the result.
|
||||
* @return An NSRect containing the first character in
|
||||
* aRange, in screen coordinates.
|
||||
* If the length of aRange is 0, the width will
|
||||
* be 0.
|
||||
*/
|
||||
NSRect FirstRectForCharacterRange(NSRange& aRange);
|
||||
NSRect FirstRectForCharacterRange(NSRange& aRange,
|
||||
NSRange* aActualRange = nullptr);
|
||||
|
||||
/**
|
||||
* CharacterIndexForPoint() returns an offset of a character at aPoint.
|
||||
@ -927,9 +955,9 @@ protected:
|
||||
// See the comment in nsCocoaTextInputHandler.mm.
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
enum {
|
||||
kResetIMEWindowLevel = 1,
|
||||
kDiscardIMEComposition = 2,
|
||||
kSyncASCIICapableOnly = 4
|
||||
kNotifyIMEOfFocusChangeInGecko = 1,
|
||||
kDiscardIMEComposition = 2,
|
||||
kSyncASCIICapableOnly = 4
|
||||
};
|
||||
uint32_t mPendingMethods;
|
||||
|
||||
@ -946,8 +974,11 @@ protected:
|
||||
* is no composition, this starts a composition and commits it immediately.
|
||||
*
|
||||
* @param aAttrString A string which is committed.
|
||||
* @param aReplacementRange The range which will be replaced with the
|
||||
* aAttrString instead of current selection.
|
||||
*/
|
||||
void InsertTextAsCommittingComposition(NSAttributedString* aAttrString);
|
||||
void InsertTextAsCommittingComposition(NSAttributedString* aAttrString,
|
||||
NSRange* aReplacementRange);
|
||||
|
||||
private:
|
||||
// If mIsIMEComposing is true, the composition string is stored here.
|
||||
@ -957,6 +988,7 @@ private:
|
||||
nsString mLastDispatchedCompositionString;
|
||||
|
||||
NSRange mMarkedRange;
|
||||
NSRange mSelectedRange;
|
||||
|
||||
bool mIsIMEComposing;
|
||||
bool mIsIMEEnabled;
|
||||
@ -967,13 +999,14 @@ private:
|
||||
// that time, the focus processing in Gecko might not be finished yet. So,
|
||||
// you cannot use nsQueryContentEvent or something.
|
||||
bool mIsInFocusProcessing;
|
||||
bool mIMEHasFocus;
|
||||
|
||||
void KillIMEComposition();
|
||||
void SendCommittedText(NSString *aString);
|
||||
void OpenSystemPreferredLanguageIME();
|
||||
|
||||
// Pending methods
|
||||
void ResetIMEWindowLevel();
|
||||
void NotifyIMEOfFocusChangeInGecko();
|
||||
void DiscardIMEComposition();
|
||||
void SyncASCIICapableOnly();
|
||||
|
||||
@ -1103,8 +1136,11 @@ public:
|
||||
* the composition by the aAttrString.
|
||||
*
|
||||
* @param aAttrString An inserted string.
|
||||
* @param aReplacementRange The range which will be replaced with the
|
||||
* aAttrString instead of current selection.
|
||||
*/
|
||||
void InsertText(NSAttributedString *aAttrString);
|
||||
void InsertText(NSAttributedString *aAttrString,
|
||||
NSRange* aReplacementRange = nullptr);
|
||||
|
||||
/**
|
||||
* doCommandBySelector event handler.
|
||||
|
@ -1929,7 +1929,8 @@ TextInputHandler::DispatchKeyEventForFlagsChanged(NSEvent* aNativeEvent,
|
||||
}
|
||||
|
||||
void
|
||||
TextInputHandler::InsertText(NSAttributedString *aAttrString)
|
||||
TextInputHandler::InsertText(NSAttributedString* aAttrString,
|
||||
NSRange* aReplacementRange)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
@ -1941,10 +1942,13 @@ TextInputHandler::InsertText(NSAttributedString *aAttrString)
|
||||
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p TextInputHandler::InsertText, aAttrString=\"%s\", "
|
||||
"aReplacementRange=%p { location=%llu, length=%llu }, "
|
||||
"IsIMEComposing()=%s, IgnoreIMEComposition()=%s, "
|
||||
"keyevent=%p, keypressDispatched=%s",
|
||||
this, GetCharacters([aAttrString string]), TrueOrFalse(IsIMEComposing()),
|
||||
TrueOrFalse(IgnoreIMEComposition()),
|
||||
this, GetCharacters([aAttrString string]), aReplacementRange,
|
||||
aReplacementRange ? aReplacementRange->location : 0,
|
||||
aReplacementRange ? aReplacementRange->length : 0,
|
||||
TrueOrFalse(IsIMEComposing()), TrueOrFalse(IgnoreIMEComposition()),
|
||||
currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr,
|
||||
currentKeyEvent ?
|
||||
TrueOrFalse(currentKeyEvent->mKeyPressDispatched) : "N/A"));
|
||||
@ -1953,14 +1957,54 @@ TextInputHandler::InsertText(NSAttributedString *aAttrString)
|
||||
return;
|
||||
}
|
||||
|
||||
InputContext context = mWidget->GetInputContext();
|
||||
bool isEditable = (context.mIMEState.mEnabled == IMEState::ENABLED ||
|
||||
context.mIMEState.mEnabled == IMEState::PASSWORD);
|
||||
NSRange selectedRange = SelectedRange();
|
||||
|
||||
nsAutoString str;
|
||||
nsCocoaUtils::GetStringForNSString([aAttrString string], str);
|
||||
if (!IsIMEComposing() && str.IsEmpty()) {
|
||||
return; // nothing to do
|
||||
// nothing to do if there is no content which can be removed.
|
||||
if (!isEditable) {
|
||||
return;
|
||||
}
|
||||
// If replacement range is specified, we need to remove the range.
|
||||
// Otherwise, we need to remove the selected range if it's not collapsed.
|
||||
if (aReplacementRange && aReplacementRange->location != NSNotFound) {
|
||||
// nothing to do since the range is collapsed.
|
||||
if (aReplacementRange->length == 0) {
|
||||
return;
|
||||
}
|
||||
// If the replacement range is different from current selected range,
|
||||
// select the range.
|
||||
if (!NSEqualRanges(selectedRange, *aReplacementRange)) {
|
||||
NS_ENSURE_TRUE_VOID(SetSelection(*aReplacementRange));
|
||||
}
|
||||
selectedRange = SelectedRange();
|
||||
}
|
||||
NS_ENSURE_TRUE_VOID(selectedRange.location != NSNotFound);
|
||||
if (selectedRange.length == 0) {
|
||||
return; // nothing to do
|
||||
}
|
||||
// If this is caused by a key input, the keypress event which will be
|
||||
// dispatched later should cause the delete. Therefore, nothing to do here.
|
||||
// Although, we're not sure if such case is actually possible.
|
||||
if (!currentKeyEvent) {
|
||||
return;
|
||||
}
|
||||
// Delete the selected range.
|
||||
nsRefPtr<TextInputHandler> kungFuDeathGrip(this);
|
||||
nsContentCommandEvent deleteCommandEvent(true, NS_CONTENT_COMMAND_DELETE,
|
||||
mWidget);
|
||||
DispatchEvent(deleteCommandEvent);
|
||||
NS_ENSURE_TRUE_VOID(deleteCommandEvent.mSucceeded);
|
||||
// Be aware! The widget might be destroyed here.
|
||||
return;
|
||||
}
|
||||
|
||||
if (str.Length() != 1 || IsIMEComposing()) {
|
||||
InsertTextAsCommittingComposition(aAttrString);
|
||||
InsertTextAsCommittingComposition(aAttrString, aReplacementRange);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1972,6 +2016,14 @@ TextInputHandler::InsertText(NSAttributedString *aAttrString)
|
||||
|
||||
nsRefPtr<nsChildView> kungFuDeathGrip(mWidget);
|
||||
|
||||
// If the replacement range is specified, select the range. Then, the
|
||||
// selection will be replaced by the later keypress event.
|
||||
if (isEditable &&
|
||||
aReplacementRange && aReplacementRange->location != NSNotFound &&
|
||||
!NSEqualRanges(selectedRange, *aReplacementRange)) {
|
||||
NS_ENSURE_TRUE_VOID(SetSelection(*aReplacementRange));
|
||||
}
|
||||
|
||||
// Dispatch keypress event with char instead of textEvent
|
||||
nsKeyEvent keypressEvent(true, NS_KEY_PRESS, mWidget);
|
||||
keypressEvent.isChar = IsPrintableChar(str.CharAt(0));
|
||||
@ -2237,15 +2289,15 @@ IMEInputHandler::GetCurrentTSMDocumentID()
|
||||
******************************************************************************/
|
||||
|
||||
void
|
||||
IMEInputHandler::ResetIMEWindowLevel()
|
||||
IMEInputHandler::NotifyIMEOfFocusChangeInGecko()
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p IMEInputHandler::ResetIMEWindowLevel, "
|
||||
"Destroyed()=%s, IsFocused()=%s, GetCurrentTSMDocumentID()=%p",
|
||||
("%p IMEInputHandler::NotifyIMEOfFocusChangeInGecko, "
|
||||
"Destroyed()=%s, IsFocused()=%s, inputContext=%p",
|
||||
this, TrueOrFalse(Destroyed()), TrueOrFalse(IsFocused()),
|
||||
GetCurrentTSMDocumentID()));
|
||||
mView ? [mView inputContext] : nullptr));
|
||||
|
||||
if (Destroyed()) {
|
||||
return;
|
||||
@ -2253,45 +2305,24 @@ IMEInputHandler::ResetIMEWindowLevel()
|
||||
|
||||
if (!IsFocused()) {
|
||||
// retry at next focus event
|
||||
mPendingMethods |= kResetIMEWindowLevel;
|
||||
mPendingMethods |= kNotifyIMEOfFocusChangeInGecko;
|
||||
return;
|
||||
}
|
||||
|
||||
TSMDocumentID doc = GetCurrentTSMDocumentID();
|
||||
if (!doc) {
|
||||
// retry
|
||||
mPendingMethods |= kResetIMEWindowLevel;
|
||||
NS_WARNING("Application is active but there is no active document");
|
||||
ResetTimer();
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(mView);
|
||||
NSTextInputContext* inputContext = [mView inputContext];
|
||||
NS_ENSURE_TRUE_VOID(inputContext);
|
||||
|
||||
// When the focus of Gecko is on a text box of a popup panel, the actual
|
||||
// focused view is the panel's parent view (mView). But the editor is
|
||||
// displayed on the popuped widget's view (editorView). So, their window
|
||||
// level may be different.
|
||||
NSView<mozView>* editorView = mWidget->GetEditorView();
|
||||
if (!editorView) {
|
||||
NS_ERROR("editorView is null");
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to set the focused window level to TSMDocument. Then, the popup
|
||||
// windows of IME (E.g., a candidate list window) will be over the focused
|
||||
// view. See http://developer.apple.com/technotes/tn2005/tn2128.html#TNTAG1
|
||||
NSInteger windowLevel = [[editorView window] level];
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p IMEInputHandler::ResetIMEWindowLevel, windowLevel=%s (%X)",
|
||||
this, GetWindowLevelName(windowLevel), windowLevel));
|
||||
|
||||
// Chinese IMEs on 10.5 don't work fine if the level is NSNormalWindowLevel,
|
||||
// then, we need to increment the value.
|
||||
if (windowLevel == NSNormalWindowLevel)
|
||||
windowLevel++;
|
||||
|
||||
::TSMSetDocumentProperty(GetCurrentTSMDocumentID(),
|
||||
kTSMDocumentWindowLevelPropertyTag,
|
||||
sizeof(windowLevel), &windowLevel);
|
||||
// When an <input> element on a XUL <panel> element gets focus from an <input>
|
||||
// element on the opener window of the <panel> element, the owner window
|
||||
// still has native focus. Therefore, IMEs may store the opener window's
|
||||
// level at this time because they don't know the actual focus is moved to
|
||||
// different window. If IMEs try to get the newest window level after the
|
||||
// focus change, we return the window level of the XUL <panel>'s widget.
|
||||
// Therefore, let's emulate the native focus change. Then, IMEs can refresh
|
||||
// the stored window level.
|
||||
[inputContext deactivate];
|
||||
[inputContext activate];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
@ -2419,8 +2450,9 @@ IMEInputHandler::ExecutePendingMethods()
|
||||
DiscardIMEComposition();
|
||||
if (pendingMethods & kSyncASCIICapableOnly)
|
||||
SyncASCIICapableOnly();
|
||||
if (pendingMethods & kResetIMEWindowLevel)
|
||||
ResetIMEWindowLevel();
|
||||
if (pendingMethods & kNotifyIMEOfFocusChangeInGecko) {
|
||||
NotifyIMEOfFocusChangeInGecko();
|
||||
}
|
||||
|
||||
mIsInFocusProcessing = false;
|
||||
|
||||
@ -2615,28 +2647,63 @@ IMEInputHandler::InitCompositionEvent(nsCompositionEvent& aCompositionEvent)
|
||||
|
||||
void
|
||||
IMEInputHandler::InsertTextAsCommittingComposition(
|
||||
NSAttributedString* aAttrString)
|
||||
NSAttributedString* aAttrString,
|
||||
NSRange* aReplacementRange)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p IMEInputHandler::InsertTextAsCommittingComposition, "
|
||||
"aAttrString=\"%s\", Destroyed()=%s, IsIMEComposing()=%s, "
|
||||
"aAttrString=\"%s\", aReplacementRange=%p { location=%llu, length=%llu }, "
|
||||
"Destroyed()=%s, IsIMEComposing()=%s, "
|
||||
"mMarkedRange={ location=%llu, length=%llu }",
|
||||
this, GetCharacters([aAttrString string]), TrueOrFalse(Destroyed()),
|
||||
TrueOrFalse(IsIMEComposing()),
|
||||
this, GetCharacters([aAttrString string]), aReplacementRange,
|
||||
aReplacementRange ? aReplacementRange->location : 0,
|
||||
aReplacementRange ? aReplacementRange->length : 0,
|
||||
TrueOrFalse(Destroyed()), TrueOrFalse(IsIMEComposing()),
|
||||
mMarkedRange.location, mMarkedRange.length));
|
||||
|
||||
if (IgnoreIMECommit()) {
|
||||
MOZ_CRASH("IMEInputHandler::InsertTextAsCommittingComposition() must not"
|
||||
"be called while canceling the composition");
|
||||
}
|
||||
|
||||
if (Destroyed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// First, commit current composition with the latest composition string if the
|
||||
// replacement range is different from marked range.
|
||||
if (IsIMEComposing() && aReplacementRange &&
|
||||
aReplacementRange->location != NSNotFound &&
|
||||
!NSEqualRanges(MarkedRange(), *aReplacementRange)) {
|
||||
NSString* latestStr =
|
||||
nsCocoaUtils::ToNSString(mLastDispatchedCompositionString);
|
||||
NSAttributedString* attrLatestStr =
|
||||
[[[NSAttributedString alloc] initWithString:latestStr] autorelease];
|
||||
InsertTextAsCommittingComposition(attrLatestStr, nullptr);
|
||||
if (Destroyed()) {
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p IMEInputHandler::InsertTextAsCommittingComposition, "
|
||||
"destroyed by commiting composition for setting replacement range",
|
||||
this));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<IMEInputHandler> kungFuDeathGrip(this);
|
||||
|
||||
nsString str;
|
||||
nsCocoaUtils::GetStringForNSString([aAttrString string], str);
|
||||
|
||||
if (!IsIMEComposing()) {
|
||||
// If there is no selection and replacement range is specified, set the
|
||||
// range as selection.
|
||||
if (aReplacementRange && aReplacementRange->location != NSNotFound &&
|
||||
!NSEqualRanges(SelectedRange(), *aReplacementRange)) {
|
||||
NS_ENSURE_TRUE_VOID(SetSelection(*aReplacementRange));
|
||||
}
|
||||
|
||||
// XXXmnakano Probably, we shouldn't emulate composition in this case.
|
||||
// I think that we should just fire DOM3 textInput event if we implement it.
|
||||
nsCompositionEvent compStart(true, NS_COMPOSITION_START, mWidget);
|
||||
@ -2653,13 +2720,6 @@ IMEInputHandler::InsertTextAsCommittingComposition(
|
||||
OnStartIMEComposition();
|
||||
}
|
||||
|
||||
if (IgnoreIMECommit()) {
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p IMEInputHandler::InsertTextAsCommittingComposition, "
|
||||
"IgnoreIMECommit()=%s", this, TrueOrFalse(IgnoreIMECommit())));
|
||||
str.Truncate();
|
||||
}
|
||||
|
||||
NSRange range = NSMakeRange(0, str.Length());
|
||||
DispatchTextEvent(str, aAttrString, range, true);
|
||||
if (Destroyed()) {
|
||||
@ -2691,18 +2751,23 @@ IMEInputHandler::InsertTextAsCommittingComposition(
|
||||
|
||||
void
|
||||
IMEInputHandler::SetMarkedText(NSAttributedString* aAttrString,
|
||||
NSRange& aSelectedRange)
|
||||
NSRange& aSelectedRange,
|
||||
NSRange* aReplacementRange)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p IMEInputHandler::SetMarkedText, "
|
||||
"aAttrString=\"%s\", aSelectedRange={ location=%llu, length=%llu }, "
|
||||
"aReplacementRange=%p { location=%llu, length=%llu }, "
|
||||
"Destroyed()=%s, IgnoreIMEComposition()=%s, IsIMEComposing()=%s, "
|
||||
"mMarkedRange={ location=%llu, length=%llu }",
|
||||
this, GetCharacters([aAttrString string]),
|
||||
aSelectedRange.location, aSelectedRange.length, TrueOrFalse(Destroyed()),
|
||||
TrueOrFalse(IgnoreIMEComposition()), TrueOrFalse(IsIMEComposing()),
|
||||
aSelectedRange.location, aSelectedRange.length, aReplacementRange,
|
||||
aReplacementRange ? aReplacementRange->location : 0,
|
||||
aReplacementRange ? aReplacementRange->length : 0,
|
||||
TrueOrFalse(Destroyed()), TrueOrFalse(IgnoreIMEComposition()),
|
||||
TrueOrFalse(IsIMEComposing()),
|
||||
mMarkedRange.location, mMarkedRange.length));
|
||||
|
||||
if (Destroyed() || IgnoreIMEComposition()) {
|
||||
@ -2711,16 +2776,42 @@ IMEInputHandler::SetMarkedText(NSAttributedString* aAttrString,
|
||||
|
||||
nsRefPtr<IMEInputHandler> kungFuDeathGrip(this);
|
||||
|
||||
// First, commit current composition with the latest composition string if the
|
||||
// replacement range is different from marked range.
|
||||
if (IsIMEComposing() && aReplacementRange &&
|
||||
aReplacementRange->location != NSNotFound &&
|
||||
!NSEqualRanges(MarkedRange(), *aReplacementRange)) {
|
||||
NSString* latestStr =
|
||||
nsCocoaUtils::ToNSString(mLastDispatchedCompositionString);
|
||||
NSAttributedString* attrLatestStr =
|
||||
[[[NSAttributedString alloc] initWithString:latestStr] autorelease];
|
||||
bool ignoreIMECommit = mIgnoreIMECommit;
|
||||
mIgnoreIMECommit = false;
|
||||
InsertTextAsCommittingComposition(attrLatestStr, nullptr);
|
||||
mIgnoreIMECommit = ignoreIMECommit;
|
||||
if (Destroyed()) {
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p IMEInputHandler::SetMarkedText, "
|
||||
"destroyed by commiting composition for setting replacement range",
|
||||
this));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsString str;
|
||||
nsCocoaUtils::GetStringForNSString([aAttrString string], str);
|
||||
|
||||
mMarkedRange.length = str.Length();
|
||||
|
||||
if (!IsIMEComposing() && !str.IsEmpty()) {
|
||||
nsQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT,
|
||||
mWidget);
|
||||
DispatchEvent(selection);
|
||||
mMarkedRange.location = selection.mSucceeded ? selection.mReply.mOffset : 0;
|
||||
// If there is no selection and replacement range is specified, set the
|
||||
// range as selection.
|
||||
if (aReplacementRange && aReplacementRange->location != NSNotFound &&
|
||||
!NSEqualRanges(SelectedRange(), *aReplacementRange)) {
|
||||
NS_ENSURE_TRUE_VOID(SetSelection(*aReplacementRange));
|
||||
}
|
||||
|
||||
mMarkedRange.location = SelectedRange().location;
|
||||
|
||||
nsCompositionEvent compStart(true, NS_COMPOSITION_START, mWidget);
|
||||
InitCompositionEvent(compStart);
|
||||
@ -2794,14 +2885,20 @@ IMEInputHandler::ConversationIdentifier()
|
||||
}
|
||||
|
||||
NSAttributedString*
|
||||
IMEInputHandler::GetAttributedSubstringFromRange(NSRange& aRange)
|
||||
IMEInputHandler::GetAttributedSubstringFromRange(NSRange& aRange,
|
||||
NSRange* aActualRange)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p IMEInputHandler::GetAttributedSubstringFromRange, "
|
||||
"aRange={ location=%llu, length=%llu }, Destroyed()=%s",
|
||||
this, aRange.location, aRange.length, TrueOrFalse(Destroyed())));
|
||||
"aRange={ location=%llu, length=%llu }, aActualRange=%p, Destroyed()=%s",
|
||||
this, aRange.location, aRange.length, aActualRange,
|
||||
TrueOrFalse(Destroyed())));
|
||||
|
||||
if (aActualRange) {
|
||||
*aActualRange = NSMakeRange(NSNotFound, 0);
|
||||
}
|
||||
|
||||
if (Destroyed() || aRange.location == NSNotFound || aRange.length == 0) {
|
||||
return nil;
|
||||
@ -2816,11 +2913,12 @@ IMEInputHandler::GetAttributedSubstringFromRange(NSRange& aRange)
|
||||
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p IMEInputHandler::GetAttributedSubstringFromRange, "
|
||||
"textContent={ mSucceeded=%s, mReply.mString=\"%s\"",
|
||||
"textContent={ mSucceeded=%s, mReply={ mString=\"%s\", mOffset=%llu } }",
|
||||
this, TrueOrFalse(textContent.mSucceeded),
|
||||
NS_ConvertUTF16toUTF8(textContent.mReply.mString).get()));
|
||||
NS_ConvertUTF16toUTF8(textContent.mReply.mString).get(),
|
||||
textContent.mReply.mOffset));
|
||||
|
||||
if (!textContent.mSucceeded || textContent.mReply.mString.IsEmpty()) {
|
||||
if (!textContent.mSucceeded) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
@ -2828,6 +2926,10 @@ IMEInputHandler::GetAttributedSubstringFromRange(NSRange& aRange)
|
||||
NSAttributedString* result =
|
||||
[[[NSAttributedString alloc] initWithString:nsstr
|
||||
attributes:nil] autorelease];
|
||||
if (aActualRange) {
|
||||
aActualRange->location = textContent.mReply.mOffset;
|
||||
aActualRange->length = textContent.mReply.mString.Length();
|
||||
}
|
||||
return result;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
@ -2864,12 +2966,18 @@ IMEInputHandler::SelectedRange()
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
||||
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p IMEInputHandler::SelectedRange, Destroyed()=%s",
|
||||
this, TrueOrFalse(Destroyed())));
|
||||
("%p IMEInputHandler::SelectedRange, Destroyed()=%s, mSelectedRange={ "
|
||||
"location=%llu, length=%llu }",
|
||||
this, TrueOrFalse(Destroyed()), mSelectedRange.location,
|
||||
mSelectedRange.length));
|
||||
|
||||
NSRange range = NSMakeRange(NSNotFound, 0);
|
||||
if (Destroyed()) {
|
||||
return range;
|
||||
return mSelectedRange;
|
||||
}
|
||||
|
||||
if (mSelectedRange.location != NSNotFound) {
|
||||
MOZ_ASSERT(mIMEHasFocus);
|
||||
return mSelectedRange;
|
||||
}
|
||||
|
||||
nsRefPtr<IMEInputHandler> kungFuDeathGrip(this);
|
||||
@ -2884,30 +2992,42 @@ IMEInputHandler::SelectedRange()
|
||||
selection.mReply.mString.Length()));
|
||||
|
||||
if (!selection.mSucceeded) {
|
||||
return range;
|
||||
return mSelectedRange;
|
||||
}
|
||||
|
||||
if (mIMEHasFocus) {
|
||||
mSelectedRange.location = selection.mReply.mOffset;
|
||||
mSelectedRange.length = selection.mReply.mString.Length();
|
||||
return mSelectedRange;
|
||||
}
|
||||
|
||||
return NSMakeRange(selection.mReply.mOffset,
|
||||
selection.mReply.mString.Length());
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRange(0, 0));
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(mSelectedRange);
|
||||
}
|
||||
|
||||
NSRect
|
||||
IMEInputHandler::FirstRectForCharacterRange(NSRange& aRange)
|
||||
IMEInputHandler::FirstRectForCharacterRange(NSRange& aRange,
|
||||
NSRange* aActualRange)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
||||
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p IMEInputHandler::FirstRectForCharacterRange, Destroyed()=%s"
|
||||
"aRange={ location=%llu, length=%llu }",
|
||||
this, TrueOrFalse(Destroyed()), aRange.location, aRange.length));
|
||||
("%p IMEInputHandler::FirstRectForCharacterRange, Destroyed()=%s, "
|
||||
"aRange={ location=%llu, length=%llu }, aActualRange=%p }",
|
||||
this, TrueOrFalse(Destroyed()), aRange.location, aRange.length,
|
||||
aActualRange));
|
||||
|
||||
// XXX this returns first character rect or caret rect, it is limitation of
|
||||
// now. We need more work for returns first line rect. But current
|
||||
// implementation is enough for IMEs.
|
||||
|
||||
NSRect rect;
|
||||
NSRect rect = NSMakeRect(0.0, 0.0, 0.0, 0.0);
|
||||
NSRange actualRange = NSMakeRange(NSNotFound, 0);
|
||||
if (aActualRange) {
|
||||
*aActualRange = actualRange;
|
||||
}
|
||||
if (Destroyed() || aRange.location == NSNotFound) {
|
||||
return rect;
|
||||
}
|
||||
@ -2922,6 +3042,8 @@ IMEInputHandler::FirstRectForCharacterRange(NSRange& aRange)
|
||||
DispatchEvent(charRect);
|
||||
if (charRect.mSucceeded) {
|
||||
r = charRect.mReply.mRect;
|
||||
actualRange.location = charRect.mReply.mOffset;
|
||||
actualRange.length = charRect.mReply.mString.Length();
|
||||
} else {
|
||||
useCaretRect = true;
|
||||
}
|
||||
@ -2936,6 +3058,8 @@ IMEInputHandler::FirstRectForCharacterRange(NSRange& aRange)
|
||||
}
|
||||
r = caretRect.mReply.mRect;
|
||||
r.width = 0;
|
||||
actualRange.location = caretRect.mReply.mOffset;
|
||||
actualRange.length = 0;
|
||||
}
|
||||
|
||||
nsIWidget* rootWidget = mWidget->GetTopLevelWidget();
|
||||
@ -2950,11 +3074,17 @@ IMEInputHandler::FirstRectForCharacterRange(NSRange& aRange)
|
||||
rect = [rootView convertRect:rect toView:nil];
|
||||
rect.origin = [rootWindow convertBaseToScreen:rect.origin];
|
||||
|
||||
if (aActualRange) {
|
||||
*aActualRange = actualRange;
|
||||
}
|
||||
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p IMEInputHandler::FirstRectForCharacterRange, "
|
||||
"useCaretRect=%s rect={ x=%f, y=%f, width=%f, height=%f }",
|
||||
"useCaretRect=%s rect={ x=%f, y=%f, width=%f, height=%f }, "
|
||||
"actualRange={ location=%llu, length=%llu }",
|
||||
this, TrueOrFalse(useCaretRect), rect.origin.x, rect.origin.y,
|
||||
rect.size.width, rect.size.height));
|
||||
rect.size.width, rect.size.height, actualRange.location,
|
||||
actualRange.length));
|
||||
|
||||
return rect;
|
||||
|
||||
@ -3011,12 +3141,14 @@ IMEInputHandler::IMEInputHandler(nsChildView* aWidget,
|
||||
mPendingMethods(0), mIMECompositionString(nullptr),
|
||||
mIsIMEComposing(false), mIsIMEEnabled(true),
|
||||
mIsASCIICapableOnly(false), mIgnoreIMECommit(false),
|
||||
mIsInFocusProcessing(false)
|
||||
mIsInFocusProcessing(false), mIMEHasFocus(false)
|
||||
{
|
||||
InitStaticMembers();
|
||||
|
||||
mMarkedRange.location = NSNotFound;
|
||||
mMarkedRange.length = 0;
|
||||
mSelectedRange.location = NSNotFound;
|
||||
mSelectedRange.length = 0;
|
||||
}
|
||||
|
||||
IMEInputHandler::~IMEInputHandler()
|
||||
@ -3038,6 +3170,9 @@ IMEInputHandler::OnFocusChangeInGecko(bool aFocus)
|
||||
"sFocusedIMEHandler=%p",
|
||||
this, TrueOrFalse(aFocus), TrueOrFalse(Destroyed()), sFocusedIMEHandler));
|
||||
|
||||
mSelectedRange.location = NSNotFound; // Marking dirty
|
||||
mIMEHasFocus = aFocus;
|
||||
|
||||
// This is called when the native focus is changed and when the native focus
|
||||
// isn't changed but the focus is changed in Gecko.
|
||||
if (!aFocus) {
|
||||
@ -3049,11 +3184,9 @@ IMEInputHandler::OnFocusChangeInGecko(bool aFocus)
|
||||
sFocusedIMEHandler = this;
|
||||
mIsInFocusProcessing = true;
|
||||
|
||||
// We need to reset the IME's window level by the current focused view of
|
||||
// Gecko. It may be different from mView. However, we cannot get the
|
||||
// new focused view here because the focus change process in Gecko hasn't
|
||||
// been finished yet. So, we should post the job to the todo list.
|
||||
mPendingMethods |= kResetIMEWindowLevel;
|
||||
// We need to notify IME of focus change in Gecko as native focus change
|
||||
// because the window level of the focused element in Gecko may be changed.
|
||||
mPendingMethods |= kNotifyIMEOfFocusChangeInGecko;
|
||||
ResetTimer();
|
||||
}
|
||||
|
||||
@ -3082,6 +3215,9 @@ IMEInputHandler::OnDestroyWidget(nsChildView* aDestroyingWidget)
|
||||
OnEndIMEComposition();
|
||||
}
|
||||
|
||||
mSelectedRange.location = NSNotFound; // Marking dirty
|
||||
mIMEHasFocus = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4113,6 +4249,51 @@ TextInputHandlerBase::SynthesizeNativeKeyEvent(
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
NSInteger
|
||||
TextInputHandlerBase::GetWindowLevel()
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
||||
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p TextInputHandlerBase::GetWindowLevel, Destryoed()=%s",
|
||||
this, TrueOrFalse(Destroyed())));
|
||||
|
||||
if (Destroyed()) {
|
||||
return NSNormalWindowLevel;
|
||||
}
|
||||
|
||||
// When an <input> element on a XUL <panel> is focused, the actual focused view
|
||||
// is the panel's parent view (mView). But the editor is displayed on the
|
||||
// popped-up widget's view (editorView). We want the latter's window level.
|
||||
NSView<mozView>* editorView = mWidget->GetEditorView();
|
||||
NS_ENSURE_TRUE(editorView, NSNormalWindowLevel);
|
||||
NSInteger windowLevel = [[editorView window] level];
|
||||
|
||||
PR_LOG(gLog, PR_LOG_ALWAYS,
|
||||
("%p TextInputHandlerBase::GetWindowLevel, windowLevel=%s (%X)",
|
||||
this, GetWindowLevelName(windowLevel), windowLevel));
|
||||
|
||||
return windowLevel;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSNormalWindowLevel);
|
||||
}
|
||||
|
||||
bool
|
||||
TextInputHandlerBase::SetSelection(NSRange& aRange)
|
||||
{
|
||||
MOZ_ASSERT(!Destroyed());
|
||||
|
||||
nsRefPtr<TextInputHandlerBase> kungFuDeathGrip(this);
|
||||
nsSelectionEvent selectionEvent(true, NS_SELECTION_SET, mWidget);
|
||||
selectionEvent.mOffset = aRange.location;
|
||||
selectionEvent.mLength = aRange.length;
|
||||
selectionEvent.mReversed = false;
|
||||
selectionEvent.mExpandToClusterBoundary = false;
|
||||
DispatchEvent(selectionEvent);
|
||||
NS_ENSURE_TRUE(selectionEvent.mSucceeded, false);
|
||||
return !Destroyed();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
TextInputHandlerBase::IsPrintableChar(PRUnichar aChar)
|
||||
{
|
||||
|
@ -215,7 +215,7 @@ typedef NSInteger NSEventGestureAxis;
|
||||
#ifdef ACCESSIBILITY
|
||||
mozAccessible,
|
||||
#endif
|
||||
mozView, NSTextInput>
|
||||
mozView, NSTextInput, NSTextInputClient>
|
||||
{
|
||||
@private
|
||||
// the nsChildView that created the view. It retains this NSView, so
|
||||
@ -480,6 +480,7 @@ public:
|
||||
NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
|
||||
const InputContextAction& aAction);
|
||||
NS_IMETHOD_(InputContext) GetInputContext();
|
||||
virtual nsIMEUpdatePreference GetIMEUpdatePreference() MOZ_OVERRIDE;
|
||||
NS_IMETHOD GetToggledKeyState(uint32_t aKeyCode,
|
||||
bool* aLEDState);
|
||||
|
||||
|
@ -1811,6 +1811,9 @@ nsChildView::NotifyIME(NotificationToIME aNotification)
|
||||
NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
|
||||
mTextInputHandler->OnFocusChangeInGecko(false);
|
||||
return NS_OK;
|
||||
case NOTIFY_IME_OF_SELECTION_CHANGE:
|
||||
NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
|
||||
mTextInputHandler->OnSelectionChange();
|
||||
default:
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
@ -1880,6 +1883,13 @@ nsChildView::GetInputContext()
|
||||
return mInputContext;
|
||||
}
|
||||
|
||||
nsIMEUpdatePreference
|
||||
nsChildView::GetIMEUpdatePreference()
|
||||
{
|
||||
return nsIMEUpdatePreference(nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE,
|
||||
false);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsChildView::GetToggledKeyState(uint32_t aKeyCode,
|
||||
bool* aLEDState)
|
||||
{
|
||||
@ -4946,7 +4956,7 @@ static int32_t RoundUp(double aDouble)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
NS_ENSURE_TRUE(mGeckoChild, );
|
||||
NS_ENSURE_TRUE_VOID(mGeckoChild);
|
||||
|
||||
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
||||
|
||||
@ -4988,7 +4998,7 @@ static int32_t RoundUp(double aDouble)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
NS_ENSURE_TRUE(mTextInputHandler, );
|
||||
NS_ENSURE_TRUE_VOID(mTextInputHandler);
|
||||
|
||||
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
||||
|
||||
@ -5082,6 +5092,75 @@ static int32_t RoundUp(double aDouble)
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
// NSTextInputClient implementation
|
||||
|
||||
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
NS_ENSURE_TRUE_VOID(mGeckoChild);
|
||||
|
||||
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
||||
|
||||
NSAttributedString* attrStr;
|
||||
if ([aString isKindOfClass:[NSAttributedString class]]) {
|
||||
attrStr = static_cast<NSAttributedString*>(aString);
|
||||
} else {
|
||||
attrStr = [[[NSAttributedString alloc] initWithString:aString] autorelease];
|
||||
}
|
||||
|
||||
mTextInputHandler->InsertText(attrStr, &replacementRange);
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
|
||||
- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange
|
||||
replacementRange:(NSRange)replacementRange
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
NS_ENSURE_TRUE_VOID(mTextInputHandler);
|
||||
|
||||
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
||||
|
||||
NSAttributedString* attrStr;
|
||||
if ([aString isKindOfClass:[NSAttributedString class]]) {
|
||||
attrStr = static_cast<NSAttributedString*>(aString);
|
||||
} else {
|
||||
attrStr = [[[NSAttributedString alloc] initWithString:aString] autorelease];
|
||||
}
|
||||
|
||||
mTextInputHandler->SetMarkedText(attrStr, selectedRange, &replacementRange);
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
|
||||
- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)aRange
|
||||
actualRange:(NSRangePointer)actualRange
|
||||
{
|
||||
NS_ENSURE_TRUE(mTextInputHandler, nil);
|
||||
return mTextInputHandler->GetAttributedSubstringFromRange(aRange,
|
||||
actualRange);
|
||||
}
|
||||
|
||||
- (NSRect)firstRectForCharacterRange:(NSRange)aRange
|
||||
actualRange:(NSRangePointer)actualRange
|
||||
{
|
||||
NS_ENSURE_TRUE(mTextInputHandler, NSMakeRect(0.0, 0.0, 0.0, 0.0));
|
||||
return mTextInputHandler->FirstRectForCharacterRange(aRange, actualRange);
|
||||
}
|
||||
|
||||
- (NSInteger)windowLevel
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
||||
|
||||
NS_ENSURE_TRUE(mTextInputHandler, [[self window] level]);
|
||||
return mTextInputHandler->GetWindowLevel();
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSNormalWindowLevel);
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
#ifdef __LP64__
|
||||
|
@ -359,6 +359,9 @@ nsCocoaUtils::GetStringForNSString(const NSString *aSrc, nsAString& aDist)
|
||||
NSString*
|
||||
nsCocoaUtils::ToNSString(const nsAString& aString)
|
||||
{
|
||||
if (aString.IsEmpty()) {
|
||||
return [NSString string];
|
||||
}
|
||||
return [NSString stringWithCharacters:aString.BeginReading()
|
||||
length:aString.Length()];
|
||||
}
|
||||
|
@ -191,13 +191,14 @@ enum nsTopLevelWidgetZPlacement { // for PlaceBehind()
|
||||
/**
|
||||
* Preference for receiving IME updates
|
||||
*
|
||||
* If mWantUpdates is true, nsTextStateManager will observe text change and
|
||||
* selection change and call nsIWidget::NotifyIMEOfTextChange() and
|
||||
* nsIWidget::NotifyIME(NOTIFY_IME_OF_SELECTION_CHANGE). The observing cost is
|
||||
* very expensive.
|
||||
* If mWantUpdates is not NOTIFY_NOTHING, nsTextStateManager will observe text
|
||||
* change and/or selection change and call nsIWidget::NotifyIMEOfTextChange()
|
||||
* and/or nsIWidget::NotifyIME(NOTIFY_IME_OF_SELECTION_CHANGE).
|
||||
* Please note that the text change observing cost is very expensive especially
|
||||
* on an HTML editor has focus.
|
||||
* If the IME implementation on a particular platform doesn't care about
|
||||
* NotifyIMEOfTextChange and NotifyIME(NOTIFY_IME_OF_SELECTION_CHANGE), they
|
||||
* should set mWantUpdates to false to avoid the cost.
|
||||
* NotifyIMEOfTextChange() and/or NotifyIME(NOTIFY_IME_OF_SELECTION_CHANGE),
|
||||
* they should set mWantUpdates to NOTIFY_NOTHING to avoid the cost.
|
||||
*
|
||||
* If mWantHints is true, PuppetWidget will forward the content of text fields
|
||||
* to the chrome process to be cached. This way we return the cached content
|
||||
@ -208,15 +209,25 @@ enum nsTopLevelWidgetZPlacement { // for PlaceBehind()
|
||||
*/
|
||||
struct nsIMEUpdatePreference {
|
||||
|
||||
typedef int8_t Notifications;
|
||||
|
||||
enum
|
||||
{
|
||||
NOTIFY_NOTHING = 0x0000,
|
||||
NOTIFY_SELECTION_CHANGE = 0x0001,
|
||||
NOTIFY_TEXT_CHANGE = 0x0002
|
||||
};
|
||||
|
||||
nsIMEUpdatePreference()
|
||||
: mWantUpdates(false), mWantHints(false)
|
||||
: mWantUpdates(NOTIFY_NOTHING), mWantHints(false)
|
||||
{
|
||||
}
|
||||
nsIMEUpdatePreference(bool aWantUpdates, bool aWantHints)
|
||||
nsIMEUpdatePreference(Notifications aWantUpdates, bool aWantHints)
|
||||
: mWantUpdates(aWantUpdates), mWantHints(aWantHints)
|
||||
{
|
||||
}
|
||||
bool mWantUpdates;
|
||||
|
||||
Notifications mWantUpdates;
|
||||
bool mWantHints;
|
||||
};
|
||||
|
||||
|
@ -221,7 +221,7 @@ IMEHandler::GetUpdatePreference()
|
||||
}
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
return nsIMEUpdatePreference(false, false);
|
||||
return nsIMEUpdatePreference();
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -2920,13 +2920,16 @@ nsTextStore::OnFocusChange(bool aGotFocus,
|
||||
nsIMEUpdatePreference
|
||||
nsTextStore::GetIMEUpdatePreference()
|
||||
{
|
||||
bool hasFocus = false;
|
||||
int8_t notifications = nsIMEUpdatePreference::NOTIFY_NOTHING;
|
||||
if (sTsfThreadMgr && sTsfTextStore && sTsfTextStore->mDocumentMgr) {
|
||||
nsRefPtr<ITfDocumentMgr> docMgr;
|
||||
sTsfThreadMgr->GetFocus(getter_AddRefs(docMgr));
|
||||
hasFocus = (docMgr == sTsfTextStore->mDocumentMgr);
|
||||
if (docMgr == sTsfTextStore->mDocumentMgr) {
|
||||
notifications = (nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE |
|
||||
nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE);
|
||||
}
|
||||
}
|
||||
return nsIMEUpdatePreference(hasFocus, false);
|
||||
return nsIMEUpdatePreference(notifications, false);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -456,13 +456,15 @@ PuppetWidget::NotifyIMEOfFocusChange(bool aFocus)
|
||||
}
|
||||
|
||||
uint32_t chromeSeqno;
|
||||
mIMEPreference.mWantUpdates = false;
|
||||
mIMEPreference.mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING;
|
||||
mIMEPreference.mWantHints = false;
|
||||
if (!mTabChild->SendNotifyIMEFocus(aFocus, &mIMEPreference, &chromeSeqno))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (aFocus) {
|
||||
if (mIMEPreference.mWantUpdates && mIMEPreference.mWantHints) {
|
||||
if ((mIMEPreference.mWantUpdates &
|
||||
nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE) &&
|
||||
mIMEPreference.mWantHints) {
|
||||
NotifyIMEOfSelectionChange(); // Update selection
|
||||
}
|
||||
} else {
|
||||
@ -500,7 +502,7 @@ PuppetWidget::NotifyIMEOfTextChange(uint32_t aStart,
|
||||
mTabChild->SendNotifyIMETextHint(queryEvent.mReply.mString);
|
||||
}
|
||||
}
|
||||
if (mIMEPreference.mWantUpdates) {
|
||||
if (mIMEPreference.mWantUpdates & nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE) {
|
||||
mTabChild->SendNotifyIMETextChange(aStart, aEnd, aNewEnd);
|
||||
}
|
||||
return NS_OK;
|
||||
@ -516,7 +518,8 @@ PuppetWidget::NotifyIMEOfSelectionChange()
|
||||
if (!mTabChild)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (mIMEPreference.mWantUpdates) {
|
||||
if (mIMEPreference.mWantUpdates &
|
||||
nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE) {
|
||||
nsEventStatus status;
|
||||
nsQueryContentEvent queryEvent(true, NS_QUERY_SELECTED_TEXT, this);
|
||||
InitEvent(queryEvent, nullptr);
|
||||
|
@ -181,7 +181,7 @@ public:
|
||||
virtual bool ComputeShouldAccelerate(bool aDefault);
|
||||
NS_IMETHOD GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD NotifyIMEOfTextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsIMEUpdatePreference GetIMEUpdatePreference() { return nsIMEUpdatePreference(false, false); }
|
||||
virtual nsIMEUpdatePreference GetIMEUpdatePreference() MOZ_OVERRIDE { return nsIMEUpdatePreference(); }
|
||||
NS_IMETHOD OnDefaultButtonLoaded(const nsIntRect &aButtonRect) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD OverrideSystemMouseScrollSpeed(double aOriginalDeltaX,
|
||||
double aOriginalDeltaY,
|
||||
|
@ -85,8 +85,6 @@
|
||||
|
||||
#define NS_APP_INSTALL_CLEANUP_DIR "XPIClnupD" //location of xpicleanup.dat xpicleanup.exe
|
||||
|
||||
#define NS_APP_STORAGE_50_FILE "UStor" // sqlite database used as mozStorage profile db
|
||||
|
||||
#define NS_APP_INDEXEDDB_PARENT_DIR "indexedDBPDir"
|
||||
|
||||
#define NS_APP_PERMISSION_PARENT_DIR "permissionDBPDir"
|
||||
|
Loading…
Reference in New Issue
Block a user