mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
merge fx-team to mozilla-central
This commit is contained in:
commit
f3825c1c94
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -973,7 +973,6 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder,
|
||||
|
||||
.urlbar-input-box {
|
||||
-moz-margin-start: 0;
|
||||
min-width: 4em;
|
||||
}
|
||||
|
||||
#urlbar-icons {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
BIN
mobile/android/base/resources/drawable-hdpi-v19/toast.9.png
Normal file
BIN
mobile/android/base/resources/drawable-hdpi-v19/toast.9.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 717 B |
20
mobile/android/base/resources/drawable-v19/toast_button.xml
Normal file
20
mobile/android/base/resources/drawable-v19/toast_button.xml
Normal 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>
|
BIN
mobile/android/base/resources/drawable-xhdpi-v19/toast.9.png
Normal file
BIN
mobile/android/base/resources/drawable-xhdpi-v19/toast.9.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
BIN
mobile/android/base/resources/drawable-xhdpi-v19/toast_button_pressed.9.png
Executable file
BIN
mobile/android/base/resources/drawable-xhdpi-v19/toast_button_pressed.9.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 808 B |
BIN
mobile/android/base/resources/drawable-xxhdpi-v19/toast.9.png
Normal file
BIN
mobile/android/base/resources/drawable-xxhdpi-v19/toast.9.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 1016 B |
@ -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>
|
||||
|
@ -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>
|
||||
|
36
mobile/android/base/resources/values-v19/styles.xml
Normal file
36
mobile/android/base/resources/values-v19/styles.xml
Normal 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>
|
@ -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>
|
||||
|
@ -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" />
|
||||
|
||||
|
@ -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"/>
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
100
toolkit/devtools/async-utils.js
Normal file
100
toolkit/devtools/async-utils.js
Normal 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);
|
||||
};
|
153
toolkit/devtools/tests/unit/test_async-utils.js
Normal file
153
toolkit/devtools/tests/unit/test_async-utils.js
Normal 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");
|
||||
});
|
||||
});
|
||||
}
|
@ -6,3 +6,4 @@ tail =
|
||||
[test_invisible_loader.js]
|
||||
[test_safeErrorString.js]
|
||||
[test_defineLazyPrototypeGetter.js]
|
||||
[test_async-utils.js]
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user