Merge fx-team to m-c.

This commit is contained in:
Ryan VanderMeulen 2013-11-19 13:44:45 -05:00
commit afdc7727d2
47 changed files with 1142 additions and 145 deletions

View File

@ -963,6 +963,9 @@ var gPluginHandler = {
return;
}
Services.telemetry.getHistogramById("PLUGINS_INFOBAR_SHOWN").
add(true);
let message;
// Icons set directly cannot be manipulated using moz-image-region, so
// we use CSS classes instead.
@ -1001,21 +1004,26 @@ var gPluginHandler = {
}
}
// These strings are temporary no-string-change for branch uplift
let buttons = [
{
label: gNavigatorBundle.getString("pluginBlockNow.label"),
accessKey: gNavigatorBundle.getString("pluginBlockNow.accesskey"),
label: gNavigatorBundle.getString("pluginContinueBlocking.label"),
accessKey: gNavigatorBundle.getString("pluginContinueBlocking.accesskey"),
callback: function() {
Services.telemetry.getHistogramById("PLUGINS_INFOBAR_BLOCK").
add(true);
Services.perms.addFromPrincipal(aBrowser.contentDocument.nodePrincipal,
"plugin-hidden-notification",
Services.perms.DENY_ACTION);
}
},
{
label: gNavigatorBundle.getString("offlineApps.allow"),
accessKey: gNavigatorBundle.getString("offlineApps.allowAccessKey"),
label: gNavigatorBundle.getString("pluginActivateTrigger.label"),
accessKey: gNavigatorBundle.getString("pluginActivateTrigger.accesskey"),
callback: function() {
Services.telemetry.getHistogramById("PLUGINS_INFOBAR_ALLOW").
add(true);
let curNotification =
PopupNotifications.getNotification("click-to-play-plugins",
aBrowser);

View File

@ -2071,6 +2071,12 @@ this.CustomizableUI = {
onWidgetDrag: function(aWidgetId, aArea) {
CustomizableUIInternal.notifyListeners("onWidgetDrag", aWidgetId, aArea);
},
notifyStartCustomizing: function(aWindow) {
CustomizableUIInternal.notifyListeners("onCustomizeStart", aWindow);
},
notifyEndCustomizing: function(aWindow) {
CustomizableUIInternal.notifyListeners("onCustomizeEnd", aWindow);
},
isAreaOverflowable: function(aAreaId) {
let area = gAreas.get(aAreaId);
return area ? area.get("type") == this.TYPE_TOOLBAR && area.get("overflowable")

View File

@ -111,6 +111,7 @@ CustomizeMode.prototype = {
this.document.documentElement._lightweightTheme.disable();
this.dispatchToolboxEvent("beforecustomization");
CustomizableUI.notifyStartCustomizing(this.window);
let window = this.window;
let document = this.document;
@ -285,6 +286,7 @@ CustomizeMode.prototype = {
this._changed = false;
this._transitioning = false;
this.dispatchToolboxEvent("aftercustomization");
CustomizableUI.notifyEndCustomizing(this.window);
}.bind(this)).then(null, ERROR);
},

View File

@ -178,6 +178,7 @@ body {
pointer-events: none;
}
body.dim > #header > #element-position,
body.dim > #main > p,
body.dim > #main > .tooltip {
visibility: hidden;

View File

@ -46,6 +46,9 @@ LayoutView.prototype = {
// 'property' is what we are measuring;
// 'value' is the computed dimension, computed in update().
this.map = {
position: {selector: "#element-position",
property: "position",
value: undefined},
marginTop: {selector: ".margin.top > span",
property: "margin-top",
value: undefined},
@ -201,7 +204,19 @@ LayoutView.prototype = {
for (let i in this.map) {
let property = this.map[i].property;
this.map[i].value = parseInt(layout[property]);
if (!(property in layout)) {
// Depending on the actor version, some properties
// might be missing.
continue;
}
let parsedValue = parseInt(layout[property]);
if (Number.isNaN(parsedValue)) {
// Not a number. We use the raw string.
// Useful for "position" for example.
this.map[i].value = layout[property];
} else {
this.map[i].value = parsedValue;
}
}
let margins = layout.autoMargins;

View File

@ -74,7 +74,7 @@
<body class="theme-body devtools-monospace">
<p id="header">
<span id="element-size"></span>
<span id="element-size"></span><span id="element-position"></span>
</p>
<div id="main">

View File

@ -229,7 +229,7 @@ CssHtmlTree.processTemplate = function CssHtmlTree_processTemplate(aTemplate,
};
XPCOMUtils.defineLazyGetter(CssHtmlTree, "_strings", function() Services.strings
.createBundle("chrome://browser/locale/devtools/styleinspector.properties"));
.createBundle("chrome://global/locale/devtools/styleinspector.properties"));
XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
return Cc["@mozilla.org/widget/clipboardhelper;1"].

View File

@ -2446,7 +2446,7 @@ XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
XPCOMUtils.defineLazyGetter(this, "_strings", function() {
return Services.strings.createBundle(
"chrome://browser/locale/devtools/styleinspector.properties");
"chrome://global/locale/devtools/styleinspector.properties");
});
XPCOMUtils.defineLazyGetter(this, "domUtils", function() {

View File

@ -14,7 +14,7 @@ loader.lazyGetter(this, "gDevTools", () => Cu.import("resource:///modules/devtoo
loader.lazyGetter(this, "RuleView", () => require("devtools/styleinspector/rule-view"));
loader.lazyGetter(this, "ComputedView", () => require("devtools/styleinspector/computed-view"));
loader.lazyGetter(this, "_strings", () => Services.strings
.createBundle("chrome://browser/locale/devtools/styleinspector.properties"));
.createBundle("chrome://global/locale/devtools/styleinspector.properties"));
// This module doesn't currently export any symbols directly, it only
// registers inspector tools.

View File

@ -47,7 +47,7 @@ function checkCssLogic()
cssLogic.processMatchedSelectors();
let _strings = Services.strings
.createBundle("chrome://browser/locale/devtools/styleinspector.properties");
.createBundle("chrome://global/locale/devtools/styleinspector.properties");
let inline = _strings.GetStringFromName("rule.sourceInline");

View File

@ -41,7 +41,7 @@ function checkSheets()
let elementStyle = ruleView()._elementStyle;
let _strings = Services.strings
.createBundle("chrome://browser/locale/devtools/styleinspector.properties");
.createBundle("chrome://global/locale/devtools/styleinspector.properties");
let inline = _strings.GetStringFromName("rule.sourceInline");

View File

@ -163,6 +163,14 @@ PluginClickToActivate=Activate %S.
PluginVulnerableUpdatable=This plugin is vulnerable and should be updated.
PluginVulnerableNoUpdate=This plugin has security vulnerabilities.
# infobar UI
pluginContinueBlocking.label=Continue Blocking
pluginContinueBlocking.accesskey=B
# LOCALIZATION NOTE (pluginActivateTrigger): Use the unicode ellipsis char, \u2026,
# or use "..." if \u2026 doesn't suit traditions in your locale.
pluginActivateTrigger.label=Allow…
pluginActivateTrigger.accesskey=A
# Sanitize
# LOCALIZATION NOTE (sanitizeDialog2.everything.title): When "Time range to
# clear" is set to "Everything", the Clear Recent History dialog's title is

View File

@ -39,7 +39,6 @@
locale/browser/devtools/scratchpad.dtd (%chrome/browser/devtools/scratchpad.dtd)
locale/browser/devtools/styleeditor.properties (%chrome/browser/devtools/styleeditor.properties)
locale/browser/devtools/styleeditor.dtd (%chrome/browser/devtools/styleeditor.dtd)
locale/browser/devtools/styleinspector.properties (%chrome/browser/devtools/styleinspector.properties)
locale/browser/devtools/styleinspector.dtd (%chrome/browser/devtools/styleinspector.dtd)
locale/browser/devtools/webConsole.dtd (%chrome/browser/devtools/webConsole.dtd)
locale/browser/devtools/VariablesView.dtd (%chrome/browser/devtools/VariablesView.dtd)

View File

@ -39,30 +39,52 @@ var APZCObserver = {
handleEvent: function APZC_handleEvent(aEvent) {
switch (aEvent.type) {
case 'pageshow':
if (aEvent.target != Browser.selectedBrowser.contentDocument)
break;
// fall through to TabSelect:
case 'TabSelect':
// ROOT_ID doesn't really identify the view we want. When we call
// this on a content document (tab), findElementWithViewId will
// always return the root content document associated with the
// scrollable frame.
const ROOT_ID = 1;
let windowUtils = Browser.selectedBrowser.contentWindow.
QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils);
// findElementWithViewId will throw if it can't find it
let element;
try {
element = windowUtils.findElementWithViewId(ROOT_ID);
} catch (e) {
// Not present; nothing to do here
if (aEvent.target != Browser.selectedBrowser.contentDocument) {
break;
}
windowUtils.setDisplayPortForElement(0, 0, ContentAreaObserver.width,
ContentAreaObserver.height,
element);
// intentional fall through
case 'TabSelect': {
// Start off with something reasonable. The apzc will handle these
// calculations once scrolling starts.
let doc = Browser.selectedBrowser.contentDocument.documentElement;
// While running tests, sometimes this can be null. If we don't have a
// root document, there's no point in setting a scrollable display port.
if (!doc) {
break;
}
let win = Browser.selectedBrowser.contentWindow;
let factor = 0.2;
let portX = 0;
let portY = 0;
let portWidth = ContentAreaObserver.width;
let portHeight = ContentAreaObserver.height;
if (portWidth < doc.scrollWidth) {
portWidth += ContentAreaObserver.width * factor;
if (portWidth > doc.scrollWidth) {
portWidth = doc.scrollWidth;
}
}
if (portHeight < doc.scrollHeight) {
portHeight += ContentAreaObserver.height * factor;
if (portHeight > doc.scrollHeight) {
portHeight = doc.scrollHeight;
}
}
if (win.scrollX > 0) {
portX -= ContentAreaObserver.width * factor;
}
if (win.scrollY > 0) {
portY -= ContentAreaObserver.height * factor;
}
let cwu = Browser.selectedBrowser.contentWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
cwu.setDisplayPortForElement(portX, portY,
portWidth, portHeight,
Browser.selectedBrowser.contentDocument.documentElement);
break;
}
case 'TabOpen': {
let browser = aEvent.originalTarget.linkedBrowser;
browser.addEventListener("pageshow", this, true);

View File

@ -253,6 +253,7 @@ var SelectionHelperUI = {
_endMark: null,
_caretMark: null,
_target: null,
_showAfterUpdate: false,
_movement: { active: false, x:0, y: 0 },
_activeSelectionRect: null,
_selectionMarkIds: [],
@ -350,13 +351,29 @@ var SelectionHelperUI = {
*/
observe: function (aSubject, aTopic, aData) {
switch (aTopic) {
case "attach_edit_session_to_content":
let event = aSubject;
SelectionHelperUI.attachEditSession(Browser.selectedTab.browser,
event.clientX, event.clientY);
break;
}
switch (aTopic) {
case "attach_edit_session_to_content":
let event = aSubject;
this.attachEditSession(Browser.selectedTab.browser,
event.clientX, event.clientY);
break;
case "apzc-handle-pan-begin":
if (this.isActive && this.layerMode == kContentLayer) {
this._hideMonocles();
}
break;
case "apzc-handle-pan-end":
// The selection range callback will check to see if the new
// position is off the screen, in which case it shuts down and
// clears the selection.
if (this.isActive && this.layerMode == kContentLayer) {
this._showAfterUpdate = true;
this._sendAsyncMessage("Browser:SelectionUpdate", {});
}
break;
}
},
/*
@ -530,7 +547,10 @@ var SelectionHelperUI = {
*/
init: function () {
Services.obs.addObserver(this, "attach_edit_session_to_content", false);
let os = Services.obs;
os.addObserver(this, "attach_edit_session_to_content", false);
os.addObserver(this, "apzc-handle-pan-begin", false);
os.addObserver(this, "apzc-handle-pan-end", false);
},
_init: function _init(aMsgTarget) {
@ -908,6 +928,16 @@ var SelectionHelperUI = {
this._shutdown();
},
_checkMonocleVisibility: function(aX, aY) {
if (aX < 0 || aY < 0 ||
aX > ContentAreaObserver.viewableWidth ||
aY > ContentAreaObserver.viewableHeight) {
this.closeEditSession(true);
return false;
}
return true;
},
/*
* Message handlers
*/
@ -920,26 +950,43 @@ var SelectionHelperUI = {
let haveSelectionRect = true;
if (json.updateStart) {
this.startMark.position(this._msgTarget.btocx(json.start.xPos, true),
this._msgTarget.btocy(json.start.yPos, true));
let x = this._msgTarget.btocx(json.start.xPos, true);
let y = this._msgTarget.btocx(json.start.yPos, true);
if (!this._checkMonocleVisibility(x, y)) {
return;
}
this.startMark.position(x, y);
}
if (json.updateEnd) {
this.endMark.position(this._msgTarget.btocx(json.end.xPos, true),
this._msgTarget.btocy(json.end.yPos, true));
let x = this._msgTarget.btocx(json.end.xPos, true);
let y = this._msgTarget.btocx(json.end.yPos, true);
if (!this._checkMonocleVisibility(x, y)) {
return;
}
this.endMark.position(x, y);
}
if (json.updateCaret) {
let x = this._msgTarget.btocx(json.caret.xPos, true);
let y = this._msgTarget.btocx(json.caret.yPos, true);
if (!this._checkMonocleVisibility(x, y)) {
return;
}
// If selectionRangeFound is set SelectionHelper found a range we can
// attach to. If not, there's no text in the control, and hence no caret
// position information we can use.
haveSelectionRect = json.selectionRangeFound;
if (json.selectionRangeFound) {
this.caretMark.position(this._msgTarget.btocx(json.caret.xPos, true),
this._msgTarget.btocy(json.caret.yPos, true));
this.caretMark.position(x, y);
this.caretMark.show();
}
}
if (this._showAfterUpdate) {
this._showAfterUpdate = false;
this._showMonocles(!json.updateCaret);
}
this._targetIsEditable = json.targetIsEditable;
this._activeSelectionRect = haveSelectionRect ?
this._msgTarget.rectBrowserToClient(json.selection, true) :

View File

@ -38,7 +38,7 @@
background-color: -moz-Dialog;
}
#navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar) {
#navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar) {
padding-top: 1px;
padding-bottom: 1px;
}

View File

@ -23,6 +23,8 @@ import org.mozilla.gecko.home.SearchEngine;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.prompts.Prompt;
import org.mozilla.gecko.toolbar.AutocompleteHandler;
import org.mozilla.gecko.toolbar.BrowserToolbar;
import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.GamepadUtils;
import org.mozilla.gecko.util.HardwareUtils;

View File

@ -14,7 +14,7 @@ import android.view.inputmethod.InputMethodManager;
import java.util.Collection;
import java.util.Locale;
final class InputMethods {
final public class InputMethods {
public static final String METHOD_ANDROID_LATINIME = "com.android.inputmethod.latin/.LatinIME";
public static final String METHOD_ATOK = "com.justsystems.atokmobile.service/.AtokInputMethodService";
public static final String METHOD_GOOGLE_JAPANESE_INPUT = "com.google.android.inputmethod.japanese/.MozcService";

View File

@ -5,7 +5,6 @@
package org.mozilla.gecko.home;
import org.mozilla.gecko.AutocompleteHandler;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.PrefsHelper;
@ -16,6 +15,7 @@ import org.mozilla.gecko.db.BrowserDB.URLColumns;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import org.mozilla.gecko.home.SearchEngine;
import org.mozilla.gecko.home.SearchLoader.SearchCursorLoader;
import org.mozilla.gecko.toolbar.AutocompleteHandler;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.StringUtils;
import org.mozilla.gecko.util.ThreadUtils;

View File

@ -91,15 +91,10 @@ gbjar.sources += [
'animation/ViewHelper.java',
'ANRReporter.java',
'AppNotificationClient.java',
'AutocompleteHandler.java',
'BackButton.java',
'BaseGeckoInterface.java',
'BrowserApp.java',
'BrowserToolbar.java',
'BrowserToolbarBackground.java',
'CameraImageResultHandler.java',
'CameraVideoResultHandler.java',
'CanvasDelegate.java',
'ContactService.java',
'ContextGetter.java',
'CustomEditText.java',
@ -127,7 +122,6 @@ gbjar.sources += [
'FilePickerResultHandlerSync.java',
'FindInPageBar.java',
'FormAssistPopup.java',
'ForwardButton.java',
'GeckoAccessibility.java',
'GeckoActivity.java',
'GeckoActivityStatus.java',
@ -245,7 +239,6 @@ gbjar.sources += [
'NotificationService.java',
'NSSBridge.java',
'OrderedBroadcastHelper.java',
'PageActionLayout.java',
'preferences/AlignRightLinkPreference.java',
'preferences/AndroidImport.java',
'preferences/AndroidImportPreference.java',
@ -272,7 +265,6 @@ gbjar.sources += [
'ScrollAnimator.java',
'ServiceNotificationClient.java',
'SessionParser.java',
'ShapedButton.java',
'SharedPreferencesHelper.java',
'SiteIdentityPopup.java',
'SmsManager.java',
@ -282,7 +274,6 @@ gbjar.sources += [
'sqlite/SQLiteBridgeException.java',
'SurfaceBits.java',
'Tab.java',
'TabCounter.java',
'Tabs.java',
'TabsAccessor.java',
'TabsPanel.java',
@ -291,6 +282,15 @@ gbjar.sources += [
'TextSelection.java',
'TextSelectionHandle.java',
'ThumbnailHelper.java',
'toolbar/AutocompleteHandler.java',
'toolbar/BackButton.java',
'toolbar/BrowserToolbar.java',
'toolbar/BrowserToolbarBackground.java',
'toolbar/CanvasDelegate.java',
'toolbar/ForwardButton.java',
'toolbar/PageActionLayout.java',
'toolbar/ShapedButton.java',
'toolbar/TabCounter.java',
'TouchEventInterceptor.java',
'updater/UpdateService.java',
'updater/UpdateServiceHelper.java',

View File

@ -6,20 +6,20 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gecko="http://schemas.android.com/apk/res-auto">
<org.mozilla.gecko.BrowserToolbarBackground android:id="@+id/url_bar_bg"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/url_bar_bg"/>
<org.mozilla.gecko.toolbar.BrowserToolbarBackground android:id="@+id/url_bar_bg"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/url_bar_bg"/>
<org.mozilla.gecko.ShapedButton android:id="@+id/tabs"
style="@style/UrlBar.ImageButton"
android:layout_width="84dip"
android:layout_alignParentLeft="true"
gecko:curveTowards="left"
android:background="@drawable/shaped_button"
android:gravity="center_vertical"
android:paddingLeft="6dip"
android:paddingRight="38dip"/>
<org.mozilla.gecko.toolbar.ShapedButton android:id="@+id/tabs"
style="@style/UrlBar.ImageButton"
android:layout_width="84dip"
android:layout_alignParentLeft="true"
gecko:curveTowards="left"
android:background="@drawable/shaped_button"
android:gravity="center_vertical"
android:paddingLeft="6dip"
android:paddingRight="38dip"/>
<!-- The TextSwitcher should be shifted 28dp on the right, to avoid
the curve. On a 56dp space, centering 24dp image will leave
@ -27,7 +27,7 @@
2 layers. Hence to center this, an additional 4dp is added to the right.
The margins will be 12dp on left, 48dp on right, instead of ideal 16dp
and 44dp. -->
<org.mozilla.gecko.TabCounter android:id="@+id/tabs_counter"
<org.mozilla.gecko.toolbar.TabCounter android:id="@+id/tabs_counter"
style="@style/UrlBar.ImageButton.TabCount"
android:layout_width="24dip"
android:layout_height="24dip"
@ -49,21 +49,21 @@
android:focusable="false"
android:background="@drawable/url_bar_entry"/>
<org.mozilla.gecko.ForwardButton style="@style/UrlBar.ImageButton.Forward"
android:id="@+id/forward"
android:layout_toRightOf="@+id/tabs"/>
<org.mozilla.gecko.toolbar.ForwardButton style="@style/UrlBar.ImageButton.Forward"
android:id="@+id/forward"
android:layout_toRightOf="@+id/tabs"/>
<org.mozilla.gecko.BackButton android:id="@+id/back"
style="@style/UrlBar.ImageButton"
android:layout_width="50dip"
android:layout_height="50dip"
android:layout_toRightOf="@id/tabs"
android:layout_marginLeft="-28dp"
android:layout_centerVertical="true"
android:padding="13dp"
android:src="@drawable/ic_menu_back"
android:contentDescription="@string/back"
android:background="@drawable/url_bar_nav_button"/>
<org.mozilla.gecko.toolbar.BackButton android:id="@+id/back"
style="@style/UrlBar.ImageButton"
android:layout_width="50dip"
android:layout_height="50dip"
android:layout_toRightOf="@id/tabs"
android:layout_marginLeft="-28dp"
android:layout_centerVertical="true"
android:padding="13dp"
android:src="@drawable/ic_menu_back"
android:contentDescription="@string/back"
android:background="@drawable/url_bar_nav_button"/>
<LinearLayout android:id="@+id/url_edit_container"
style="@style/UrlBar.Button"
@ -142,12 +142,12 @@
android:layout_gravity="center_vertical"
gecko:autoUpdateTheme="false"/>
<org.mozilla.gecko.PageActionLayout android:id="@+id/page_action_layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginRight="@dimen/browser_toolbar_button_padding"
android:visibility="gone"
android:orientation="horizontal"/>
<org.mozilla.gecko.toolbar.PageActionLayout android:id="@+id/page_action_layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginRight="@dimen/browser_toolbar_button_padding"
android:visibility="gone"
android:orientation="horizontal"/>
<ImageButton android:id="@+id/stop"
style="@style/UrlBar.ImageButton.Icon"

View File

@ -12,10 +12,10 @@
<ImageButton android:id="@+id/forward"
style="@style/UrlBar.ImageButton.Unused"/>
<org.mozilla.gecko.BrowserToolbarBackground android:id="@+id/url_bar_bg"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/url_bar_bg"/>
<org.mozilla.gecko.toolbar.BrowserToolbarBackground android:id="@+id/url_bar_bg"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/url_bar_bg"/>
<ImageView android:id="@+id/url_bar_entry"
style="@style/UrlBar.Button"
@ -48,13 +48,13 @@
<LinearLayout android:id="@+id/menu_items"
style="@style/UrlBar.ImageButton.Unused"/>
<org.mozilla.gecko.ShapedButton android:id="@+id/menu"
style="@style/UrlBar.ImageButton"
android:layout_width="48dip"
android:layout_alignParentRight="true"
android:contentDescription="@string/menu"
android:background="@drawable/shaped_button"
android:visibility="gone"/>
<org.mozilla.gecko.toolbar.ShapedButton android:id="@+id/menu"
style="@style/UrlBar.ImageButton"
android:layout_width="48dip"
android:layout_alignParentRight="true"
android:contentDescription="@string/menu"
android:background="@drawable/shaped_button"
android:visibility="gone"/>
<org.mozilla.gecko.widget.GeckoImageView android:id="@+id/menu_icon"
style="@style/UrlBar.ImageButton"
@ -64,16 +64,16 @@
android:src="@drawable/menu_level"
android:visibility="gone"/>
<org.mozilla.gecko.ShapedButton android:id="@+id/tabs"
style="@style/UrlBar.ImageButton"
android:layout_width="72dip"
android:layout_toLeftOf="@id/menu"
android:layout_alignWithParentIfMissing="true"
gecko:curveTowards="right"
android:background="@drawable/shaped_button"
android:gravity="center_vertical"
android:paddingLeft="37dip"
android:paddingRight="11dip"/>
<org.mozilla.gecko.toolbar.ShapedButton android:id="@+id/tabs"
style="@style/UrlBar.ImageButton"
android:layout_width="72dip"
android:layout_toLeftOf="@id/menu"
android:layout_alignWithParentIfMissing="true"
gecko:curveTowards="right"
android:background="@drawable/shaped_button"
android:gravity="center_vertical"
android:paddingLeft="37dip"
android:paddingRight="11dip"/>
<!-- The TextSwitcher should be shifted 24dp on the left, to avoid
the curve. On a 48dp space, centering 24dp image will leave
@ -81,7 +81,7 @@
2 layers. Hence to center this, an additional 4dp is added to the left.
The margins will be 40dp on left, 8dp on right, instead of ideal 30dp
and 12dp. -->
<org.mozilla.gecko.TabCounter android:id="@+id/tabs_counter"
<org.mozilla.gecko.toolbar.TabCounter android:id="@+id/tabs_counter"
style="@style/UrlBar.ImageButton.TabCount"
android:layout_width="24dip"
android:layout_height="24dip"
@ -166,12 +166,12 @@
android:layout_gravity="center_vertical"
gecko:autoUpdateTheme="false"/>
<org.mozilla.gecko.PageActionLayout android:id="@+id/page_action_layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginRight="12dp"
android:visibility="gone"
android:orientation="horizontal"/>
<org.mozilla.gecko.toolbar.PageActionLayout android:id="@+id/page_action_layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginRight="12dp"
android:visibility="gone"
android:orientation="horizontal"/>
<ImageButton android:id="@+id/stop"
style="@style/UrlBar.ImageButton.Icon"

View File

@ -67,7 +67,8 @@
view. To make sure the EditText is not the first focusable view in
the root view, BrowserToolbar should be specified as low in the
view hierarchy as possible. -->
<org.mozilla.gecko.BrowserToolbar android:id="@id/browser_toolbar"
<org.mozilla.gecko.toolbar.BrowserToolbar
android:id="@id/browser_toolbar"
style="@style/BrowserToolbar"
android:layout_width="fill_parent"
android:layout_height="@dimen/browser_toolbar_height"

View File

@ -3,7 +3,7 @@
* 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/. */
package org.mozilla.gecko;
package org.mozilla.gecko.toolbar;
public interface AutocompleteHandler {
void onAutocomplete(String res);

View File

@ -2,7 +2,9 @@
* 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/. */
package org.mozilla.gecko;
package org.mozilla.gecko.toolbar;
import org.mozilla.gecko.R;
import android.content.Context;
import android.graphics.Canvas;

View File

@ -3,8 +3,19 @@
* 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/. */
package org.mozilla.gecko;
package org.mozilla.gecko.toolbar;
import org.mozilla.gecko.BrowserApp;
import org.mozilla.gecko.CustomEditText;
import org.mozilla.gecko.InputMethods;
import org.mozilla.gecko.GeckoApplication;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.LightweightTheme;
import org.mozilla.gecko.R;
import org.mozilla.gecko.SiteIdentityPopup;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.animation.PropertyAnimator;
import org.mozilla.gecko.animation.ViewHelper;
import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
@ -12,7 +23,6 @@ import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.util.GamepadUtils;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.menu.MenuPopup;
import org.mozilla.gecko.PageActionLayout;
import org.mozilla.gecko.PrefsHelper;
import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.StringUtils;

View File

@ -2,8 +2,11 @@
* 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/. */
package org.mozilla.gecko;
package org.mozilla.gecko.toolbar;
import org.mozilla.gecko.GeckoApplication;
import org.mozilla.gecko.LightweightTheme;
import org.mozilla.gecko.R;
import org.mozilla.gecko.widget.GeckoLinearLayout;
import android.content.Context;

View File

@ -2,7 +2,7 @@
* 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/. */
package org.mozilla.gecko;
package org.mozilla.gecko.toolbar;
import android.graphics.Bitmap;
import android.graphics.Canvas;

View File

@ -2,7 +2,9 @@
* 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/. */
package org.mozilla.gecko;
package org.mozilla.gecko.toolbar;
import org.mozilla.gecko.R;
import android.content.Context;
import android.graphics.Canvas;

View File

@ -3,8 +3,11 @@
* 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/. */
package org.mozilla.gecko;
package org.mozilla.gecko.toolbar;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.R;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.ThreadUtils;

View File

@ -2,8 +2,12 @@
* 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/. */
package org.mozilla.gecko;
package org.mozilla.gecko.toolbar;
import org.mozilla.gecko.GeckoApplication;
import org.mozilla.gecko.LightweightTheme;
import org.mozilla.gecko.LightweightThemeDrawable;
import org.mozilla.gecko.R;
import org.mozilla.gecko.widget.GeckoImageButton;
import android.content.Context;

View File

@ -3,9 +3,10 @@
* 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/. */
package org.mozilla.gecko;
package org.mozilla.gecko.toolbar;
import org.mozilla.gecko.animation.Rotate3DAnimation;
import org.mozilla.gecko.R;
import org.mozilla.gecko.widget.GeckoTextSwitcher;
import android.content.Context;

View File

@ -17,6 +17,7 @@ var SelectionHandler = {
_cache: null,
_activeType: 0, // TYPE_NONE
_ignoreSelectionChanges: false, // True while user drags text selection handles
_ignoreCompositionChanges: false, // Persist caret during IME composition updates
// The window that holds the selection (can be a sub-frame)
get _contentWindow() {
@ -108,6 +109,9 @@ var SelectionHandler = {
this._ignoreSelectionChanges = true;
this._moveSelection(data.handleType == this.HANDLE_TYPE_START, data.x, data.y);
} else if (this._activeType == this.TYPE_CURSOR) {
// Ignore IMM composition notifications when caret movement starts
this._ignoreCompositionChanges = true;
// Send a click event to the text box, which positions the caret
this._sendMouseEvents(data.x, data.y);
@ -133,6 +137,10 @@ var SelectionHandler = {
}
// Act on selectionChange notifications after handle movement ends
this._ignoreSelectionChanges = false;
} else if (this._activeType == this.TYPE_CURSOR) {
// Act on IMM composition notifications after caret movement ends
this._ignoreCompositionChanges = false;
}
this._positionHandles();
break;
@ -158,7 +166,8 @@ var SelectionHandler = {
break;
case "compositionend":
if (this._activeType == this.TYPE_CURSOR) {
// compositionend messages normally terminate caret display
if (this._activeType == this.TYPE_CURSOR && !this._ignoreCompositionChanges) {
this._deactivate();
}
break;
@ -530,6 +539,7 @@ var SelectionHandler = {
this._isRTL = false;
this._cache = null;
this._ignoreSelectionChanges = false;
this._ignoreCompositionChanges = false;
},
_getViewOffset: function sh_getViewOffset() {

426
testing/modules/Assert.jsm Normal file
View File

@ -0,0 +1,426 @@
/* 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/. */
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
// When you see a javadoc comment that contains a number, it's a reference to a
// specific section of the CommonJS spec.
//
// Originally from narwhal.js (http://narwhaljs.org)
// Copyright (c) 2009 Thomas Robinson <280north.com>
// MIT license: http://opensource.org/licenses/MIT
"use strict";
this.EXPORTED_SYMBOLS = [
"Assert"
];
/**
* 1. The assert module provides functions that throw AssertionError's when
* particular conditions are not met.
*
* To use the module you'll need to instantiate it first, which allows consumers
* to override certain behavior on the newly obtained instance. For examples,
* see the javadoc comments for the `report` member function.
*/
let Assert = this.Assert = function(reporterFunc) {
if (reporterFunc)
this.setReporter(reporterFunc);
};
function instanceOf(object, type) {
return Object.prototype.toString.call(object) == "[object " + type + "]";
}
function replacer(key, value) {
if (value === undefined) {
return "" + value;
}
if (typeof value === "number" && (isNaN(value) || !isFinite(value))) {
return value.toString();
}
if (typeof value === "function" || instanceOf(value, "RegExp")) {
return value.toString();
}
return value;
}
const kTruncateLength = 128;
function truncate(text, newLength = kTruncateLength) {
if (typeof text == "string") {
return text.length < newLength ? text : text.slice(0, newLength);
} else {
return text;
}
}
function getMessage(error) {
return truncate(JSON.stringify(error.actual, replacer)) + " " +
(error.operator ? error.operator + " " : "") +
truncate(JSON.stringify(error.expected, replacer));
}
/**
* 2. The AssertionError is defined in assert.
*
* Example:
* new assert.AssertionError({
* message: message,
* actual: actual,
* expected: expected,
* operator: operator
* });
*
* At present only the four keys mentioned above are used and
* understood by the spec. Implementations or sub modules can pass
* other keys to the AssertionError's constructor - they will be
* ignored.
*/
Assert.AssertionError = function(options) {
this.name = "AssertionError";
this.actual = options.actual;
this.expected = options.expected;
this.operator = options.operator;
this.message = options.message || getMessage(this);
// The part of the stack that comes from this module is not interesting.
let stack = Components.stack;
do {
stack = stack.caller;
} while(stack.filename && stack.filename.contains("Assert.jsm"))
this.stack = stack;
};
// assert.AssertionError instanceof Error
Assert.AssertionError.prototype = Object.create(Error.prototype, {
constructor: {
value: Assert.AssertionError,
enumerable: false,
writable: true,
configurable: true
}
});
let proto = Assert.prototype;
proto._reporter = null;
/**
* Set a custom assertion report handler function. Arguments passed in to this
* function are:
* err (AssertionError|null) An error object when the assertion failed or null
* when it passed
* message (string) Message describing the assertion
* stack (stack) Stack trace of the assertion function
*
* Example:
* ```js
* Assert.setReporter(function customReporter(err, message, stack) {
* if (err) {
* do_report_result(false, err.message, err.stack);
* } else {
* do_report_result(true, message, stack);
* }
* });
* ```
*
* @param reporterFunc
* (function) Report handler function
*/
proto.setReporter = function(reporterFunc) {
this._reporter = reporterFunc;
};
/**
* 3. All of the following functions must throw an AssertionError when a
* corresponding condition is not met, with a message that may be undefined if
* not provided. All assertion methods provide both the actual and expected
* values to the assertion error for display purposes.
*
* This report method only throws errors on assertion failures, as per spec,
* but consumers of this module (think: xpcshell-test, mochitest) may want to
* override this default implementation.
*
* Example:
* ```js
* // The following will report an assertion failure.
* this.report(1 != 2, 1, 2, "testing JS number math!", "==");
* ```
*
* @param failed
* (boolean) Indicates if the assertion failed or not
* @param actual
* (mixed) The result of evaluating the assertion
* @param expected (optional)
* (mixed) Expected result from the test author
* @param message (optional)
* (string) Short explanation of the expected result
* @param operator (optional)
* (string) Operation qualifier used by the assertion method (ex: '==')
*/
proto.report = function(failed, actual, expected, message, operator) {
let err = new Assert.AssertionError({
message: message,
actual: actual,
expected: expected,
operator: operator
});
if (!this._reporter) {
// If no custom reporter is set, throw the error.
if (failed) {
throw err;
}
} else {
this._reporter(failed ? err : null, message, err.stack);
}
};
/**
* 4. Pure assertion tests whether a value is truthy, as determined by !!guard.
* assert.ok(guard, message_opt);
* This statement is equivalent to assert.equal(true, !!guard, message_opt);.
* To test strictly for the value true, use assert.strictEqual(true, guard,
* message_opt);.
*
* @param value
* (mixed) Test subject to be evaluated as truthy
* @param message (optional)
* (string) Short explanation of the expected result
*/
proto.ok = function(value, message) {
this.report(!value, value, true, message, "==");
};
/**
* 5. The equality assertion tests shallow, coercive equality with ==.
* assert.equal(actual, expected, message_opt);
*
* @param actual
* (mixed) Test subject to be evaluated as equivalent to `expected`
* @param expected
* (mixed) Test reference to evaluate against `actual`
* @param message (optional)
* (string) Short explanation of the expected result
*/
proto.equal = function equal(actual, expected, message) {
this.report(actual != expected, actual, expected, message, "==");
};
/**
* 6. The non-equality assertion tests for whether two objects are not equal
* with != assert.notEqual(actual, expected, message_opt);
*
* @param actual
* (mixed) Test subject to be evaluated as NOT equivalent to `expected`
* @param expected
* (mixed) Test reference to evaluate against `actual`
* @param message (optional)
* (string) Short explanation of the expected result
*/
proto.notEqual = function notEqual(actual, expected, message) {
this.report(actual == expected, actual, expected, message, "!=");
};
/**
* 7. The equivalence assertion tests a deep equality relation.
* assert.deepEqual(actual, expected, message_opt);
*
* We check using the most exact approximation of equality between two objects
* to keep the chance of false positives to a minimum.
* `JSON.stringify` is not designed to be used for this purpose; objects may
* have ambiguous `toJSON()` implementations that would influence the test.
*
* @param actual
* (mixed) Test subject to be evaluated as equivalent to `expected`, including nested properties
* @param expected
* (mixed) Test reference to evaluate against `actual`
* @param message (optional)
* (string) Short explanation of the expected result
*/
proto.deepEqual = function deepEqual(actual, expected, message) {
this.report(!_deepEqual(actual, expected), actual, expected, message, "deepEqual");
};
function _deepEqual(actual, expected) {
// 7.1. All identical values are equivalent, as determined by ===.
if (actual === expected) {
return true;
// 7.2. If the expected value is a Date object, the actual value is
// equivalent if it is also a Date object that refers to the same time.
} else if (instanceOf(actual, "Date") && instanceOf(expected, "Date")) {
return actual.getTime() === expected.getTime();
// 7.3 If the expected value is a RegExp object, the actual value is
// equivalent if it is also a RegExp object with the same source and
// properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
} else if (instanceOf(actual, "RegExp") && instanceOf(expected, "RegExp")) {
return actual.source === expected.source &&
actual.global === expected.global &&
actual.multiline === expected.multiline &&
actual.lastIndex === expected.lastIndex &&
actual.ignoreCase === expected.ignoreCase;
// 7.4. Other pairs that do not both pass typeof value == "object",
// equivalence is determined by ==.
} else if (typeof actual != "object" && typeof expected != "object") {
return actual == expected;
// 7.5 For all other Object pairs, including Array objects, equivalence is
// determined by having the same number of owned properties (as verified
// with Object.prototype.hasOwnProperty.call), the same set of keys
// (although not necessarily the same order), equivalent values for every
// corresponding key, and an identical 'prototype' property. Note: this
// accounts for both named and indexed properties on Arrays.
} else {
return objEquiv(actual, expected);
}
}
function isUndefinedOrNull(value) {
return value === null || value === undefined;
}
function isArguments(object) {
return instanceOf(object, "Arguments");
}
function objEquiv(a, b) {
if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) {
return false;
}
// An identical 'prototype' property.
if (a.prototype !== b.prototype) {
return false;
}
// Object.keys may be broken through screwy arguments passing. Converting to
// an array solves the problem.
if (isArguments(a)) {
if (!isArguments(b)) {
return false;
}
a = pSlice.call(a);
b = pSlice.call(b);
return _deepEqual(a, b);
}
let ka, kb, key, i;
try {
ka = Object.keys(a);
kb = Object.keys(b);
} catch (e) {
// Happens when one is a string literal and the other isn't
return false;
}
// Having the same number of owned properties (keys incorporates
// hasOwnProperty)
if (ka.length != kb.length)
return false;
// The same set of keys (although not necessarily the same order),
ka.sort();
kb.sort();
// Equivalent values for every corresponding key, and possibly expensive deep
// test
for (i = ka.length - 1; i >= 0; i--) {
key = ka[i];
if (!_deepEqual(a[key], b[key])) {
return false;
}
}
return true;
}
/**
* 8. The non-equivalence assertion tests for any deep inequality.
* assert.notDeepEqual(actual, expected, message_opt);
*
* @param actual
* (mixed) Test subject to be evaluated as NOT equivalent to `expected`, including nested properties
* @param expected
* (mixed) Test reference to evaluate against `actual`
* @param message (optional)
* (string) Short explanation of the expected result
*/
proto.notDeepEqual = function notDeepEqual(actual, expected, message) {
this.report(_deepEqual(actual, expected), actual, expected, message, "notDeepEqual");
};
/**
* 9. The strict equality assertion tests strict equality, as determined by ===.
* assert.strictEqual(actual, expected, message_opt);
*
* @param actual
* (mixed) Test subject to be evaluated as strictly equivalent to `expected`
* @param expected
* (mixed) Test reference to evaluate against `actual`
* @param message (optional)
* (string) Short explanation of the expected result
*/
proto.strictEqual = function strictEqual(actual, expected, message) {
this.report(actual !== expected, actual, expected, message, "===");
};
/**
* 10. The strict non-equality assertion tests for strict inequality, as
* determined by !==. assert.notStrictEqual(actual, expected, message_opt);
*
* @param actual
* (mixed) Test subject to be evaluated as NOT strictly equivalent to `expected`
* @param expected
* (mixed) Test reference to evaluate against `actual`
* @param message (optional)
* (string) Short explanation of the expected result
*/
proto.notStrictEqual = function notStrictEqual(actual, expected, message) {
this.report(actual === expected, actual, expected, message, "!==");
};
function expectedException(actual, expected) {
if (!actual || !expected) {
return false;
}
if (instanceOf(expected, "RegExp")) {
return expected.test(actual);
} else if (actual instanceof expected) {
return true;
} else if (expected.call({}, actual) === true) {
return true;
}
return false;
}
/**
* 11. Expected to throw an error:
* assert.throws(block, Error_opt, message_opt);
*
* @param block
* (function) Function block to evaluate and catch eventual thrown errors
* @param expected (optional)
* (mixed) Test reference to evaluate against the thrown result from `block`
* @param message (optional)
* (string) Short explanation of the expected result
*/
proto.throws = function(block, expected, message) {
let actual;
if (typeof expected === "string") {
message = expected;
expected = null;
}
try {
block();
} catch (e) {
actual = e;
}
message = (expected && expected.name ? " (" + expected.name + ")." : ".") +
(message ? " " + message : ".");
if (!actual) {
this.report(true, actual, expected, "Missing expected exception" + message);
}
if ((actual && expected && !expectedException(actual, expected))) {
throw actual;
}
this.report(false, expected, expected, message);
};

View File

@ -4,4 +4,5 @@
TESTING_JS_MODULES := \
AppInfo.jsm \
Assert.jsm \
$(NULL)

View File

@ -4,3 +4,4 @@
# 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/.
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']

View File

@ -0,0 +1,291 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test cases borrowed and adapted from:
// https://github.com/joyent/node/blob/6101eb184db77d0b11eb96e48744e57ecce4b73d/test/simple/test-assert.js
// MIT license: http://opensource.org/licenses/MIT
function run_test() {
let ns = {};
Components.utils.import("resource://testing-common/Assert.jsm", ns);
let assert = new ns.Assert();
function makeBlock(f, ...args) {
return function() {
return f.apply(assert, args);
};
}
function protoCtrChain(o) {
let result = [];
while (o = o.__proto__) {
result.push(o.constructor);
}
return result.join();
}
function indirectInstanceOf(obj, cls) {
if (obj instanceof cls) {
return true;
}
let clsChain = protoCtrChain(cls.prototype);
let objChain = protoCtrChain(obj);
return objChain.slice(-clsChain.length) === clsChain;
};
assert.ok(indirectInstanceOf(ns.Assert.AssertionError.prototype, Error),
"Assert.AssertionError instanceof Error");
assert.throws(makeBlock(assert.ok, false),
ns.Assert.AssertionError, "ok(false)");
assert.ok(true, "ok(true)");
assert.ok("test", "ok('test')");
assert.throws(makeBlock(assert.equal, true, false), ns.Assert.AssertionError, "equal");
assert.equal(null, null, "equal");
assert.equal(undefined, undefined, "equal");
assert.equal(null, undefined, "equal");
assert.equal(true, true, "equal");
assert.notEqual(true, false, "notEqual");
assert.throws(makeBlock(assert.notEqual, true, true),
ns.Assert.AssertionError, "notEqual");
assert.throws(makeBlock(assert.strictEqual, 2, "2"),
ns.Assert.AssertionError, "strictEqual");
assert.throws(makeBlock(assert.strictEqual, null, undefined),
ns.Assert.AssertionError, "strictEqual");
assert.notStrictEqual(2, "2", "notStrictEqual");
// deepEquals joy!
// 7.2
assert.deepEqual(new Date(2000, 3, 14), new Date(2000, 3, 14), "deepEqual date");
assert.throws(makeBlock(assert.deepEqual, new Date(), new Date(2000, 3, 14)),
ns.Assert.AssertionError,
"deepEqual date");
// 7.3
assert.deepEqual(/a/, /a/);
assert.deepEqual(/a/g, /a/g);
assert.deepEqual(/a/i, /a/i);
assert.deepEqual(/a/m, /a/m);
assert.deepEqual(/a/igm, /a/igm);
assert.throws(makeBlock(assert.deepEqual, /ab/, /a/));
assert.throws(makeBlock(assert.deepEqual, /a/g, /a/));
assert.throws(makeBlock(assert.deepEqual, /a/i, /a/));
assert.throws(makeBlock(assert.deepEqual, /a/m, /a/));
assert.throws(makeBlock(assert.deepEqual, /a/igm, /a/im));
let re1 = /a/;
re1.lastIndex = 3;
assert.throws(makeBlock(assert.deepEqual, re1, /a/));
// 7.4
assert.deepEqual(4, "4", "deepEqual == check");
assert.deepEqual(true, 1, "deepEqual == check");
assert.throws(makeBlock(assert.deepEqual, 4, "5"),
ns.Assert.AssertionError,
"deepEqual == check");
// 7.5
// having the same number of owned properties && the same set of keys
assert.deepEqual({a: 4}, {a: 4});
assert.deepEqual({a: 4, b: "2"}, {a: 4, b: "2"});
assert.deepEqual([4], ["4"]);
assert.throws(makeBlock(assert.deepEqual, {a: 4}, {a: 4, b: true}),
ns.Assert.AssertionError);
assert.deepEqual(["a"], {0: "a"});
let a1 = [1, 2, 3];
let a2 = [1, 2, 3];
a1.a = "test";
a1.b = true;
a2.b = true;
a2.a = "test";
assert.throws(makeBlock(assert.deepEqual, Object.keys(a1), Object.keys(a2)),
ns.Assert.AssertionError);
assert.deepEqual(a1, a2);
let nbRoot = {
toString: function() { return this.first + " " + this.last; }
};
function nameBuilder(first, last) {
this.first = first;
this.last = last;
return this;
}
nameBuilder.prototype = nbRoot;
function nameBuilder2(first, last) {
this.first = first;
this.last = last;
return this;
}
nameBuilder2.prototype = nbRoot;
let nb1 = new nameBuilder("Ryan", "Dahl");
let nb2 = new nameBuilder2("Ryan", "Dahl");
assert.deepEqual(nb1, nb2);
nameBuilder2.prototype = Object;
nb2 = new nameBuilder2("Ryan", "Dahl");
assert.throws(makeBlock(assert.deepEqual, nb1, nb2), ns.Assert.AssertionError);
// String literal + object
assert.throws(makeBlock(assert.deepEqual, "a", {}), ns.Assert.AssertionError);
// Testing the throwing
function thrower(errorConstructor) {
throw new errorConstructor("test");
}
let aethrow = makeBlock(thrower, ns.Assert.AssertionError);
aethrow = makeBlock(thrower, ns.Assert.AssertionError);
// the basic calls work
assert.throws(makeBlock(thrower, ns.Assert.AssertionError),
ns.Assert.AssertionError, "message");
assert.throws(makeBlock(thrower, ns.Assert.AssertionError), ns.Assert.AssertionError);
assert.throws(makeBlock(thrower, ns.Assert.AssertionError));
// if not passing an error, catch all.
assert.throws(makeBlock(thrower, TypeError));
// when passing a type, only catch errors of the appropriate type
let threw = false;
try {
assert.throws(makeBlock(thrower, TypeError), ns.Assert.AssertionError);
} catch (e) {
threw = true;
assert.ok(e instanceof TypeError, "type");
}
assert.equal(true, threw,
"Assert.throws with an explicit error is eating extra errors",
ns.Assert.AssertionError);
threw = false;
function ifError(err) {
if (err) {
throw err;
}
}
assert.throws(function() {
ifError(new Error("test error"));
});
// make sure that validating using constructor really works
threw = false;
try {
assert.throws(
function() {
throw ({});
},
Array
);
} catch (e) {
threw = true;
}
assert.ok(threw, "wrong constructor validation");
// use a RegExp to validate error message
assert.throws(makeBlock(thrower, TypeError), /test/);
// use a fn to validate error object
assert.throws(makeBlock(thrower, TypeError), function(err) {
if ((err instanceof TypeError) && /test/.test(err)) {
return true;
}
});
// Make sure deepEqual doesn't loop forever on circular refs
let b = {};
b.b = b;
let c = {};
c.b = c;
let gotError = false;
try {
assert.deepEqual(b, c);
} catch (e) {
gotError = true;
}
dump("All OK\n");
assert.ok(gotError);
function testAssertionMessage(actual, expected) {
try {
assert.equal(actual, "");
} catch (e) {
assert.equal(e.toString(),
["AssertionError:", expected, "==", '""'].join(" "));
}
}
testAssertionMessage(undefined, '"undefined"');
testAssertionMessage(null, "null");
testAssertionMessage(true, "true");
testAssertionMessage(false, "false");
testAssertionMessage(0, "0");
testAssertionMessage(100, "100");
testAssertionMessage(NaN, '"NaN"');
testAssertionMessage(Infinity, '"Infinity"');
testAssertionMessage(-Infinity, '"-Infinity"');
testAssertionMessage("", '""');
testAssertionMessage("foo", '"foo"');
testAssertionMessage([], "[]");
testAssertionMessage([1, 2, 3], "[1,2,3]");
testAssertionMessage(/a/, '"/a/"');
testAssertionMessage(/abc/gim, '"/abc/gim"');
testAssertionMessage(function f() {}, '"function f() {}"');
testAssertionMessage({}, "{}");
testAssertionMessage({a: undefined, b: null}, '{"a":"undefined","b":null}');
testAssertionMessage({a: NaN, b: Infinity, c: -Infinity},
'{"a":"NaN","b":"Infinity","c":"-Infinity"}');
// https://github.com/joyent/node/issues/2893
try {
assert.throws(function () {
ifError(null);
});
} catch (e) {
threw = true;
assert.equal(e.message, "Missing expected exception..");
}
assert.ok(threw);
// https://github.com/joyent/node/issues/5292
try {
assert.equal(1, 2);
} catch (e) {
assert.equal(e.toString().split("\n")[0], "AssertionError: 1 == 2")
}
try {
assert.equal(1, 2, "oh no");
} catch (e) {
assert.equal(e.toString().split("\n")[0], "AssertionError: oh no")
}
// Export Assert.jsm methods to become globally accessible.
export_assertions();
// Test XPCShell-test integration:
ok(true, "OK, this went well");
deepEqual(/a/g, /a/g, "deep equal should work on RegExp");
deepEqual(/a/igm, /a/igm, "deep equal should work on RegExp");
deepEqual({a: 4, b: "1"}, {b: "1", a: 4}, "deep equal should work on regular Object");
deepEqual(a1, a2, "deep equal should work on Array with Object properties");
}

View File

@ -0,0 +1,5 @@
[DEFAULT]
head =
tail =
[test_assert.js]

View File

@ -184,6 +184,15 @@ function _do_quit() {
}
function _format_exception_stack(stack) {
if (typeof stack == "object" && stack.caller) {
let frame = stack;
let strStack = "";
while (frame != null) {
strStack += frame + "\n";
frame = frame.caller;
}
stack = strStack;
}
// frame is of the form "fname@file:line"
let frame_regexp = new RegExp("(.*)@(.*):(\\d*)", "g");
return stack.split("\n").reduce(function(stack_msg, frame) {
@ -343,6 +352,24 @@ function _execute_test() {
// _TEST_FILE is dynamically defined by <runxpcshelltests.py>.
_load_files(_TEST_FILE);
// Support a common assertion library, Assert.jsm.
let Assert = Components.utils.import("resource://testing-common/Assert.jsm", null).Assert;
// Pass a custom report function for xpcshell-test style reporting.
let assertImpl = new Assert(function(err, message, stack) {
if (err) {
do_report_result(false, err.message, err.stack);
} else {
do_report_result(true, message, stack);
}
});
// Allow Assert.jsm methods to be tacked to the current scope.
this.export_assertions = function() {
for (let func in assertImpl) {
this[func] = assertImpl[func].bind(assertImpl);
}
};
this.Assert = assertImpl;
try {
do_test_pending("MAIN run_test");
run_test();

View File

@ -3671,6 +3671,18 @@
"n_values": 3,
"description": "User actions taken in the plugin notification: 0: allownow 1: allowalways 2: block"
},
"PLUGINS_INFOBAR_SHOWN": {
"kind": "boolean",
"description": "Count of when the hidden-plugin infobar was displayed."
},
"PLUGINS_INFOBAR_BLOCK": {
"kind": "boolean",
"description": "Count the number of times the user clicked 'block' on the hidden-plugin infobar."
},
"PLUGINS_INFOBAR_ALLOW": {
"kind": "boolean",
"description": "Count the number of times the user clicked 'allow' on the hidden-plugin infobar."
},
"POPUP_NOTIFICATION_MAINACTION_TRIGGERED_MS": {
"kind": "linear",
"low": 25,

View File

@ -452,6 +452,7 @@ var PageStyleActor = protocol.ActorClass({
// We compute and update the values of margins & co.
let style = node.rawNode.ownerDocument.defaultView.getComputedStyle(node.rawNode);
for (let prop of [
"position",
"margin-top",
"margin-right",
"margin-bottom",

View File

@ -730,7 +730,7 @@ CssLogic.getSelectors = function CssLogic_getSelectors(aDOMRule)
CssLogic.l10n = function(aName) CssLogic._strings.GetStringFromName(aName);
XPCOMUtils.defineLazyGetter(CssLogic, "_strings", function() Services.strings
.createBundle("chrome://browser/locale/devtools/styleinspector.properties"));
.createBundle("chrome://global/locale/devtools/styleinspector.properties"));
/**
* Is the given property sheet a content stylesheet?

View File

@ -32,6 +32,7 @@
locale/@AB_CD@/global/datetimepicker.dtd (%chrome/global/datetimepicker.dtd)
locale/@AB_CD@/global/dateFormat.properties (%chrome/global/dateFormat.properties)
locale/@AB_CD@/global/devtools/debugger.properties (%chrome/global/devtools/debugger.properties)
locale/@AB_CD@/global/devtools/styleinspector.properties (%chrome/global/devtools/styleinspector.properties)
locale/@AB_CD@/global/dialogOverlay.dtd (%chrome/global/dialogOverlay.dtd)
locale/@AB_CD@/global/editMenuOverlay.dtd (%chrome/global/editMenuOverlay.dtd)
locale/@AB_CD@/global/filefield.properties (%chrome/global/filefield.properties)

View File

@ -1461,30 +1461,33 @@ function recursiveRemove(aFile) {
* Returns the timestamp and leaf file name of the most recently modified
* entry in a directory,
* or simply the file's own timestamp if it is not a directory.
* Also returns the total number of items (directories and files) visited in the scan
*
* @param aFile
* A non-null nsIFile object
* @return [File Name, Epoch time], as described above.
* @return [File Name, Epoch time, items visited], as described above.
*/
function recursiveLastModifiedTime(aFile) {
try {
let modTime = aFile.lastModifiedTime;
let fileName = aFile.leafName;
if (aFile.isFile())
return [fileName, modTime];
return [fileName, modTime, 1];
if (aFile.isDirectory()) {
let entries = aFile.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
let entry;
let totalItems = 1;
while ((entry = entries.nextFile)) {
let [subName, subTime] = recursiveLastModifiedTime(entry);
let [subName, subTime, items] = recursiveLastModifiedTime(entry);
totalItems += items;
if (subTime > modTime) {
modTime = subTime;
fileName = subName;
}
}
entries.close();
return [fileName, modTime];
return [fileName, modTime, totalItems];
}
}
catch (e) {
@ -1492,7 +1495,7 @@ function recursiveLastModifiedTime(aFile) {
}
// If the file is something else, just ignore it.
return ["", 0];
return ["", 0, 0];
}
/**
@ -1638,6 +1641,49 @@ var Prefs = {
}
}
// Helper function to compare JSON saved version of the directory state
// with the new state returned by getInstallLocationStates()
// Structure is: ordered array of {'name':?, 'addons': {addonID: {'descriptor':?, 'mtime':?} ...}}
function directoryStateDiffers(aState, aCache)
{
// check equality of an object full of addons; fortunately we can destroy the 'aOld' object
function addonsMismatch(aNew, aOld) {
for (let [id, val] of aNew) {
if (!id in aOld)
return true;
if (val.descriptor != aOld[id].descriptor ||
val.mtime != aOld[id].mtime)
return true;
delete aOld[id];
}
// make sure aOld doesn't have any extra entries
for (let id in aOld)
return true;
return false;
}
if (!aCache)
return true;
try {
let old = JSON.parse(aCache);
if (aState.length != old.length)
return true;
for (let i = 0; i < aState.length; i++) {
// conveniently, any missing fields would require a 'true' return, which is
// handled by our catch wrapper
if (aState[i].name != old[i].name)
return true;
if (addonsMismatch(aState[i].addons, old[i].addons))
return true;
}
}
catch (e) {
return true;
}
return false;
}
var XPIProvider = {
// An array of known install locations
installLocations: null,
@ -2178,9 +2224,10 @@ var XPIProvider = {
getAddonStates: function XPI_getAddonStates(aLocation) {
let addonStates = {};
for (let file of aLocation.addonLocations) {
let scanStarted = Date.now();
let id = aLocation.getIDForLocation(file);
let unpacked = 0;
let [modFile, modTime] = recursiveLastModifiedTime(file);
let [modFile, modTime, items] = recursiveLastModifiedTime(file);
addonStates[id] = {
descriptor: file.persistentDescriptor,
mtime: modTime
@ -2196,6 +2243,8 @@ var XPIProvider = {
this._mostRecentlyModifiedFile[id] = modFile;
this.setTelemetry(id, "unpacked", unpacked);
this.setTelemetry(id, "location", aLocation.name);
this.setTelemetry(id, "scan_MS", Date.now() - scanStarted);
this.setTelemetry(id, "scan_items", items);
}
return addonStates;
@ -3372,8 +3421,15 @@ var XPIProvider = {
// If the install directory state has changed then we must update the database
let cache = Prefs.getCharPref(PREF_INSTALL_CACHE, null);
// For a little while, gather telemetry on whether the deep comparison
// makes a difference
if (cache != JSON.stringify(state)) {
updateReasons.push("directoryState");
if (directoryStateDiffers(state, cache)) {
updateReasons.push("directoryState");
}
else {
AddonManagerPrivate.recordSimpleMeasure("XPIDB_startup_state_badCompare", 1);
}
}
// If the database doesn't exist and there are add-ons installed then we
@ -5500,7 +5556,9 @@ AddonInstall.prototype = {
// Update the metadata in the database
this.addon._sourceBundle = file;
this.addon._installLocation = this.installLocation;
let [mFile, mTime] = recursiveLastModifiedTime(file);
let scanStarted = Date.now();
let [, mTime, scanItems] = recursiveLastModifiedTime(file);
let scanTime = Date.now() - scanStarted;
this.addon.updateDate = mTime;
this.addon.visible = true;
if (isUpgrade) {
@ -5547,6 +5605,8 @@ AddonInstall.prototype = {
}
XPIProvider.setTelemetry(this.addon.id, "unpacked", installedUnpacked);
XPIProvider.setTelemetry(this.addon.id, "location", this.installLocation.name);
XPIProvider.setTelemetry(this.addon.id, "scan_MS", scanTime);
XPIProvider.setTelemetry(this.addon.id, "scan_items", scanItems);
let loc = this.addon.defaultLocale;
if (loc) {
XPIProvider.setTelemetry(this.addon.id, "name", loc.name);

View File

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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
@ -26,6 +26,7 @@
#include "BasicLayers.h"
#include "FrameMetrics.h"
#include "Windows.Graphics.Display.h"
#include "nsNativeDragTarget.h"
#ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
#endif
@ -333,6 +334,28 @@ MetroWidget::IsVisible() const
return mView->IsVisible();
}
NS_IMETHODIMP
MetroWidget::EnableDragDrop(bool aEnable) {
if (aEnable) {
if (nullptr == mNativeDragTarget) {
mNativeDragTarget = new nsNativeDragTarget(this);
if (!mNativeDragTarget) {
return NS_ERROR_FAILURE;
}
}
HRESULT hr = ::RegisterDragDrop(mWnd, static_cast<LPDROPTARGET>(mNativeDragTarget));
return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
} else {
if (nullptr == mNativeDragTarget) {
return NS_OK;
}
HRESULT hr = ::RevokeDragDrop(mWnd);
return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
}
}
NS_IMETHODIMP
MetroWidget::IsEnabled(bool *aState)
{

View File

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
@ -34,6 +34,7 @@
#include <Windows.ApplicationModel.h>
#include <Windows.Applicationmodel.Activation.h>
class nsNativeDragTarget;
namespace mozilla {
namespace widget {
@ -94,6 +95,7 @@ public:
nsDeviceContext *aContext,
nsWidgetInitData *aInitData = nullptr);
NS_IMETHOD Destroy();
NS_IMETHOD EnableDragDrop(bool aEnable);
NS_IMETHOD SetParent(nsIWidget *aNewParent);
NS_IMETHOD Show(bool bState);
NS_IMETHOD IsVisible(bool & aState);
@ -260,4 +262,5 @@ protected:
nsDeque mEventQueue;
nsDeque mKeyEventQueue;
nsRefPtr<APZController> mController;
nsRefPtr<nsNativeDragTarget> mNativeDragTarget;
};