mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-03 07:01:19 +00:00
Merge fx-team to m-c.
This commit is contained in:
commit
afdc7727d2
@ -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);
|
||||
|
@ -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")
|
||||
|
@ -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);
|
||||
},
|
||||
|
||||
|
@ -178,6 +178,7 @@ body {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
body.dim > #header > #element-position,
|
||||
body.dim > #main > p,
|
||||
body.dim > #main > .tooltip {
|
||||
visibility: hidden;
|
||||
|
@ -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;
|
||||
|
@ -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">
|
||||
|
@ -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"].
|
||||
|
@ -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() {
|
||||
|
@ -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.
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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) :
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
|
@ -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',
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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);
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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
426
testing/modules/Assert.jsm
Normal 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);
|
||||
};
|
@ -4,4 +4,5 @@
|
||||
|
||||
TESTING_JS_MODULES := \
|
||||
AppInfo.jsm \
|
||||
Assert.jsm \
|
||||
$(NULL)
|
||||
|
@ -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']
|
||||
|
291
testing/modules/tests/xpcshell/test_assert.js
Normal file
291
testing/modules/tests/xpcshell/test_assert.js
Normal 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");
|
||||
}
|
5
testing/modules/tests/xpcshell/xpcshell.ini
Normal file
5
testing/modules/tests/xpcshell/xpcshell.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
head =
|
||||
tail =
|
||||
|
||||
[test_assert.js]
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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?
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user