merge fx-team to mozilla-central

This commit is contained in:
Carsten "Tomcat" Book 2014-02-20 12:55:32 +01:00
commit f3825c1c94
28 changed files with 425 additions and 58 deletions

View File

@ -783,7 +783,6 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
.urlbar-input-box {
-moz-margin-start: 0;
min-width: 4em;
}
.urlbar-history-dropmarker {

View File

@ -1730,7 +1730,6 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
.urlbar-input-box {
-moz-margin-start: 0;
padding: 3px 0 2px;
min-width: 4em;
}
.urlbar-history-dropmarker {

View File

@ -973,7 +973,6 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder,
.urlbar-input-box {
-moz-margin-start: 0;
min-width: 4em;
}
#urlbar-icons {

View File

@ -2180,6 +2180,7 @@ abstract public class BrowserApp extends GeckoApp
}
bookmark.setEnabled(!AboutPages.isAboutReader(tab.getURL()));
bookmark.setVisible(!GeckoProfile.get(this).inGuestMode());
bookmark.setCheckable(true);
bookmark.setChecked(tab.isBookmark());
bookmark.setIcon(tab.isBookmark() ? R.drawable.ic_menu_bookmark_remove : R.drawable.ic_menu_bookmark_add);

View File

@ -702,6 +702,8 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
// extend it to look like
//
// SELECT COALESCE((SELECT ...), 0) | COALESCE(...) | ...
final boolean includeDeleted = shouldShowDeleted(uri);
final String query = "SELECT COALESCE(SUM(flag), 0) AS flags " +
"FROM ( SELECT DISTINCT CASE" +
" WHEN " + Bookmarks.PARENT + " = " + Bookmarks.FIXED_READING_LIST_ID +
@ -712,7 +714,9 @@ public class BrowserProvider extends TransactionalProvider<BrowserDatabaseHelper
" ELSE " + Bookmarks.FLAG_BOOKMARK +
" END flag " +
"FROM " + TABLE_BOOKMARKS + " WHERE " + Bookmarks.URL + " = ? " +
"FROM " + TABLE_BOOKMARKS + " WHERE " +
Bookmarks.URL + " = ? " +
(includeDeleted ? "" : ("AND " + Bookmarks.IS_DELETED + " = 0")) +
")";
return db.rawQuery(query, selectionArgs);

View File

@ -103,7 +103,7 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
// Don't add padding to color picker views
if (input.canApplyInputStyle()) {
view.setPadding(mInputPaddingSize, 0, mInputPaddingSize, 0);
}
}
return view;
}
@ -314,6 +314,20 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
mSelected = null;
}
/* Wraps an input in a linearlayout. We do this so that we can set padding that appears outside the background
* drawable for the view.
*/
private View wrapInput(final PromptInput input) {
final LinearLayout linearLayout = new LinearLayout(mContext);
linearLayout.setOrientation(LinearLayout.VERTICAL);
applyInputStyle(linearLayout, input);
linearLayout.addView(input.getView(mContext));
return linearLayout;
}
/* Add the requested input elements to the dialog.
*
* @param builder
@ -333,18 +347,18 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
boolean scrollable = false; // If any of the innuts are scrollable, we won't wrap this in a ScrollView
if (length == 1) {
root = mInputs[0].getView(mContext);
applyInputStyle(root, mInputs[0]);
root = wrapInput(mInputs[0]);
scrollable |= mInputs[0].getScrollable();
} else if (length > 1) {
LinearLayout linearLayout = new LinearLayout(mContext);
linearLayout.setOrientation(LinearLayout.VERTICAL);
for (int i = 0; i < length; i++) {
View content = mInputs[i].getView(mContext);
applyInputStyle(content, mInputs[i]);
View content = wrapInput(mInputs[i]);
linearLayout.addView(content);
scrollable |= mInputs[i].getScrollable();
}
root = linearLayout;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

View File

@ -0,0 +1,20 @@
<!--
Copyright 2012 Roman Nurik
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/toast_button_pressed" android:state_pressed="true"/>
<item android:drawable="@drawable/toast_button_pressed" android:state_focused="true"/>
<item android:drawable="@android:color/transparent"/>
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 B

View File

@ -28,7 +28,7 @@
<item name="topDivider">true</item>
</style>
<style name="Widget.HomeGridView" parent="Widget.GridView">
<style name="Widget.TopSitesGridView" parent="Widget.GridView">
<item name="android:paddingLeft">55dp</item>
<item name="android:paddingRight">55dp</item>
<item name="android:paddingBottom">30dp</item>
@ -36,8 +36,6 @@
<item name="android:verticalSpacing">20dp</item>
</style>
<style name="Widget.TopSitesGridView" parent="Widget.HomeGridView" />
<style name="Widget.Home.HistoryListView">
<item name="android:paddingLeft">50dp</item>
<item name="android:paddingRight">100dp</item>

View File

@ -77,7 +77,7 @@
<item name="topDivider">true</item>
</style>
<style name="Widget.HomeGridView" parent="Widget.GridView">
<style name="Widget.TopSitesGridView" parent="Widget.GridView">
<item name="android:paddingLeft">5dp</item>
<item name="android:paddingRight">5dp</item>
<item name="android:paddingBottom">30dp</item>
@ -85,8 +85,6 @@
<item name="android:verticalSpacing">10dp</item>
</style>
<style name="Widget.TopSitesGridView" parent="Widget.HomeGridView" />
<style name="Widget.TopSitesListView" parent="Widget.BookmarksListView">
<item name="topDivider">false</item>
</style>

View File

@ -0,0 +1,36 @@
<?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/. -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="ToastMessage">
<item name="android:textSize">16sp</item>
<item name="android:fontFamily">sans-serif-condensed</item>
<item name="android:textColor">#ffffff</item>
<item name="android:shadowColor">#BB000000</item>
<item name="android:shadowRadius">2.75</item>
<item name="android:layout_width">0dp</item>
<item name="android:layout_weight">1</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_gravity">center_vertical</item>
<item name="android:paddingLeft">@dimen/toast_button_padding</item>
<item name="android:layout_marginLeft">8dp</item>
</style>
<style name="ToastButton">
<item name="android:background">@drawable/toast_button</item>
<item name="android:textSize">16sp</item>
<item name="android:fontFamily">sans-serif-condensed</item>
<item name="android:textColor">#ffffff</item>
<item name="android:shadowColor">#BB000000</item>
<item name="android:shadowRadius">2.75</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">match_parent</item>
<item name="android:paddingLeft">16dp</item>
<item name="android:maxWidth">160dp</item>
<item name="android:layout_gravity">center_vertical</item>
</style>
</resources>

View File

@ -5,7 +5,7 @@
<resources>
<style name="Widget.HomeGridView" parent="Widget.GridView">
<style name="Widget.TopSitesGridView" parent="Widget.GridView">
<item name="android:paddingLeft">55dp</item>
<item name="android:paddingRight">55dp</item>
<item name="android:paddingBottom">30dp</item>
@ -13,8 +13,6 @@
<item name="android:verticalSpacing">20dp</item>
</style>
<style name="Widget.TopSitesGridView" parent="Widget.HomeGridView" />
<style name="Widget.Home.HistoryListView">
<item name="android:paddingLeft">50dp</item>
<item name="android:paddingRight">100dp</item>

View File

@ -35,9 +35,6 @@
<!-- Default style for the TopSitesGridItemView -->
<attr name="topSitesGridItemViewStyle" format="reference" />
<!-- Default style for the HomeGridView -->
<attr name="homeGridViewStyle" format="reference" />
<!-- Style for the PanelGridView -->
<attr name="panelGridViewStyle" format="reference" />

View File

@ -145,14 +145,12 @@
<item name="android:ellipsize">end</item>
</style>
<style name="Widget.HomeGridView" parent="Widget.GridView">
<style name="Widget.TopSitesGridView" parent="Widget.GridView">
<item name="android:padding">7dp</item>
<item name="android:horizontalSpacing">0dp</item>
<item name="android:verticalSpacing">7dp</item>
</style>
<style name="Widget.TopSitesGridView" parent="Widget.HomeGridView" />
<style name="Widget.TopSitesGridItemView">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">fill_parent</item>
@ -169,6 +167,7 @@
<item name="android:columnWidth">@dimen/panel_grid_view_column_width</item>
<item name="android:horizontalSpacing">2dp</item>
<item name="android:verticalSpacing">2dp</item>
<item name="android:drawSelectorOnTop">true</item>
</style>
<style name="Widget.BookmarkItemView" parent="Widget.TwoLineRow"/>

View File

@ -58,7 +58,9 @@ var SelectionHandler = {
Services.obs.addObserver(this, "TextSelection:Position", false);
Services.obs.addObserver(this, "TextSelection:End", false);
Services.obs.addObserver(this, "TextSelection:Action", false);
BrowserApp.deck.addEventListener("compositionend", this, false);
BrowserApp.deck.addEventListener("pagehide", this, false);
BrowserApp.deck.addEventListener("blur", this, true);
},
_removeObservers: function sh_removeObservers() {
@ -69,13 +71,25 @@ var SelectionHandler = {
Services.obs.removeObserver(this, "TextSelection:Position");
Services.obs.removeObserver(this, "TextSelection:End");
Services.obs.removeObserver(this, "TextSelection:Action");
BrowserApp.deck.removeEventListener("compositionend", this);
BrowserApp.deck.removeEventListener("pagehide", this);
BrowserApp.deck.removeEventListener("blur", this);
},
observe: function sh_observe(aSubject, aTopic, aData) {
switch (aTopic) {
// Update caret position on keyboard activity
case "TextSelection:UpdateCaretPos":
// Generated by IME close, autoCorrection / styling
this._positionHandles();
break;
case "Gesture:SingleTap": {
if (this._activeType == this.TYPE_CURSOR) {
if (this._activeType == this.TYPE_SELECTION) {
let data = JSON.parse(aData);
if (!this._pointInSelection(data.x, data.y))
this._closeSelection();
} else if (this._activeType == this.TYPE_CURSOR) {
// attachCaret() is called in the "Gesture:SingleTap" handler in BrowserEventHandler
// We're guaranteed to call this first, because this observer was added last
this._deactivate();
@ -171,16 +185,18 @@ var SelectionHandler = {
handleEvent: function sh_handleEvent(aEvent) {
switch (aEvent.type) {
case "pagehide":
// We only add keydown and blur listeners for TYPE_CURSOR
case "keydown":
case "blur":
this._closeSelection();
break;
// Update caret position on keyboard activity
case "keyup":
// Not generated by Swiftkeyboard
case "compositionupdate":
case "compositionend":
// compositionend messages normally terminate caret display
if (this._activeType == this.TYPE_CURSOR && !this._ignoreCompositionChanges) {
this._deactivate();
// Generated by SwiftKeyboard, et. al.
if (!this._ignoreCompositionChanges) {
this._positionHandles();
}
break;
}
@ -493,10 +509,14 @@ var SelectionHandler = {
!((aElement instanceof HTMLInputElement && aElement.mozIsTextField(false)) ||
(aElement instanceof HTMLTextAreaElement)))
return;
this._initTargetInfo(aElement);
this._contentWindow.addEventListener("keydown", this, false);
this._contentWindow.addEventListener("blur", this, true);
// Caret-specific observer/listeners
Services.obs.addObserver(this, "TextSelection:UpdateCaretPos", false);
BrowserApp.deck.addEventListener("keyup", this, false);
BrowserApp.deck.addEventListener("compositionupdate", this, false);
BrowserApp.deck.addEventListener("compositionend", this, false);
this._activeType = this.TYPE_CURSOR;
this._positionHandles();
@ -504,9 +524,14 @@ var SelectionHandler = {
this._sendMessage("TextSelection:ShowHandles", [this.HANDLE_TYPE_MIDDLE]);
},
// Target initialization for both TYPE_CURSOR and TYPE_SELECTION
_initTargetInfo: function sh_initTargetInfo(aElement) {
this._targetElement = aElement;
if (aElement instanceof Ci.nsIDOMNSEditableElement) {
// Blur the targetElement to force IME code to undo previous style compositions
// (visible underlines / etc generated by autoCorrection, autoSuggestion)
aElement.blur();
// Ensure targetElement is now focused normally
aElement.focus();
}
@ -514,7 +539,6 @@ var SelectionHandler = {
this._isRTL = (this._contentWindow.getComputedStyle(aElement, "").direction == "rtl");
this._addObservers();
this._contentWindow.addEventListener("pagehide", this, false);
},
_getSelection: function sh_getSelection() {
@ -745,20 +769,26 @@ var SelectionHandler = {
},
_deactivate: function sh_deactivate() {
this._activeType = this.TYPE_NONE;
sendMessageToJava({ type: "TextSelection:HideHandles" });
this._removeObservers();
this._contentWindow.removeEventListener("pagehide", this, false);
this._contentWindow.removeEventListener("keydown", this, false);
this._contentWindow.removeEventListener("blur", this, true);
// Only observed for caret positioning
if (this._activeType == this.TYPE_CURSOR) {
Services.obs.removeObserver(this, "TextSelection:UpdateCaretPos");
BrowserApp.deck.removeEventListener("keyup", this);
BrowserApp.deck.removeEventListener("compositionupdate", this);
BrowserApp.deck.removeEventListener("compositionend", this);
}
this._contentWindow = null;
this._targetElement = null;
this._isRTL = false;
this._cache = null;
this._ignoreSelectionChanges = false;
this._ignoreCompositionChanges = false;
this._activeType = this.TYPE_NONE;
},
_getViewOffset: function sh_getViewOffset() {

View File

@ -572,7 +572,7 @@ var BrowserApp = {
});
NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.bookmarkLink"),
NativeWindow.contextmenus.linkBookmarkableContext,
NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.linkBookmarkableContext),
function(aTarget) {
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
let title = aTarget.textContent || aTarget.title || url;

View File

@ -139,6 +139,22 @@ InternalPrompt.prototype = {
return aPrompt;
},
addTextbox: function(prompt, value, autofocus, hint) {
prompt.addTextbox({
value: (value !== null) ? value : "",
autofocus: autofocus,
hint: hint
});
},
addPassword: function(prompt, value, autofocus, hint) {
prompt.addPassword({
value: (value !== null) ? value : "",
autofocus: autofocus,
hint: hint
});
},
/* Shows a native prompt, and then spins the event loop for this thread while we wait
* for a response
*/
@ -288,10 +304,7 @@ InternalPrompt.prototype = {
nsIPrompt_prompt: function nsIPrompt_prompt(aTitle, aText, aValue, aCheckMsg, aCheckState) {
let p = this._getPrompt(aTitle, aText, null, aCheckMsg, aCheckState);
p.setHint("prompt");
p.addTextbox({
value: aValue.value,
autofocus: true
});
this.addTextbox(p, aValue.value, true);
this.addCheckbox(p, aCheckMsg, aCheckState);
let data = this.showPrompt(p);
@ -306,11 +319,7 @@ InternalPrompt.prototype = {
nsIPrompt_promptPassword: function nsIPrompt_promptPassword(
aTitle, aText, aPassword, aCheckMsg, aCheckState) {
let p = this._getPrompt(aTitle, aText, null);
p.addPassword({
value: aPassword.value || "",
autofocus: true,
hint: PromptUtils.getLocaleString("password", "passwdmgr")
});
this.addPassword(p, aPassword.value, true, PromptUtils.getLocaleString("password", "passwdmgr"));
this.addCheckbox(p, aCheckMsg, aCheckState);
let data = this.showPrompt(p);
@ -325,14 +334,8 @@ InternalPrompt.prototype = {
nsIPrompt_promptUsernameAndPassword: function nsIPrompt_promptUsernameAndPassword(
aTitle, aText, aUsername, aPassword, aCheckMsg, aCheckState) {
let p = this._getPrompt(aTitle, aText, null);
p.addTextbox({
value: aUsername.value,
autofocus: true,
hint: PromptUtils.getLocaleString("username", "passwdmgr")
}).addPassword({
value: aPassword.value,
hint: PromptUtils.getLocaleString("password", "passwdmgr")
});
this.addTextbox(p, aUsername.value, true, PromptUtils.getLocaleString("username", "passwdmgr"));
this.addPassword(p, aPassword.value, true, PromptUtils.getLocaleString("password", "passwdmgr"));
this.addCheckbox(p, aCheckMsg, aCheckState);
let data = this.showPrompt(p);

View File

@ -128,6 +128,7 @@
@BINPATH@/components/content_canvas.xpt
@BINPATH@/components/content_htmldoc.xpt
@BINPATH@/components/content_html.xpt
@BINPATH@/components/content_webrtc.xpt
@BINPATH@/components/content_xslt.xpt
@BINPATH@/components/cookie.xpt
@BINPATH@/components/directory.xpt

View File

@ -67,6 +67,7 @@ BuiltinProvider.prototype = {
"devtools/touch-events": "resource://gre/modules/devtools/touch-events",
"devtools/client": "resource://gre/modules/devtools/client",
"devtools/pretty-fast": "resource://gre/modules/devtools/pretty-fast.js",
"devtools/async-utils": "resource://gre/modules/devtools/async-utils",
"acorn": "resource://gre/modules/devtools/acorn",
"acorn/util/walk": "resource://gre/modules/devtools/acorn/walk.js",
@ -114,6 +115,7 @@ SrcdirProvider.prototype = {
let touchEventsURI = this.fileURI(OS.Path.join(toolkitDir, "touch-events"));
let clientURI = this.fileURI(OS.Path.join(toolkitDir, "client"));
let prettyFastURI = this.fileURI(OS.Path.join(toolkitDir), "pretty-fast.js");
let asyncUtilsURI = this.fileURI(OS.Path.join(toolkitDir), "async-utils.js");
let acornURI = this.fileURI(OS.Path.join(toolkitDir, "acorn"));
let acornWalkURI = OS.Path.join(acornURI, "walk.js");
this.loader = new loader.Loader({
@ -134,6 +136,7 @@ SrcdirProvider.prototype = {
"devtools/touch-events": touchEventsURI,
"devtools/client": clientURI,
"devtools/pretty-fast": prettyFastURI,
"devtools/async-utils": asyncUtilsURI,
"acorn": acornURI,
"acorn/util/walk": acornWalkURI

View File

@ -0,0 +1,100 @@
/* 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/. */
"use strict";
/**
* Helpers for async functions. Async functions are generator functions that are
* run by Tasks. An async function returns a Promise for the resolution of the
* function. When the function returns, the promise is resolved with the
* returned value. If it throws the promise rejects with the thrown error.
*
* See Task documentation at https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Task.jsm.
*/
let {Cu} = require("chrome");
let {Task} = require("resource://gre/modules/Task.jsm");
let {Promise} = require("resource://gre/modules/Promise.jsm");
/**
* Create an async function from a generator function.
*
* @param Function func
* The generator function that to wrap as an async function.
* @return Function
* The async function.
*/
exports.async = function async(func) {
return function(...args) {
return Task.spawn(func.apply(this, args));
};
};
/**
* Create an async function that only executes once per instance of an object.
* Once called on a given object, the same promise will be returned for any
* future calls for that object.
*
* @param Function func
* The generator function that to wrap as an async function.
* @return Function
* The async function.
*/
exports.asyncOnce = function asyncOnce(func) {
const promises = new WeakMap();
return function(...args) {
let promise = promises.get(this);
if (!promise) {
promise = Task.spawn(func.apply(this, args));
promises.set(this, promise);
}
return promise;
};
};
/**
* Call a function that expects a callback as the last argument and returns a
* promise for the result. This simplifies using callback APIs from tasks and
* async functions.
*
* @param Any obj
* The |this| value to call the function on.
* @param Function func
* The callback-expecting function to call.
* @param Array args
* Additional arguments to pass to the method.
* @return Promise
* The promise for the result. If the callback is called with only one
* argument, it is used as the resolution value. If there's multiple
* arguments, an array containing the arguments is the resolution value.
* If the method throws, the promise is rejected with the thrown value.
*/
function promisify(obj, func, args) {
return new Promise(resolve => {
args.push((...results) => {
resolve(results.length > 1 ? results : results[0]);
});
func.apply(obj, args);
});
}
/**
* Call a method that expects a callback as the last argument and returns a
* promise for the result.
*
* @see promisify
*/
exports.promiseInvoke = function promiseInvoke(obj, func, ...args) {
return promisify(obj, func, args);
};
/**
* Call a function that expects a callback as the last argument.
*
* @see promisify
*/
exports.promiseCall = function promiseCall(func, ...args) {
return promisify(undefined, func, args);
};

View File

@ -0,0 +1,153 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test async-utils.js
const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
const {Promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
const {async, asyncOnce, promiseInvoke, promiseCall} = require("devtools/async-utils");
function run_test() {
do_test_pending();
Task.spawn(function*() {
for (let helper of [async, asyncOnce]) {
yield test_async_args(helper);
yield test_async_return(helper);
yield test_async_throw(helper);
}
yield test_async_once();
yield test_async_invoke();
do_test_finished();
}).then(null, error => {
do_throw(error);
});
}
// Test that arguments are correctly passed through to the async function.
function test_async_args(async) {
let obj = {
method: async(function*(a, b) {
do_check_eq(this, obj);
do_check_eq(a, "foo");
do_check_eq(b, "bar");
})
};
return obj.method("foo", "bar");
}
// Test that the return value from the async function is resolution value of
// the promise.
function test_async_return(async) {
let obj = {
method: async(function*(a, b) {
return a + b;
})
};
return obj.method("foo", "bar").then(ret => {
do_check_eq(ret, "foobar");
});
}
// Test that the throwing from an async function rejects the promise.
function test_async_throw(async) {
let obj = {
method: async(function*() {
throw "boom";
})
};
return obj.method().then(null, error => {
do_check_eq(error, "boom");
});
}
// Test that asyncOnce only runs the async function once per instance and
// returns the same promise for that instance.
function test_async_once() {
let counter = 0;
function Foo() {}
Foo.prototype = {
ran: false,
method: asyncOnce(function*() {
yield Promise.resolve();
if (this.ran) {
do_throw("asyncOnce function unexpectedly ran twice on the same object");
}
this.ran = true;
return counter++;
})
};
let foo1 = new Foo();
let foo2 = new Foo();
let p1 = foo1.method();
let p2 = foo2.method();
do_check_neq(p1, p2);
let p3 = foo1.method();
do_check_eq(p1, p3);
do_check_false(foo1.ran);
let p4 = foo2.method();
do_check_eq(p2, p4);
do_check_false(foo2.ran);
return p1.then(ret => {
do_check_true(foo1.ran);
do_check_eq(ret, 0);
return p2;
}).then(ret => {
do_check_true(foo2.ran);
do_check_eq(ret, 1);
});
}
// Test invoke and call.
function test_async_invoke() {
return Task.spawn(function*() {
function func(a, b, expectedThis, callback) {
"use strict";
do_check_eq(a, "foo");
do_check_eq(b, "bar");
do_check_eq(this, expectedThis);
callback(a + b);
}
// Test call.
let callResult = yield promiseCall(func, "foo", "bar", undefined);
do_check_eq(callResult, "foobar");
// Test invoke.
let obj = { method: func };
let invokeResult = yield promiseInvoke(obj, obj.method, "foo", "bar", obj);
do_check_eq(invokeResult, "foobar");
// Test passing multiple values to the callback.
function multipleResults(callback) {
callback("foo", "bar");
}
let results = yield promiseCall(multipleResults);
do_check_eq(results.length, 2);
do_check_eq(results[0], "foo");
do_check_eq(results[1], "bar");
// Test throwing from the function.
function thrower() {
throw "boom";
}
yield promiseCall(thrower).then(null, error => {
do_check_eq(error, "boom");
});
});
}

View File

@ -6,3 +6,4 @@ tail =
[test_invisible_loader.js]
[test_safeErrorString.js]
[test_defineLazyPrototypeGetter.js]
[test_async-utils.js]

View File

@ -1960,6 +1960,13 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
selEvent.mExpandToClusterBoundary = false;
DispatchEvent(&selEvent);
// Notify SelectionHandler of final caret position
// Required after IME hide via 'Back' button
AndroidGeckoEvent* broadcastEvent = AndroidGeckoEvent::MakeBroadcastEvent(
NS_LITERAL_CSTRING("TextSelection:UpdateCaretPos"),
NS_LITERAL_CSTRING(""));
nsAppShell::gAppShell->PostEvent(broadcastEvent);
}
break;
case AndroidGeckoEvent::IME_ADD_COMPOSITION_RANGE:
@ -2047,6 +2054,13 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
DispatchEvent(&event);
mIMERanges.Clear();
// Notify SelectionHandler of final caret position
// Required in cases of keyboards providing autoCorrections
AndroidGeckoEvent* broadcastEvent = AndroidGeckoEvent::MakeBroadcastEvent(
NS_LITERAL_CSTRING("TextSelection:UpdateCaretPos"),
NS_LITERAL_CSTRING(""));
nsAppShell::gAppShell->PostEvent(broadcastEvent);
}
break;
case AndroidGeckoEvent::IME_REMOVE_COMPOSITION: