mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge fx-team to m-c.
This commit is contained in:
commit
5c9865da52
@ -49,7 +49,11 @@ const EXPECTED_REFLOWS = [
|
||||
|
||||
// tabPreviews.capture()
|
||||
"tabPreviews_capture@chrome://browser/content/browser.js|" +
|
||||
"tabPreviews_handleEvent/<@chrome://browser/content/browser.js|"
|
||||
"tabPreviews_handleEvent/<@chrome://browser/content/browser.js|",
|
||||
|
||||
// tabPreviews.capture()
|
||||
"tabPreviews_capture@chrome://browser/content/browser.js|" +
|
||||
"@chrome://browser/content/browser.js|"
|
||||
];
|
||||
|
||||
const PREF_PRELOAD = "browser.newtab.preload";
|
||||
|
@ -1219,81 +1219,102 @@ ElementEditor.prototype = {
|
||||
return this.node.startModifyingAttributes();
|
||||
},
|
||||
|
||||
_createAttribute: function EE_createAttribute(aAttr, aBefore)
|
||||
_createAttribute: function EE_createAttribute(aAttr, aBefore = null)
|
||||
{
|
||||
if (this.attrs.hasOwnProperty(aAttr.name)) {
|
||||
var attr = this.attrs[aAttr.name];
|
||||
var name = attr.querySelector(".attrname");
|
||||
var val = attr.querySelector(".attrvalue");
|
||||
} else {
|
||||
// Create the template editor, which will save some variables here.
|
||||
let data = {
|
||||
attrName: aAttr.name,
|
||||
};
|
||||
this.template("attribute", data);
|
||||
var {attr, inner, name, val} = data;
|
||||
// Create the template editor, which will save some variables here.
|
||||
let data = {
|
||||
attrName: aAttr.name,
|
||||
};
|
||||
this.template("attribute", data);
|
||||
var {attr, inner, name, val} = data;
|
||||
|
||||
// Figure out where we should place the attribute.
|
||||
let before = aBefore || null;
|
||||
if (aAttr.name == "id") {
|
||||
before = this.attrList.firstChild;
|
||||
} else if (aAttr.name == "class") {
|
||||
let idNode = this.attrs["id"];
|
||||
before = idNode ? idNode.nextSibling : this.attrList.firstChild;
|
||||
}
|
||||
this.attrList.insertBefore(attr, before);
|
||||
// Double quotes need to be handled specially to prevent DOMParser failing.
|
||||
// name="v"a"l"u"e" when editing -> name='v"a"l"u"e"'
|
||||
// name="v'a"l'u"e" when editing -> name="v'a"l'u"e"
|
||||
let editValueDisplayed = aAttr.value;
|
||||
let hasDoubleQuote = editValueDisplayed.contains('"');
|
||||
let hasSingleQuote = editValueDisplayed.contains("'");
|
||||
let initial = aAttr.name + '="' + editValueDisplayed + '"';
|
||||
|
||||
// Make the attribute editable.
|
||||
editableField({
|
||||
element: inner,
|
||||
trigger: "dblclick",
|
||||
stopOnReturn: true,
|
||||
selectAll: false,
|
||||
contentType: InplaceEditor.CONTENT_TYPES.CSS_MIXED,
|
||||
popup: this.markup.popup,
|
||||
start: (aEditor, aEvent) => {
|
||||
// If the editing was started inside the name or value areas,
|
||||
// select accordingly.
|
||||
if (aEvent && aEvent.target === name) {
|
||||
aEditor.input.setSelectionRange(0, name.textContent.length);
|
||||
} else if (aEvent && aEvent.target === val) {
|
||||
let length = val.textContent.length;
|
||||
let editorLength = aEditor.input.value.length;
|
||||
let start = editorLength - (length + 1);
|
||||
aEditor.input.setSelectionRange(start, start + length);
|
||||
} else {
|
||||
aEditor.input.select();
|
||||
}
|
||||
},
|
||||
done: (aVal, aCommit) => {
|
||||
if (!aCommit) {
|
||||
return;
|
||||
}
|
||||
|
||||
let doMods = this._startModifyingAttributes();
|
||||
let undoMods = this._startModifyingAttributes();
|
||||
|
||||
// Remove the attribute stored in this editor and re-add any attributes
|
||||
// parsed out of the input element. Restore original attribute if
|
||||
// parsing fails.
|
||||
try {
|
||||
this._saveAttribute(aAttr.name, undoMods);
|
||||
doMods.removeAttribute(aAttr.name);
|
||||
this._applyAttributes(aVal, attr, doMods, undoMods);
|
||||
this.undo.do(() => {
|
||||
doMods.apply();
|
||||
}, () => {
|
||||
undoMods.apply();
|
||||
})
|
||||
} catch(ex) {
|
||||
console.error(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.attrs[aAttr.name] = attr;
|
||||
// Can't just wrap value with ' since the value contains both " and '.
|
||||
if (hasDoubleQuote && hasSingleQuote) {
|
||||
editValueDisplayed = editValueDisplayed.replace(/\"/g, """);
|
||||
initial = aAttr.name + '="' + editValueDisplayed + '"';
|
||||
}
|
||||
|
||||
// Wrap with ' since there are no single quotes in the attribute value.
|
||||
if (hasDoubleQuote && !hasSingleQuote) {
|
||||
initial = aAttr.name + "='" + editValueDisplayed + "'";
|
||||
}
|
||||
|
||||
// Make the attribute editable.
|
||||
editableField({
|
||||
element: inner,
|
||||
trigger: "dblclick",
|
||||
stopOnReturn: true,
|
||||
selectAll: false,
|
||||
initial: initial,
|
||||
contentType: InplaceEditor.CONTENT_TYPES.CSS_MIXED,
|
||||
popup: this.markup.popup,
|
||||
start: (aEditor, aEvent) => {
|
||||
// If the editing was started inside the name or value areas,
|
||||
// select accordingly.
|
||||
if (aEvent && aEvent.target === name) {
|
||||
aEditor.input.setSelectionRange(0, name.textContent.length);
|
||||
} else if (aEvent && aEvent.target === val) {
|
||||
let length = editValueDisplayed.length;
|
||||
let editorLength = aEditor.input.value.length;
|
||||
let start = editorLength - (length + 1);
|
||||
aEditor.input.setSelectionRange(start, start + length);
|
||||
} else {
|
||||
aEditor.input.select();
|
||||
}
|
||||
},
|
||||
done: (aVal, aCommit) => {
|
||||
if (!aCommit) {
|
||||
return;
|
||||
}
|
||||
|
||||
let doMods = this._startModifyingAttributes();
|
||||
let undoMods = this._startModifyingAttributes();
|
||||
|
||||
// Remove the attribute stored in this editor and re-add any attributes
|
||||
// parsed out of the input element. Restore original attribute if
|
||||
// parsing fails.
|
||||
try {
|
||||
this._saveAttribute(aAttr.name, undoMods);
|
||||
doMods.removeAttribute(aAttr.name);
|
||||
this._applyAttributes(aVal, attr, doMods, undoMods);
|
||||
this.undo.do(() => {
|
||||
doMods.apply();
|
||||
}, () => {
|
||||
undoMods.apply();
|
||||
})
|
||||
} catch(ex) {
|
||||
console.error(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Figure out where we should place the attribute.
|
||||
let before = aBefore;
|
||||
if (aAttr.name == "id") {
|
||||
before = this.attrList.firstChild;
|
||||
} else if (aAttr.name == "class") {
|
||||
let idNode = this.attrs["id"];
|
||||
before = idNode ? idNode.nextSibling : this.attrList.firstChild;
|
||||
}
|
||||
this.attrList.insertBefore(attr, before);
|
||||
|
||||
// Remove the old version of this attribute from the DOM.
|
||||
let oldAttr = this.attrs[aAttr.name];
|
||||
if (oldAttr && oldAttr.parentNode) {
|
||||
oldAttr.parentNode.removeChild(oldAttr);
|
||||
}
|
||||
|
||||
this.attrs[aAttr.name] = attr;
|
||||
|
||||
name.textContent = aAttr.name;
|
||||
val.textContent = aAttr.value;
|
||||
|
||||
|
@ -40,5 +40,7 @@
|
||||
<div id="retag-me-2"></div>
|
||||
</div>
|
||||
<div id="node25"></div>
|
||||
<div id="node26" style='background-image: url("moz-page-thumb://thumbnail?url=http%3A%2F%2Fwww.mozilla.org%2F");'></div>
|
||||
<div id="node27" class="Double " and single '"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -239,6 +239,79 @@ function test() {
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Modify inline style containing \"",
|
||||
before: function() {
|
||||
assertAttributes(doc.querySelector("#node26"), {
|
||||
id: "node26",
|
||||
style: 'background-image: url("moz-page-thumb://thumbnail?url=http%3A%2F%2Fwww.mozilla.org%2F");'
|
||||
});
|
||||
},
|
||||
execute: function(after) {
|
||||
inspector.once("markupmutation", after);
|
||||
let editor = getContainerForRawNode(markup, doc.querySelector("#node26")).editor;
|
||||
let attr = editor.attrs["style"].querySelector(".editable");
|
||||
|
||||
|
||||
attr.focus();
|
||||
EventUtils.sendKey("return", inspector.panelWin);
|
||||
|
||||
let input = inplaceEditor(attr).input;
|
||||
let value = input.value;
|
||||
|
||||
is (value,
|
||||
"style='background-image: url(\"moz-page-thumb://thumbnail?url=http%3A%2F%2Fwww.mozilla.org%2F\");'",
|
||||
"Value contains actual double quotes"
|
||||
);
|
||||
|
||||
value = value.replace(/mozilla\.org/, "mozilla.com");
|
||||
input.value = value;
|
||||
|
||||
EventUtils.sendKey("return", inspector.panelWin);
|
||||
},
|
||||
after: function() {
|
||||
assertAttributes(doc.querySelector("#node26"), {
|
||||
id: "node26",
|
||||
style: 'background-image: url("moz-page-thumb://thumbnail?url=http%3A%2F%2Fwww.mozilla.com%2F");'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Modify inline style containing \" and \'",
|
||||
before: function() {
|
||||
assertAttributes(doc.querySelector("#node27"), {
|
||||
id: "node27",
|
||||
class: 'Double " and single \''
|
||||
});
|
||||
},
|
||||
execute: function(after) {
|
||||
inspector.once("markupmutation", after);
|
||||
let editor = getContainerForRawNode(markup, doc.querySelector("#node27")).editor;
|
||||
let attr = editor.attrs["class"].querySelector(".editable");
|
||||
|
||||
attr.focus();
|
||||
EventUtils.sendKey("return", inspector.panelWin);
|
||||
|
||||
let input = inplaceEditor(attr).input;
|
||||
let value = input.value;
|
||||
|
||||
is (value, "class=\"Double " and single '\"", "Value contains "");
|
||||
|
||||
value = value.replace(/Double/, """).replace(/single/, "'");
|
||||
input.value = value;
|
||||
|
||||
EventUtils.sendKey("return", inspector.panelWin);
|
||||
},
|
||||
after: function() {
|
||||
assertAttributes(doc.querySelector("#node27"), {
|
||||
id: "node27",
|
||||
class: '" " and \' \''
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Add an attribute value without closing \"",
|
||||
enteredText: 'style="display: block;',
|
||||
|
@ -687,6 +687,7 @@ ResponsiveUI.prototype = {
|
||||
this.stack.removeAttribute("notransition");
|
||||
}
|
||||
this.ignoreY = false;
|
||||
this.ignoreX = false;
|
||||
this.isResizing = false;
|
||||
},
|
||||
|
||||
|
@ -42,7 +42,7 @@ function simpleInherit(aInspector, aRuleView)
|
||||
styleNode.parentNode.removeChild(styleNode);
|
||||
|
||||
emptyInherit();
|
||||
}).then(null, console.error);
|
||||
});
|
||||
}
|
||||
|
||||
function emptyInherit()
|
||||
@ -68,7 +68,7 @@ function emptyInherit()
|
||||
styleNode.parentNode.removeChild(styleNode);
|
||||
|
||||
elementStyleInherit();
|
||||
}).then(null, console.error);
|
||||
});
|
||||
}
|
||||
|
||||
function elementStyleInherit()
|
||||
@ -92,7 +92,7 @@ function elementStyleInherit()
|
||||
is(inheritProp.name, "color", "color should have been inherited.");
|
||||
|
||||
finishTest();
|
||||
}).then(null, console.error);
|
||||
});
|
||||
}
|
||||
|
||||
function finishTest()
|
||||
|
@ -37,7 +37,7 @@ function SI_inspectNode()
|
||||
is(span, computedView.viewedElement.rawNode(),
|
||||
"style inspector node matches the selected node");
|
||||
SI_toggleDefaultStyles();
|
||||
}).then(null, (err) => console.error(err));
|
||||
});
|
||||
}
|
||||
|
||||
function SI_toggleDefaultStyles()
|
||||
|
@ -60,7 +60,7 @@
|
||||
<!ENTITY btnPageJS.accesskey "J">
|
||||
<!ENTITY btnPageSecurity.label "Security">
|
||||
<!ENTITY btnPageSecurity.tooltip "Log security errors and warnings">
|
||||
<!ENTITY btnPageSecurity.accesskey "S">
|
||||
<!ENTITY btnPageSecurity.accesskey "u">
|
||||
|
||||
<!-- LOCALIZATION NOTE (btnPageLogging): This is used as the text of the
|
||||
- the toolbar. It shows or hides messages that the web developer inserted on
|
||||
|
@ -9,12 +9,6 @@
|
||||
|
||||
.newattr {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Give some padding to focusable elements to match the editor input
|
||||
* that will replace them. */
|
||||
span[tabindex] {
|
||||
display: inline-block;
|
||||
padding: 1px 0;
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ _MOZBUILD_EXTERNAL_VARIABLES := \
|
||||
TEST_DIRS \
|
||||
TIERS \
|
||||
TOOL_DIRS \
|
||||
XPCSHELL_TESTS \
|
||||
XPIDL_MODULE \
|
||||
$(NULL)
|
||||
|
||||
|
@ -28,8 +28,11 @@ NoFilesSelected=No files selected.
|
||||
# %S will be a number greater or equal to 2.
|
||||
XFilesSelected=%S files selected.
|
||||
ColorPicker=Choose a color
|
||||
# LOCALIZATION NOTE (AndXMoreFiles): this string is shown at the end of the
|
||||
# tooltip text for <input type='file' multiple> when there are more than 21
|
||||
# files selected (when we will only list the first 20, plus an "and X more"
|
||||
# line). %S will be the number of files minus 20.
|
||||
AndXMoreFiles=and %S more
|
||||
# LOCALIZATION NOTE (AndNMoreFiles): Semi-colon list of plural forms.
|
||||
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
# This string is shown at the end of the tooltip text for <input type='file'
|
||||
# multiple> when there are more than 21 files selected (when we will only list
|
||||
# the first 20, plus an "and X more" line). #1 represents the number of files
|
||||
# minus 20 and will always be a number equal to or greater than 2. So the
|
||||
# singular case will never be used.
|
||||
AndNMoreFiles=and one more;and #1 more
|
||||
|
@ -30,6 +30,7 @@ _MOZBUILD_EXTERNAL_VARIABLES := \
|
||||
TEST_DIRS \
|
||||
TIERS \
|
||||
TOOL_DIRS \
|
||||
XPCSHELL_TESTS \
|
||||
XPIDL_MODULE \
|
||||
$(NULL)
|
||||
|
||||
|
@ -31,7 +31,7 @@ import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PageActionLayout extends LinearLayout implements GeckoEventListener,
|
||||
View.OnClickListener,
|
||||
@ -40,7 +40,7 @@ public class PageActionLayout extends LinearLayout implements GeckoEventListener
|
||||
private final String MENU_BUTTON_KEY = "MENU_BUTTON_KEY";
|
||||
private final int DEFAULT_PAGE_ACTIONS_SHOWN = 2;
|
||||
|
||||
private LinkedHashMap<String, PageAction> mPageActionList;
|
||||
private ArrayList<PageAction> mPageActionList;
|
||||
private GeckoPopupMenu mPageActionsMenu;
|
||||
private Context mContext;
|
||||
private LinearLayout mLayout;
|
||||
@ -53,7 +53,7 @@ public class PageActionLayout extends LinearLayout implements GeckoEventListener
|
||||
mContext = context;
|
||||
mLayout = this;
|
||||
|
||||
mPageActionList = new LinkedHashMap<String, PageAction>();
|
||||
mPageActionList = new ArrayList<PageAction>();
|
||||
setNumberShown(DEFAULT_PAGE_ACTIONS_SHOWN);
|
||||
refreshPageActionIcons();
|
||||
|
||||
@ -91,6 +91,7 @@ public class PageActionLayout extends LinearLayout implements GeckoEventListener
|
||||
final String id = message.getString("id");
|
||||
final String title = message.getString("title");
|
||||
final String imageURL = message.optString("icon");
|
||||
final boolean mImportant = message.getBoolean("important");
|
||||
|
||||
addPageAction(id, title, imageURL, new OnPageActionClickListeners() {
|
||||
@Override
|
||||
@ -103,7 +104,7 @@ public class PageActionLayout extends LinearLayout implements GeckoEventListener
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("PageActions:LongClicked", id));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}, mImportant);
|
||||
} else if (event.equals("PageActions:Remove")) {
|
||||
final String id = message.getString("id");
|
||||
|
||||
@ -114,14 +115,19 @@ public class PageActionLayout extends LinearLayout implements GeckoEventListener
|
||||
}
|
||||
}
|
||||
|
||||
public void addPageAction(final String id, final String title, final String imageData, final OnPageActionClickListeners mOnPageActionClickListeners) {
|
||||
final PageAction pageAction = new PageAction(id, title, null, mOnPageActionClickListeners);
|
||||
mPageActionList.put(id, pageAction);
|
||||
public void addPageAction(final String id, final String title, final String imageData, final OnPageActionClickListeners mOnPageActionClickListeners, boolean mImportant) {
|
||||
final PageAction pageAction = new PageAction(id, title, null, mOnPageActionClickListeners, mImportant);
|
||||
|
||||
int insertAt = mPageActionList.size();
|
||||
while(insertAt > 0 && mPageActionList.get(insertAt-1).isImportant()) {
|
||||
insertAt--;
|
||||
}
|
||||
mPageActionList.add(insertAt, pageAction);
|
||||
|
||||
BitmapUtils.getDrawable(mContext, imageData, new BitmapUtils.BitmapLoader() {
|
||||
@Override
|
||||
public void onBitmapFound(final Drawable d) {
|
||||
if (mPageActionList.containsKey(id)) {
|
||||
if (mPageActionList.contains(pageAction)) {
|
||||
pageAction.setDrawable(d);
|
||||
refreshPageActionIcons();
|
||||
}
|
||||
@ -130,8 +136,13 @@ public class PageActionLayout extends LinearLayout implements GeckoEventListener
|
||||
}
|
||||
|
||||
public void removePageAction(String id) {
|
||||
mPageActionList.remove(id);
|
||||
refreshPageActionIcons();
|
||||
for(int i = 0; i < mPageActionList.size(); i++) {
|
||||
if (mPageActionList.get(i).getID().equals(id)) {
|
||||
mPageActionList.remove(i);
|
||||
refreshPageActionIcons();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ImageButton createImageButton() {
|
||||
@ -150,7 +161,7 @@ public class PageActionLayout extends LinearLayout implements GeckoEventListener
|
||||
if (buttonClickedId.equals(MENU_BUTTON_KEY)) {
|
||||
showMenu(v, mPageActionList.size() - mMaxVisiblePageActions + 1);
|
||||
} else {
|
||||
mPageActionList.get(buttonClickedId).onClick();
|
||||
getPageActionWithId(buttonClickedId).onClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,7 +173,7 @@ public class PageActionLayout extends LinearLayout implements GeckoEventListener
|
||||
showMenu(v, mPageActionList.size() - mMaxVisiblePageActions + 1);
|
||||
return true;
|
||||
} else {
|
||||
return mPageActionList.get(buttonClickedId).onLongClick();
|
||||
return getPageActionWithId(buttonClickedId).onLongClick();
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,18 +241,17 @@ public class PageActionLayout extends LinearLayout implements GeckoEventListener
|
||||
|
||||
if (mPageActionList.size() > buttonIndex) {
|
||||
// Return the pageactions starting from the end of the list for the number of visible pageactions.
|
||||
return getPageActionAt((mPageActionList.size() - totalVisibleButtons) + buttonIndex);
|
||||
return mPageActionList.get((mPageActionList.size() - totalVisibleButtons) + buttonIndex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private PageAction getPageActionAt(int index) {
|
||||
int count = 0;
|
||||
for(PageAction pageAction : mPageActionList.values()) {
|
||||
if (count == index) {
|
||||
private PageAction getPageActionWithId(String id) {
|
||||
for(int i = 0; i < mPageActionList.size(); i++) {
|
||||
PageAction pageAction = mPageActionList.get(i);
|
||||
if (pageAction.getID().equals(id)) {
|
||||
return pageAction;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -253,25 +263,27 @@ public class PageActionLayout extends LinearLayout implements GeckoEventListener
|
||||
mPageActionsMenu.setOnMenuItemClickListener(new GeckoPopupMenu.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
for(PageAction pageAction : mPageActionList.values()) {
|
||||
if (pageAction.key() == item.getItemId()) {
|
||||
int id = item.getItemId();
|
||||
for(int i = 0; i < mPageActionList.size(); i++) {
|
||||
PageAction pageAction = mPageActionList.get(i);
|
||||
if (pageAction.key() == id) {
|
||||
pageAction.onClick();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
Menu menu = mPageActionsMenu.getMenu();
|
||||
menu.clear();
|
||||
|
||||
int count = 0;
|
||||
for(PageAction pageAction : mPageActionList.values()) {
|
||||
if (count < toShow) {
|
||||
for(int i = 0; i < mPageActionList.size(); i++) {
|
||||
if (i < toShow) {
|
||||
PageAction pageAction = mPageActionList.get(i);
|
||||
MenuItem item = menu.add(Menu.NONE, pageAction.key(), Menu.NONE, pageAction.getTitle());
|
||||
item.setIcon(pageAction.getDrawable());
|
||||
}
|
||||
count++;
|
||||
}
|
||||
mPageActionsMenu.show();
|
||||
}
|
||||
@ -287,12 +299,18 @@ public class PageActionLayout extends LinearLayout implements GeckoEventListener
|
||||
private String mTitle;
|
||||
private String mId;
|
||||
private int key;
|
||||
private boolean mImportant;
|
||||
|
||||
public PageAction(String id, String title, Drawable image, OnPageActionClickListeners mOnPageActionClickListeners) {
|
||||
public PageAction(String id,
|
||||
String title,
|
||||
Drawable image,
|
||||
OnPageActionClickListeners mOnPageActionClickListeners,
|
||||
boolean mImportant) {
|
||||
this.mId = id;
|
||||
this.mTitle = title;
|
||||
this.mDrawable = image;
|
||||
this.mOnPageActionClickListeners = mOnPageActionClickListeners;
|
||||
this.mImportant = mImportant;
|
||||
|
||||
this.key = UUID.fromString(mId.subSequence(1, mId.length() - 2).toString()).hashCode();
|
||||
}
|
||||
@ -317,6 +335,10 @@ public class PageActionLayout extends LinearLayout implements GeckoEventListener
|
||||
return key;
|
||||
}
|
||||
|
||||
public boolean isImportant() {
|
||||
return mImportant;
|
||||
}
|
||||
|
||||
public void onClick() {
|
||||
if (mOnPageActionClickListeners != null) {
|
||||
mOnPageActionClickListeners.onClick(mId);
|
||||
|
@ -74,13 +74,17 @@ var SelectionHandler = {
|
||||
this.copySelection();
|
||||
else
|
||||
this._closeSelection();
|
||||
} else if (this._activeType == this.TYPE_CURSOR) {
|
||||
// attachCaret() is called in the "Gesture:SingleTap" handler in BrowserEventHandler
|
||||
// We're guaranteed to call this first, because this observer was added last
|
||||
this._closeSelection();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Tab:Selected":
|
||||
this._closeSelection();
|
||||
break;
|
||||
|
||||
|
||||
case "Window:Resize": {
|
||||
if (this._activeType == this.TYPE_SELECTION) {
|
||||
// Knowing when the page is done drawing is hard, so let's just cancel
|
||||
|
@ -1628,7 +1628,8 @@ var NativeWindow = {
|
||||
type: "PageActions:Add",
|
||||
id: id,
|
||||
title: aOptions.title,
|
||||
icon: resolveGeckoURI(aOptions.icon)
|
||||
icon: resolveGeckoURI(aOptions.icon),
|
||||
important: "important" in aOptions ? aOptions.important : false
|
||||
});
|
||||
this._items[id] = {
|
||||
clickCallback: aOptions.clickCallback,
|
||||
@ -7157,14 +7158,16 @@ let Reader = {
|
||||
this.pageAction.id = NativeWindow.pageactions.add({
|
||||
title: Strings.browser.GetStringFromName("readerMode.exit"),
|
||||
icon: "drawable://reader_active",
|
||||
clickCallback: this.pageAction.readerModeCallback
|
||||
clickCallback: this.pageAction.readerModeCallback,
|
||||
important: true
|
||||
});
|
||||
} else if (tab.readerEnabled) {
|
||||
this.pageAction.id = NativeWindow.pageactions.add({
|
||||
title: Strings.browser.GetStringFromName("readerMode.enter"),
|
||||
icon: "drawable://reader",
|
||||
clickCallback:this.pageAction.readerModeCallback,
|
||||
longClickCallback: this.pageAction.readerModeActiveCallback
|
||||
longClickCallback: this.pageAction.readerModeActiveCallback,
|
||||
important: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -346,7 +346,7 @@ add_test(function test_bso_if_unmodified_since() {
|
||||
let coll = server.user("123").collection("test");
|
||||
do_check_neq(coll, null);
|
||||
|
||||
let time = coll.timestamp;
|
||||
let time = coll.bso("bso").modified;
|
||||
|
||||
_("Ensure we get a 412 for specified times older than server time.");
|
||||
let request = localRequest(server, "/2.0/123/storage/test/bso",
|
||||
|
@ -3366,6 +3366,18 @@
|
||||
"n_buckets": "1000",
|
||||
"description": "The time (in milliseconds) that it took a 'prototypeAndProperties' request to go round trip."
|
||||
},
|
||||
"DEVTOOLS_DEBUGGER_RDP_LOCAL_PROTOTYPESANDPROPERTIES_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
"n_buckets": "1000",
|
||||
"description": "The time (in milliseconds) that it took a 'prototypesAndProperties' request to go round trip."
|
||||
},
|
||||
"DEVTOOLS_DEBUGGER_RDP_REMOTE_PROTOTYPESANDPROPERTIES_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
"n_buckets": "1000",
|
||||
"description": "The time (in milliseconds) that it took a 'prototypesAndProperties' request to go round trip."
|
||||
},
|
||||
"DEVTOOLS_DEBUGGER_RDP_LOCAL_PROPERTY_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
|
@ -590,11 +590,11 @@
|
||||
if (files.length == TRUNCATED_FILE_COUNT + 1) {
|
||||
titleText += "\n" + files[TRUNCATED_FILE_COUNT].name;
|
||||
} else if (files.length > TRUNCATED_FILE_COUNT + 1) {
|
||||
let xmoreStr = bundle.GetStringFromName("AndXMoreFiles");
|
||||
let xmoreStr = bundle.GetStringFromName("AndNMoreFiles");
|
||||
let xmoreNum = files.length - TRUNCATED_FILE_COUNT;
|
||||
let tmp = {};
|
||||
Components.utils.import("resource://gre/modules/PluralForm.jsm", tmp);
|
||||
let andXMoreStr = tmp.PluralForm.get(xmoreNum, xmoreStr).replace("%S", xmoreNum);
|
||||
let andXMoreStr = tmp.PluralForm.get(xmoreNum, xmoreStr).replace("#1", xmoreNum);
|
||||
titleText += "\n" + andXMoreStr;
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,26 @@
|
||||
|
||||
/* General utilities used throughout devtools. */
|
||||
|
||||
/* Turn the error e into a string, without fail. */
|
||||
/**
|
||||
* Turn the error |aError| into a string, without fail.
|
||||
*/
|
||||
this.safeErrorString = function safeErrorString(aError) {
|
||||
try {
|
||||
var s = aError.toString();
|
||||
if (typeof s === "string")
|
||||
return s;
|
||||
let errorString = aError.toString();
|
||||
if (typeof errorString === "string") {
|
||||
// Attempt to attach a stack to |errorString|. If it throws an error, or
|
||||
// isn't a string, don't use it.
|
||||
try {
|
||||
if (aError.stack) {
|
||||
let stack = aError.stack.toString();
|
||||
if (typeof stack === "string") {
|
||||
errorString += "\nStack: " + stack;
|
||||
}
|
||||
}
|
||||
} catch (ee) { }
|
||||
|
||||
return errorString;
|
||||
}
|
||||
} catch (ee) { }
|
||||
|
||||
return "<failed trying to find error description>";
|
||||
@ -22,9 +36,6 @@ this.safeErrorString = function safeErrorString(aError) {
|
||||
*/
|
||||
this.reportException = function reportException(aWho, aException) {
|
||||
let msg = aWho + " threw an exception: " + safeErrorString(aException);
|
||||
if (aException.stack) {
|
||||
msg += "\nCall stack:\n" + aException.stack;
|
||||
}
|
||||
|
||||
dump(msg + "\n");
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
# 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/.
|
||||
|
||||
DEPTH = @DEPTH@
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = @relativesrcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
XPCSHELL_TESTS = unit
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
@ -5,3 +5,5 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
MODULE = 'test_webapps_actor'
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
|
||||
|
@ -3,4 +3,8 @@ head = head_apps.js
|
||||
tail = tail_apps.js
|
||||
|
||||
[test_webappsActor.js]
|
||||
# Persistent failures.
|
||||
skip-if = true
|
||||
[test_appInstall.js]
|
||||
# Persistent failures.
|
||||
skip-if = true
|
||||
|
@ -1626,8 +1626,22 @@ ThreadClient.prototype = {
|
||||
|
||||
return this._threadGrips[aForm.actor] = new SourceClient(this._client,
|
||||
aForm);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Request the prototype and own properties of mutlipleObjects.
|
||||
*
|
||||
* @param aOnResponse function
|
||||
* Called with the request's response.
|
||||
* @param actors [string]
|
||||
* List of actor ID of the queried objects.
|
||||
*/
|
||||
getPrototypesAndProperties: DebuggerClient.requester({
|
||||
type: "prototypesAndProperties",
|
||||
actors: args(0)
|
||||
}, {
|
||||
telemetry: "PROTOTYPESANDPROPERTIES"
|
||||
})
|
||||
};
|
||||
|
||||
eventSource(ThreadClient.prototype);
|
||||
|
@ -4,6 +4,8 @@
|
||||
# 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/.
|
||||
|
||||
TEST_DIRS += ['tests']
|
||||
|
||||
PARALLEL_DIRS += [
|
||||
'server',
|
||||
'client',
|
||||
|
@ -2070,6 +2070,34 @@ ThreadActor.prototype = {
|
||||
return true;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Get prototypes and properties of multiple objects.
|
||||
*/
|
||||
onPrototypesAndProperties: function TA_onPrototypesAndProperties(aRequest) {
|
||||
let result = {};
|
||||
for (let actorID of aRequest.actors) {
|
||||
// This code assumes that there are no lazily loaded actors returned
|
||||
// by this call.
|
||||
let actor = this.conn.getActor(actorID);
|
||||
if (!actor) {
|
||||
return { from: this.actorID,
|
||||
error: "noSuchActor" };
|
||||
}
|
||||
let handler = actor.onPrototypeAndProperties;
|
||||
if (!handler) {
|
||||
return { from: this.actorID,
|
||||
error: "unrecognizedPacketType",
|
||||
message: ('Actor "' + actorID +
|
||||
'" does not recognize the packet type ' +
|
||||
'"prototypeAndProperties"') };
|
||||
}
|
||||
result[actorID] = handler.call(actor, {});
|
||||
}
|
||||
return { from: this.actorID,
|
||||
actors: result };
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ThreadActor.prototype.requestTypes = {
|
||||
@ -2084,7 +2112,8 @@ ThreadActor.prototype.requestTypes = {
|
||||
"releaseMany": ThreadActor.prototype.onReleaseMany,
|
||||
"setBreakpoint": ThreadActor.prototype.onSetBreakpoint,
|
||||
"sources": ThreadActor.prototype.onSources,
|
||||
"threadGrips": ThreadActor.prototype.onThreadGrips
|
||||
"threadGrips": ThreadActor.prototype.onThreadGrips,
|
||||
"prototypesAndProperties": ThreadActor.prototype.onPrototypesAndProperties
|
||||
};
|
||||
|
||||
|
||||
|
@ -535,7 +535,7 @@ function timeSinceTraceStarted({ startTime }) {
|
||||
* The type of completion value to serialize (return, throw, or yield).
|
||||
*/
|
||||
function serializeCompletionValue(aType, { value }) {
|
||||
if (typeof value[aType] === "undefined") {
|
||||
if (!Object.hasOwnProperty.call(value, aType)) {
|
||||
return undefined;
|
||||
}
|
||||
return createValueGrip(value[aType], true);
|
||||
|
@ -834,13 +834,12 @@ DebuggerServerConnection.prototype = {
|
||||
},
|
||||
|
||||
_unknownError: function DSC__unknownError(aPrefix, aError) {
|
||||
let errorString = safeErrorString(aError);
|
||||
errorString += "\n" + aError.stack;
|
||||
let errorString = aPrefix + ": " + safeErrorString(aError);
|
||||
Cu.reportError(errorString);
|
||||
dumpn(errorString);
|
||||
return {
|
||||
error: "unknownError",
|
||||
message: (aPrefix + "': " + errorString)
|
||||
message: errorString
|
||||
};
|
||||
},
|
||||
|
||||
|
65
toolkit/devtools/server/tests/unit/test_objectgrips-09.js
Normal file
65
toolkit/devtools/server/tests/unit/test_objectgrips-09.js
Normal file
@ -0,0 +1,65 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/**
|
||||
* This tests exercises getProtypesAndProperties message accepted
|
||||
* by a thread actor.
|
||||
*/
|
||||
|
||||
var gDebuggee;
|
||||
var gClient;
|
||||
var gThreadClient;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
initTestDebuggerServer();
|
||||
gDebuggee = addTestGlobal("test-grips");
|
||||
gDebuggee.eval(function stopMe(arg1, arg2) {
|
||||
debugger;
|
||||
}.toString());
|
||||
|
||||
gClient = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
gClient.connect(function() {
|
||||
attachTestTabAndResume(gClient, "test-grips", function(aResponse, aTabClient, aThreadClient) {
|
||||
gThreadClient = aThreadClient;
|
||||
test_object_grip();
|
||||
});
|
||||
});
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function test_object_grip()
|
||||
{
|
||||
gThreadClient.addOneTimeListener("paused", function(aEvent, aPacket) {
|
||||
let args = aPacket.frame.arguments;
|
||||
|
||||
gThreadClient.getPrototypesAndProperties([args[0].actor, args[1].actor], function(aResponse) {
|
||||
let obj1 = aResponse.actors[args[0].actor];
|
||||
let obj2 = aResponse.actors[args[1].actor];
|
||||
do_check_eq(obj1.ownProperties.x.configurable, true);
|
||||
do_check_eq(obj1.ownProperties.x.enumerable, true);
|
||||
do_check_eq(obj1.ownProperties.x.writable, true);
|
||||
do_check_eq(obj1.ownProperties.x.value, 10);
|
||||
|
||||
do_check_eq(obj1.ownProperties.y.configurable, true);
|
||||
do_check_eq(obj1.ownProperties.y.enumerable, true);
|
||||
do_check_eq(obj1.ownProperties.y.writable, true);
|
||||
do_check_eq(obj1.ownProperties.y.value, "kaiju");
|
||||
|
||||
do_check_eq(obj2.ownProperties.z.configurable, true);
|
||||
do_check_eq(obj2.ownProperties.z.enumerable, true);
|
||||
do_check_eq(obj2.ownProperties.z.writable, true);
|
||||
do_check_eq(obj2.ownProperties.z.value, 123);
|
||||
|
||||
do_check_true(obj1.prototype != undefined);
|
||||
do_check_true(obj2.prototype != undefined);
|
||||
|
||||
gThreadClient.resume(function() {
|
||||
finishClient(gClient);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
gDebuggee.eval("stopMe({ x: 10, y: 'kaiju'}, { z: 123 })");
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that objects are correctly serialized and sent in exitedFrame
|
||||
* packets.
|
||||
* Tests that values are correctly serialized and sent in enteredFrame
|
||||
* and exitedFrame packets.
|
||||
*/
|
||||
|
||||
let {defer} = devtools.require("sdk/core/promise");
|
||||
@ -30,73 +30,8 @@ function run_test()
|
||||
|
||||
function test_enter_exit_frame()
|
||||
{
|
||||
gTraceClient.addListener("exitedFrame", function(aEvent, aPacket) {
|
||||
if (aPacket.sequence === 3) {
|
||||
let obj = aPacket.return;
|
||||
do_check_eq(typeof obj, "object",
|
||||
'exitedFrame response should have return value');
|
||||
do_check_eq(typeof obj.prototype, "object",
|
||||
'return value should have prototype');
|
||||
do_check_eq(typeof obj.ownProperties, "object",
|
||||
'return value should have ownProperties list');
|
||||
do_check_eq(typeof obj.safeGetterValues, "object",
|
||||
'return value should have safeGetterValues');
|
||||
|
||||
do_check_eq(typeof obj.ownProperties.num, "object",
|
||||
'return value should have property "num"');
|
||||
do_check_eq(typeof obj.ownProperties.str, "object",
|
||||
'return value should have property "str"');
|
||||
do_check_eq(typeof obj.ownProperties.bool, "object",
|
||||
'return value should have property "bool"');
|
||||
do_check_eq(typeof obj.ownProperties.undef, "object",
|
||||
'return value should have property "undef"');
|
||||
do_check_eq(typeof obj.ownProperties.undef.value, "object",
|
||||
'return value property "undef" should be a grip');
|
||||
do_check_eq(typeof obj.ownProperties.nil, "object",
|
||||
'return value should have property "nil"');
|
||||
do_check_eq(typeof obj.ownProperties.nil.value, "object",
|
||||
'return value property "nil" should be a grip');
|
||||
do_check_eq(typeof obj.ownProperties.obj, "object",
|
||||
'return value should have property "obj"');
|
||||
do_check_eq(typeof obj.ownProperties.obj.value, "object",
|
||||
'return value property "obj" should be a grip');
|
||||
do_check_eq(typeof obj.ownProperties.arr, "object",
|
||||
'return value should have property "arr"');
|
||||
do_check_eq(typeof obj.ownProperties.arr.value, "object",
|
||||
'return value property "arr" should be a grip');
|
||||
do_check_eq(typeof obj.ownProperties.inf, "object",
|
||||
'return value should have property "inf"');
|
||||
do_check_eq(typeof obj.ownProperties.inf.value, "object",
|
||||
'return value property "inf" should be a grip');
|
||||
do_check_eq(typeof obj.ownProperties.ninf, "object",
|
||||
'return value should have property "ninf"');
|
||||
do_check_eq(typeof obj.ownProperties.ninf.value, "object",
|
||||
'return value property "ninf" should be a grip');
|
||||
do_check_eq(typeof obj.ownProperties.nan, "object",
|
||||
'return value should have property "nan"');
|
||||
do_check_eq(typeof obj.ownProperties.nan.value, "object",
|
||||
'return value property "nan" should be a grip');
|
||||
do_check_eq(typeof obj.ownProperties.nzero, "object",
|
||||
'return value should have property "nzero"');
|
||||
do_check_eq(typeof obj.ownProperties.nzero.value, "object",
|
||||
'return value property "nzero" should be a grip');
|
||||
|
||||
do_check_eq(obj.prototype.type, "object");
|
||||
do_check_eq(obj.ownProperties.num.value, 25);
|
||||
do_check_eq(obj.ownProperties.str.value, "foo");
|
||||
do_check_eq(obj.ownProperties.bool.value, false);
|
||||
do_check_eq(obj.ownProperties.undef.value.type, "undefined");
|
||||
do_check_eq(obj.ownProperties.nil.value.type, "null");
|
||||
do_check_eq(obj.ownProperties.obj.value.type, "object");
|
||||
do_check_eq(obj.ownProperties.obj.value.class, "Object");
|
||||
do_check_eq(obj.ownProperties.arr.value.type, "object");
|
||||
do_check_eq(obj.ownProperties.arr.value.class, "Array");
|
||||
do_check_eq(obj.ownProperties.inf.value.type, "Infinity");
|
||||
do_check_eq(obj.ownProperties.ninf.value.type, "-Infinity");
|
||||
do_check_eq(obj.ownProperties.nan.value.type, "NaN");
|
||||
do_check_eq(obj.ownProperties.nzero.value.type, "-0");
|
||||
}
|
||||
});
|
||||
gTraceClient.addListener("enteredFrame", check_packet);
|
||||
gTraceClient.addListener("exitedFrame", check_packet);
|
||||
|
||||
start_trace()
|
||||
.then(eval_code)
|
||||
@ -109,32 +44,45 @@ function test_enter_exit_frame()
|
||||
function start_trace()
|
||||
{
|
||||
let deferred = defer();
|
||||
gTraceClient.startTrace(["return"], null, function() { deferred.resolve(); });
|
||||
gTraceClient.startTrace(["arguments", "return"], null, function() { deferred.resolve(); });
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function eval_code()
|
||||
{
|
||||
gDebuggee.eval("(" + function() {
|
||||
function foo() {
|
||||
let obj = {};
|
||||
obj.self = obj;
|
||||
|
||||
return {
|
||||
num: 25,
|
||||
str: "foo",
|
||||
bool: false,
|
||||
undef: undefined,
|
||||
nil: null,
|
||||
obj: obj,
|
||||
arr: [1,2,3,4,5],
|
||||
inf: Infinity,
|
||||
ninf: -Infinity,
|
||||
nan: NaN,
|
||||
nzero: -0
|
||||
};
|
||||
function identity(x) {
|
||||
return x;
|
||||
}
|
||||
foo();
|
||||
|
||||
let circular = {};
|
||||
circular.self = circular;
|
||||
|
||||
let obj = {
|
||||
num: 0,
|
||||
str: "foo",
|
||||
bool: false,
|
||||
undef: undefined,
|
||||
nil: null,
|
||||
inf: Infinity,
|
||||
ninf: -Infinity,
|
||||
nan: NaN,
|
||||
nzero: -0,
|
||||
obj: circular,
|
||||
arr: [1,2,3,4,5]
|
||||
};
|
||||
|
||||
identity();
|
||||
identity(0);
|
||||
identity("");
|
||||
identity(false);
|
||||
identity(undefined);
|
||||
identity(null);
|
||||
identity(Infinity);
|
||||
identity(-Infinity);
|
||||
identity(NaN);
|
||||
identity(-0);
|
||||
identity(obj);
|
||||
} + ")()");
|
||||
}
|
||||
|
||||
@ -144,3 +92,132 @@ function stop_trace()
|
||||
gTraceClient.stopTrace(null, function() { deferred.resolve(); });
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function check_packet(aEvent, aPacket)
|
||||
{
|
||||
let value = (aPacket.type === "enteredFrame" && aPacket.arguments)
|
||||
? aPacket.arguments[0]
|
||||
: aPacket.return;
|
||||
switch(aPacket.sequence) {
|
||||
case 2:
|
||||
do_check_eq(typeof aPacket.arguments, "object",
|
||||
"zero-argument function call should send arguments list");
|
||||
do_check_eq(aPacket.arguments.length, 0,
|
||||
"zero-argument function call should send zero-length arguments list");
|
||||
break;
|
||||
case 3:
|
||||
check_value(value, "object", "undefined");
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
check_value(value, "number", 0);
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
check_value(value, "string", "");
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
check_value(value, "boolean", false);
|
||||
break;
|
||||
case 10:
|
||||
case 11:
|
||||
check_value(value, "object", "undefined");
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
check_value(value, "object", "null");
|
||||
break;
|
||||
case 14:
|
||||
case 15:
|
||||
check_value(value, "object", "Infinity");
|
||||
break;
|
||||
case 16:
|
||||
case 17:
|
||||
check_value(value, "object", "-Infinity");
|
||||
break;
|
||||
case 18:
|
||||
case 19:
|
||||
check_value(value, "object", "NaN");
|
||||
break;
|
||||
case 20:
|
||||
case 21:
|
||||
check_value(value, "object", "-0");
|
||||
break;
|
||||
case 22:
|
||||
case 23:
|
||||
check_object(aPacket.type, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function check_value(aActual, aExpectedType, aExpectedValue)
|
||||
{
|
||||
do_check_eq(typeof aActual, aExpectedType);
|
||||
do_check_eq(aExpectedType === "object" ? aActual.type : aActual, aExpectedValue);
|
||||
}
|
||||
|
||||
function check_object(aType, aObj) {
|
||||
do_check_eq(typeof aObj, "object",
|
||||
'serialized object should be present in packet');
|
||||
do_check_eq(typeof aObj.prototype, "object",
|
||||
'serialized object should have prototype');
|
||||
do_check_eq(typeof aObj.ownProperties, "object",
|
||||
'serialized object should have ownProperties list');
|
||||
do_check_eq(typeof aObj.safeGetterValues, "object",
|
||||
'serialized object should have safeGetterValues');
|
||||
|
||||
do_check_eq(typeof aObj.ownProperties.num, "object",
|
||||
'serialized object should have property "num"');
|
||||
do_check_eq(typeof aObj.ownProperties.str, "object",
|
||||
'serialized object should have property "str"');
|
||||
do_check_eq(typeof aObj.ownProperties.bool, "object",
|
||||
'serialized object should have property "bool"');
|
||||
do_check_eq(typeof aObj.ownProperties.undef, "object",
|
||||
'serialized object should have property "undef"');
|
||||
do_check_eq(typeof aObj.ownProperties.undef.value, "object",
|
||||
'serialized object property "undef" should be a grip');
|
||||
do_check_eq(typeof aObj.ownProperties.nil, "object",
|
||||
'serialized object should have property "nil"');
|
||||
do_check_eq(typeof aObj.ownProperties.nil.value, "object",
|
||||
'serialized object property "nil" should be a grip');
|
||||
do_check_eq(typeof aObj.ownProperties.obj, "object",
|
||||
'serialized object should have property "aObj"');
|
||||
do_check_eq(typeof aObj.ownProperties.obj.value, "object",
|
||||
'serialized object property "aObj" should be a grip');
|
||||
do_check_eq(typeof aObj.ownProperties.arr, "object",
|
||||
'serialized object should have property "arr"');
|
||||
do_check_eq(typeof aObj.ownProperties.arr.value, "object",
|
||||
'serialized object property "arr" should be a grip');
|
||||
do_check_eq(typeof aObj.ownProperties.inf, "object",
|
||||
'serialized object should have property "inf"');
|
||||
do_check_eq(typeof aObj.ownProperties.inf.value, "object",
|
||||
'serialized object property "inf" should be a grip');
|
||||
do_check_eq(typeof aObj.ownProperties.ninf, "object",
|
||||
'serialized object should have property "ninf"');
|
||||
do_check_eq(typeof aObj.ownProperties.ninf.value, "object",
|
||||
'serialized object property "ninf" should be a grip');
|
||||
do_check_eq(typeof aObj.ownProperties.nan, "object",
|
||||
'serialized object should have property "nan"');
|
||||
do_check_eq(typeof aObj.ownProperties.nan.value, "object",
|
||||
'serialized object property "nan" should be a grip');
|
||||
do_check_eq(typeof aObj.ownProperties.nzero, "object",
|
||||
'serialized object should have property "nzero"');
|
||||
do_check_eq(typeof aObj.ownProperties.nzero.value, "object",
|
||||
'serialized object property "nzero" should be a grip');
|
||||
|
||||
do_check_eq(aObj.prototype.type, "object");
|
||||
do_check_eq(aObj.ownProperties.num.value, 0);
|
||||
do_check_eq(aObj.ownProperties.str.value, "foo");
|
||||
do_check_eq(aObj.ownProperties.bool.value, false);
|
||||
do_check_eq(aObj.ownProperties.undef.value.type, "undefined");
|
||||
do_check_eq(aObj.ownProperties.nil.value.type, "null");
|
||||
do_check_eq(aObj.ownProperties.obj.value.type, "object");
|
||||
do_check_eq(aObj.ownProperties.obj.value.class, "Object");
|
||||
do_check_eq(aObj.ownProperties.arr.value.type, "object");
|
||||
do_check_eq(aObj.ownProperties.arr.value.class, "Array");
|
||||
do_check_eq(aObj.ownProperties.inf.value.type, "Infinity");
|
||||
do_check_eq(aObj.ownProperties.ninf.value.type, "-Infinity");
|
||||
do_check_eq(aObj.ownProperties.nan.value.type, "NaN");
|
||||
do_check_eq(aObj.ownProperties.nzero.value.type, "-0");
|
||||
}
|
||||
|
@ -127,6 +127,7 @@ reason = bug 820380
|
||||
[test_objectgrips-06.js]
|
||||
[test_objectgrips-07.js]
|
||||
[test_objectgrips-08.js]
|
||||
[test_objectgrips-09.js]
|
||||
[test_interrupt.js]
|
||||
[test_stepping-01.js]
|
||||
[test_stepping-02.js]
|
||||
|
9
toolkit/devtools/tests/moz.build
Normal file
9
toolkit/devtools/tests/moz.build
Normal file
@ -0,0 +1,9 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
MODULE = 'test_devtools'
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
|
42
toolkit/devtools/tests/unit/head_devtools.js
Normal file
42
toolkit/devtools/tests/unit/head_devtools.js
Normal file
@ -0,0 +1,42 @@
|
||||
"use strict";
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
|
||||
|
||||
// Register a console listener, so console messages don't just disappear
|
||||
// into the ether.
|
||||
let errorCount = 0;
|
||||
let listener = {
|
||||
observe: function (aMessage) {
|
||||
errorCount++;
|
||||
try {
|
||||
// If we've been given an nsIScriptError, then we can print out
|
||||
// something nicely formatted, for tools like Emacs to pick up.
|
||||
var scriptError = aMessage.QueryInterface(Ci.nsIScriptError);
|
||||
dump(aMessage.sourceName + ":" + aMessage.lineNumber + ": " +
|
||||
scriptErrorFlagsToKind(aMessage.flags) + ": " +
|
||||
aMessage.errorMessage + "\n");
|
||||
var string = aMessage.errorMessage;
|
||||
} catch (x) {
|
||||
// Be a little paranoid with message, as the whole goal here is to lose
|
||||
// no information.
|
||||
try {
|
||||
var string = "" + aMessage.message;
|
||||
} catch (x) {
|
||||
var string = "<error converting error message to string>";
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we exit all nested event loops so that the test can finish.
|
||||
while (DebuggerServer.xpcInspector.eventLoopNestLevel > 0) {
|
||||
DebuggerServer.xpcInspector.exitNestedEventLoop();
|
||||
}
|
||||
do_throw("head_dbg.js got console message: " + string + "\n");
|
||||
}
|
||||
};
|
||||
|
||||
let consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
|
||||
consoleService.registerListener(listener);
|
54
toolkit/devtools/tests/unit/test_safeErrorString.js
Normal file
54
toolkit/devtools/tests/unit/test_safeErrorString.js
Normal file
@ -0,0 +1,54 @@
|
||||
/* -*- Mode: js; js-indent-level: 2; -*- */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test DevToolsUtils.safeErrorString
|
||||
|
||||
function run_test() {
|
||||
test_with_error();
|
||||
test_with_tricky_error();
|
||||
test_with_string();
|
||||
test_with_thrower();
|
||||
test_with_psychotic();
|
||||
}
|
||||
|
||||
function test_with_error() {
|
||||
let s = DevToolsUtils.safeErrorString(new Error("foo bar"));
|
||||
// Got the message.
|
||||
do_check_true(s.contains("foo bar"));
|
||||
// Got the stack.
|
||||
do_check_true(s.contains("test_with_error"))
|
||||
do_check_true(s.contains("test_safeErrorString.js"));
|
||||
}
|
||||
|
||||
function test_with_tricky_error() {
|
||||
let e = new Error("batman");
|
||||
e.stack = { toString: Object.create(null) };
|
||||
let s = DevToolsUtils.safeErrorString(e);
|
||||
// Still got the message, despite a bad stack property.
|
||||
do_check_true(s.contains("batman"));
|
||||
}
|
||||
|
||||
function test_with_string() {
|
||||
let s = DevToolsUtils.safeErrorString("not really an error");
|
||||
// Still get the message.
|
||||
do_check_true(s.contains("not really an error"));
|
||||
}
|
||||
|
||||
function test_with_thrower() {
|
||||
let s = DevToolsUtils.safeErrorString({
|
||||
toString: () => {
|
||||
throw new Error("Muahahaha");
|
||||
}
|
||||
});
|
||||
// Still don't fail, get string back.
|
||||
do_check_eq(typeof s, "string");
|
||||
}
|
||||
|
||||
function test_with_psychotic() {
|
||||
let s = DevToolsUtils.safeErrorString({
|
||||
toString: () => Object.create(null)
|
||||
});
|
||||
// Still get a string out, and no exceptions thrown
|
||||
do_check_eq(typeof s, "string");
|
||||
}
|
5
toolkit/devtools/tests/unit/xpcshell.ini
Normal file
5
toolkit/devtools/tests/unit/xpcshell.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
head = head_devtools.js
|
||||
tail =
|
||||
|
||||
[test_safeErrorString.js]
|
@ -2207,6 +2207,11 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason)
|
||||
if (mSaver) {
|
||||
mSaver->Finish(aReason);
|
||||
mSaver = nullptr;
|
||||
} else if (mStopRequestIssued && mTempFile) {
|
||||
// This branch can only happen when the user cancels the helper app dialog
|
||||
// when the request has completed. The temp file has to be removed here,
|
||||
// because mSaver has been released at that time with the temp file left.
|
||||
(void)mTempFile->Remove(false);
|
||||
}
|
||||
|
||||
// Break our reference cycle with the helper app dialog (set up in
|
||||
|
@ -6,6 +6,8 @@ const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const UNKNOWN_FAIL = ["geolocation", "desktop-notification"];
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://webapprt/modules/WebappRT.jsm");
|
||||
@ -16,64 +18,83 @@ ContentPermission.prototype = {
|
||||
classID: Components.ID("{07ef5b2e-88fb-47bd-8cec-d3b0bef11ac4}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]),
|
||||
|
||||
_getChromeWindow: function(aWindow) {
|
||||
return aWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow)
|
||||
.QueryInterface(Ci.nsIDOMChromeWindow);
|
||||
},
|
||||
|
||||
prompt: function(request) {
|
||||
// Only handle geolocation requests for now
|
||||
if (request.type != "geolocation") {
|
||||
return;
|
||||
// Reuse any remembered permission preferences
|
||||
let result =
|
||||
Services.perms.testExactPermissionFromPrincipal(request.principal,
|
||||
request.type);
|
||||
|
||||
// We used to use the name "geo" for the geolocation permission, now we're
|
||||
// using "geolocation". We need to check both to support existing
|
||||
// installations.
|
||||
if ((result == Ci.nsIPermissionManager.UNKNOWN_ACTION ||
|
||||
result == Ci.nsIPermissionManager.PROMPT_ACTION) &&
|
||||
request.type == "geolocation") {
|
||||
let geoResult = Services.perms.testExactPermission(request.principal.URI,
|
||||
"geo");
|
||||
// We override the result only if the "geo" permission was allowed or
|
||||
// denied.
|
||||
if (geoResult == Ci.nsIPermissionManager.ALLOW_ACTION ||
|
||||
geoResult == Ci.nsIPermissionManager.DENY_ACTION) {
|
||||
result = geoResult;
|
||||
}
|
||||
}
|
||||
|
||||
// Reuse any remembered permission preferences
|
||||
let result = Services.perms.testExactPermissionFromPrincipal(request.principal, "geo");
|
||||
if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
request.allow();
|
||||
return;
|
||||
}
|
||||
else if (result == Ci.nsIPermissionManager.DENY_ACTION) {
|
||||
} else if (result == Ci.nsIPermissionManager.DENY_ACTION ||
|
||||
(result == Ci.nsIPermissionManager.UNKNOWN_ACTION &&
|
||||
UNKNOWN_FAIL.indexOf(request.type) >= 0)) {
|
||||
request.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
function getChromeWindow(aWindow) {
|
||||
var chromeWin = aWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow)
|
||||
.QueryInterface(Ci.nsIDOMChromeWindow);
|
||||
return chromeWin;
|
||||
}
|
||||
|
||||
// Display a prompt at the top level
|
||||
let {name} = WebappRT.config.app.manifest;
|
||||
let requestingWindow = request.window.top;
|
||||
let chromeWin = getChromeWindow(requestingWindow);
|
||||
let chromeWin = this._getChromeWindow(requestingWindow);
|
||||
let bundle = Services.strings.createBundle("chrome://webapprt/locale/webapp.properties");
|
||||
|
||||
// Construct a prompt with share/don't and remember checkbox
|
||||
let remember = {value: false};
|
||||
let choice = Services.prompt.confirmEx(
|
||||
chromeWin,
|
||||
bundle.formatStringFromName("geolocation.title", [name], 1),
|
||||
bundle.GetStringFromName("geolocation.description"),
|
||||
bundle.formatStringFromName(request.type + ".title", [name], 1),
|
||||
bundle.GetStringFromName(request.type + ".description"),
|
||||
// Set both buttons to strings with the cancel button being default
|
||||
Ci.nsIPromptService.BUTTON_POS_1_DEFAULT |
|
||||
Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_0 |
|
||||
Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_1,
|
||||
bundle.GetStringFromName("geolocation.sharelocation"),
|
||||
bundle.GetStringFromName("geolocation.dontshare"),
|
||||
bundle.GetStringFromName(request.type + ".allow"),
|
||||
bundle.GetStringFromName(request.type + ".deny"),
|
||||
null,
|
||||
bundle.GetStringFromName("geolocation.remember"),
|
||||
bundle.GetStringFromName(request.type + ".remember"),
|
||||
remember);
|
||||
|
||||
// Persist the choice if the user wants to remember
|
||||
let action = Ci.nsIPermissionManager.ALLOW_ACTION;
|
||||
if (choice != 0) {
|
||||
action = Ci.nsIPermissionManager.DENY_ACTION;
|
||||
}
|
||||
|
||||
if (remember.value) {
|
||||
let action = Ci.nsIPermissionManager.ALLOW_ACTION;
|
||||
if (choice != 0) {
|
||||
action = Ci.nsIPermissionManager.DENY_ACTION;
|
||||
}
|
||||
Services.perms.addFromPrincipal(request.principal, "geo", action);
|
||||
// Persist the choice if the user wants to remember
|
||||
Services.perms.addFromPrincipal(request.principal, request.type, action);
|
||||
} else {
|
||||
// Otherwise allow the permission for the current session
|
||||
Services.perms.addFromPrincipal(request.principal, request.type, action,
|
||||
Ci.nsIPermissionManager.EXPIRE_SESSION);
|
||||
}
|
||||
|
||||
// Trigger the selected choice
|
||||
|
@ -45,7 +45,7 @@
|
||||
<key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
|
||||
<key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel"/>
|
||||
<key id="key_quitApplication"
|
||||
key="&quitApplicationCmdMac.key;"
|
||||
key="&quitApplicationCmdUnix.key;"
|
||||
command="cmd_quitApplication"
|
||||
modifiers="accel"/>
|
||||
<key id="key_hideThisAppCmdMac"
|
||||
|
@ -18,12 +18,14 @@
|
||||
<!-- On Mac, we create the Quit and Hide command labels dynamically,
|
||||
- using properties in window.properties, in order to include the name
|
||||
- of the webapp in the labels without creating a DTD file for it. -->
|
||||
<!ENTITY quitApplicationCmdMac.key "Q">
|
||||
<!ENTITY hideThisAppCmdMac.key "H">
|
||||
<!ENTITY hideOtherAppsCmdMac.label "Hide Others">
|
||||
<!ENTITY hideOtherAppsCmdMac.key "H">
|
||||
<!ENTITY showAllAppsCmdMac.label "Show All">
|
||||
|
||||
<!-- LOCALIZATION NOTE(quitApplicationCmdUnix.key): This keyboard shortcut is used by both Linux and OSX -->
|
||||
<!ENTITY quitApplicationCmdUnix.key "Q">
|
||||
|
||||
<!ENTITY editMenu.label "Edit">
|
||||
<!ENTITY editMenu.accesskey "E">
|
||||
<!ENTITY undoCmd.label "Undo">
|
||||
|
@ -20,10 +20,18 @@ hideApplicationCmdMac.label=Hide %S
|
||||
# the webapp.
|
||||
geolocation.title=%S - Share Location
|
||||
geolocation.description=Do you want to share your location?
|
||||
geolocation.sharelocation=Share Location
|
||||
geolocation.dontshare=Don't Share
|
||||
geolocation.allow=Share Location
|
||||
geolocation.deny=Don't Share
|
||||
geolocation.remember=Remember my choice
|
||||
|
||||
# LOCALIZATION NOTE (desktop-notification.title): %S will be replaced with the
|
||||
# name of the webapp.
|
||||
desktop-notification.title=%S - Show notifications
|
||||
desktop-notification.description=Do you want to allow notifications?
|
||||
desktop-notification.allow=Show
|
||||
desktop-notification.deny=Don't show
|
||||
desktop-notification.remember=Remember my choice
|
||||
|
||||
# LOCALIZATION NOTE (webapps.install.title): %S will be replaced with the name
|
||||
# of the webapp being installed.
|
||||
webapps.install.title=Install %S
|
||||
|
Loading…
Reference in New Issue
Block a user