зеркало из https://github.com/mozilla/gecko-dev.git
Merge the last PGO-green inbound changeset to m-c.
This commit is contained in:
Коммит
8612caaf5c
|
@ -635,6 +635,8 @@ pref("memory.free_dirty_pages", true);
|
|||
pref("wap.UAProf.url", "");
|
||||
pref("wap.UAProf.tagname", "x-wap-profile");
|
||||
|
||||
pref("layout.imagevisibility.enabled", false);
|
||||
|
||||
// Enable native identity (persona/browserid)
|
||||
pref("dom.identity.enabled", true);
|
||||
|
||||
|
|
|
@ -155,6 +155,11 @@ SettingsListener.observe('language.current', 'en-US', function(value) {
|
|||
});
|
||||
});
|
||||
|
||||
SettingsListener.observe('ril.mms.retrieval_mode', 'manual',
|
||||
function(value) {
|
||||
Services.prefs.setCharPref('dom.mms.retrieval_mode', value);
|
||||
});
|
||||
|
||||
SettingsListener.observe('ril.sms.strict7BitEncoding.enabled', false,
|
||||
function(value) {
|
||||
Services.prefs.setBoolPref('dom.sms.strict7BitEncoding', value);
|
||||
|
|
|
@ -31,7 +31,20 @@ let SocialUI = {
|
|||
SocialChatBar.update();
|
||||
});
|
||||
|
||||
Social.init(this._providerReady.bind(this));
|
||||
SocialChatBar.init();
|
||||
SocialShareButton.init();
|
||||
SocialMenu.init();
|
||||
SocialToolbar.init();
|
||||
SocialSidebar.init();
|
||||
|
||||
Social.init();
|
||||
// If social was previously initialized it isn't going to notify observers
|
||||
// about the provider being set or the list of providers changing, so
|
||||
// handle those now.
|
||||
if (Social.provider) {
|
||||
this.observe(null, "social:provider-set", Social.provider.origin);
|
||||
this.observe(null, "social:providers-changed", null);
|
||||
}
|
||||
},
|
||||
|
||||
// Called on window unload
|
||||
|
@ -48,18 +61,6 @@ let SocialUI = {
|
|||
Services.prefs.removeObserver("social.toast-notifications.enabled", this);
|
||||
},
|
||||
|
||||
// Called once, after window load, once Social.jsm's provider has been set.
|
||||
_providerReady: function SocialUI_providerReady() {
|
||||
this._updateActiveUI();
|
||||
this._updateMenuItems();
|
||||
|
||||
SocialChatBar.update();
|
||||
SocialShareButton.init();
|
||||
SocialMenu.populate();
|
||||
SocialToolbar.init();
|
||||
SocialSidebar.init();
|
||||
},
|
||||
|
||||
// Social.provider has changed, update any state that depends on it.
|
||||
// Note: this method is not called when Social.provider is first set, during
|
||||
// the first window load.
|
||||
|
@ -335,6 +336,8 @@ let SocialUI = {
|
|||
}
|
||||
|
||||
let SocialChatBar = {
|
||||
init: function() {
|
||||
},
|
||||
get chatbar() {
|
||||
return document.getElementById("pinnedchats");
|
||||
},
|
||||
|
@ -556,7 +559,6 @@ let SocialFlyout = {
|
|||
let SocialShareButton = {
|
||||
// Called once, after window load, when the Social.provider object is initialized
|
||||
init: function SSB_init() {
|
||||
this.updateProvider();
|
||||
},
|
||||
|
||||
// Called when the Social.provider changes
|
||||
|
@ -703,6 +705,9 @@ let SocialShareButton = {
|
|||
};
|
||||
|
||||
var SocialMenu = {
|
||||
init: function SocialMenu_init() {
|
||||
},
|
||||
|
||||
populate: function SocialMenu_populate() {
|
||||
let submenu = document.getElementById("menu_social-statusarea-popup");
|
||||
let ambientMenuItems = submenu.getElementsByClassName("ambient-menuitem");
|
||||
|
@ -740,8 +745,6 @@ var SocialToolbar = {
|
|||
let accesskey = gNavigatorBundle.getString("social.removeProvider.accesskey");
|
||||
let removeCommand = document.getElementById("Social:Remove");
|
||||
removeCommand.setAttribute("accesskey", accesskey);
|
||||
|
||||
this.updateProvider();
|
||||
this._dynamicResizer = new DynamicResizeWatcher();
|
||||
},
|
||||
|
||||
|
@ -1073,7 +1076,6 @@ var SocialSidebar = {
|
|||
Social.setErrorListener(sbrowser, this.setSidebarErrorMessage.bind(this));
|
||||
// setting isAppTab causes clicks on untargeted links to open new tabs
|
||||
sbrowser.docShell.isAppTab = true;
|
||||
this.update();
|
||||
},
|
||||
|
||||
// Whether the sidebar can be shown for this window.
|
||||
|
|
|
@ -13,43 +13,18 @@ var ContextMenuHandler = {
|
|||
// Events we catch from content during the bubbling phase
|
||||
addEventListener("contextmenu", this, false);
|
||||
addEventListener("pagehide", this, false);
|
||||
addEventListener("select", this, false);
|
||||
|
||||
// Messages we receive from browser
|
||||
// Command sent over from browser that only we can handle.
|
||||
addMessageListener("Browser:ContextCommand", this, false);
|
||||
// InvokeContextAtPoint is sent to us from browser's selection
|
||||
// overlay when it traps a contextmenu event. In response we
|
||||
// should invoke context menu logic at the point specified.
|
||||
addMessageListener("Browser:InvokeContextAtPoint", this, false);
|
||||
|
||||
this.popupNode = null;
|
||||
},
|
||||
|
||||
_getLinkURL: function ch_getLinkURL(aLink) {
|
||||
let href = aLink.href;
|
||||
if (href)
|
||||
return href;
|
||||
|
||||
href = aLink.getAttributeNS(kXLinkNamespace, "href");
|
||||
if (!href || !href.match(/\S/)) {
|
||||
// Without this we try to save as the current doc,
|
||||
// for example, HTML case also throws if empty
|
||||
throw "Empty href";
|
||||
}
|
||||
|
||||
return Util.makeURLAbsolute(aLink.baseURI, href);
|
||||
},
|
||||
|
||||
_getURI: function ch_getURI(aURL) {
|
||||
try {
|
||||
return Util.makeURI(aURL);
|
||||
} catch (ex) { }
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
_getProtocol: function ch_getProtocol(aURI) {
|
||||
if (aURI)
|
||||
return aURI.scheme;
|
||||
return null;
|
||||
},
|
||||
|
||||
handleEvent: function ch_handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "contextmenu":
|
||||
|
@ -58,12 +33,25 @@ var ContextMenuHandler = {
|
|||
case "pagehide":
|
||||
this.reset();
|
||||
break;
|
||||
case "select":
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage: function ch_receiveMessage(aMessage) {
|
||||
switch (aMessage.name) {
|
||||
case "Browser:ContextCommand":
|
||||
this._onContextCommand(aMessage);
|
||||
break;
|
||||
case "Browser:InvokeContextAtPoint":
|
||||
this._onContextAtPoint(aMessage);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Handler for commands send over from browser's ContextCommands.js
|
||||
* in response to certain context menu actions only we can handle.
|
||||
*/
|
||||
_onContextCommand: function _onContextCommand(aMessage) {
|
||||
let node = this.popupNode;
|
||||
let command = aMessage.json.command;
|
||||
|
||||
|
@ -97,6 +85,17 @@ var ContextMenuHandler = {
|
|||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Handler for selection overlay context menu events.
|
||||
*/
|
||||
_onContextAtPoint: function _onContextCommand(aMessage) {
|
||||
// we need to find popupNode as if the context menu were
|
||||
// invoked on underlying content.
|
||||
let elem = elementFromPoint(aMessage.json.xPos, aMessage.json.yPos);
|
||||
this._processPopupNode(elem, aMessage.json.xPos, aMessage.json.yPos,
|
||||
Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
|
||||
},
|
||||
|
||||
/******************************************************
|
||||
* Event handlers
|
||||
*/
|
||||
|
@ -106,7 +105,7 @@ var ContextMenuHandler = {
|
|||
this._target = null;
|
||||
},
|
||||
|
||||
// content contextmenu
|
||||
// content contextmenu handler
|
||||
_onContentContextMenu: function _onContentContextMenu(aEvent) {
|
||||
if (aEvent.defaultPrevented)
|
||||
return;
|
||||
|
@ -115,6 +114,80 @@ var ContextMenuHandler = {
|
|||
aEvent.stopPropagation();
|
||||
aEvent.preventDefault();
|
||||
|
||||
this._processPopupNode(aEvent.originalTarget, aEvent.clientX,
|
||||
aEvent.clientY, aEvent.mozInputSource);
|
||||
},
|
||||
|
||||
/******************************************************
|
||||
* ContextCommand handlers
|
||||
*/
|
||||
|
||||
_onSelectAll: function _onSelectAll() {
|
||||
if (this._isTextInput(this._target)) {
|
||||
// select all text in the input control
|
||||
this._target.select();
|
||||
} else {
|
||||
// select the entire document
|
||||
content.getSelection().selectAllChildren(content.document);
|
||||
}
|
||||
this.reset();
|
||||
},
|
||||
|
||||
_onPaste: function _onPaste() {
|
||||
// paste text if this is an input control
|
||||
if (this._isTextInput(this._target)) {
|
||||
let edit = this._target.QueryInterface(Ci.nsIDOMNSEditableElement);
|
||||
if (edit) {
|
||||
edit.editor.paste(Ci.nsIClipboard.kGlobalClipboard);
|
||||
} else {
|
||||
Util.dumpLn("error: target element does not support nsIDOMNSEditableElement");
|
||||
}
|
||||
}
|
||||
this.reset();
|
||||
},
|
||||
|
||||
_onCopyImage: function _onCopyImage() {
|
||||
Util.copyImageToClipboard(this._target);
|
||||
},
|
||||
|
||||
/******************************************************
|
||||
* Utility routines
|
||||
*/
|
||||
|
||||
/*
|
||||
* _translateToTopLevelWindow - Given a potential coordinate set within
|
||||
* a subframe, translate up to the parent window which is what front
|
||||
* end code expect.
|
||||
*/
|
||||
_translateToTopLevelWindow: function _translateToTopLevelWindow(aPopupNode) {
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
let element = aPopupNode;
|
||||
while (element &&
|
||||
element.ownerDocument &&
|
||||
element.ownerDocument.defaultView != content) {
|
||||
element = element.ownerDocument.defaultView.frameElement;
|
||||
let rect = element.getBoundingClientRect();
|
||||
offsetX += rect.left;
|
||||
offsetY += rect.top;
|
||||
}
|
||||
return { offsetX: offsetX, offsetY: offsetY };
|
||||
},
|
||||
|
||||
/*
|
||||
* _processPopupNode - Generate and send a Content:ContextMenu message
|
||||
* to browser detailing the underlying content types at this.popupNode.
|
||||
* Note the event we receive targets the sub frame (if there is one) of
|
||||
* the page.
|
||||
*/
|
||||
_processPopupNode: function _processPopupNode(aPopupNode, aX, aY, aInputSrc) {
|
||||
if (!aPopupNode)
|
||||
return;
|
||||
let { offsetX: offsetX, offsetY: offsetY } =
|
||||
this._translateToTopLevelWindow(aPopupNode);
|
||||
let popupNode = this.popupNode = aPopupNode;
|
||||
let imageUrl = "";
|
||||
|
||||
let state = {
|
||||
types: [],
|
||||
label: "",
|
||||
|
@ -122,13 +195,8 @@ var ContextMenuHandler = {
|
|||
linkTitle: "",
|
||||
linkProtocol: null,
|
||||
mediaURL: "",
|
||||
x: aEvent.x,
|
||||
y: aEvent.y
|
||||
};
|
||||
|
||||
let popupNode = this.popupNode = aEvent.originalTarget;
|
||||
let imageUrl = "";
|
||||
|
||||
// Do checks for nodes that never have children.
|
||||
if (popupNode.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
|
||||
// See if the user clicked on an image.
|
||||
|
@ -248,9 +316,9 @@ var ContextMenuHandler = {
|
|||
}
|
||||
|
||||
// populate position and event source
|
||||
state.xPos = aEvent.clientX;
|
||||
state.yPos = aEvent.clientY;
|
||||
state.source = aEvent.mozInputSource;
|
||||
state.xPos = offsetX + aX;
|
||||
state.yPos = offsetY + aY;
|
||||
state.source = aInputSrc;
|
||||
|
||||
for (let i = 0; i < this._types.length; i++)
|
||||
if (this._types[i].handler(state, popupNode))
|
||||
|
@ -259,39 +327,6 @@ var ContextMenuHandler = {
|
|||
sendAsyncMessage("Content:ContextMenu", state);
|
||||
},
|
||||
|
||||
_onSelectAll: function _onSelectAll() {
|
||||
if (this._isTextInput(this._target)) {
|
||||
// select all text in the input control
|
||||
this._target.select();
|
||||
} else {
|
||||
// select the entire document
|
||||
content.getSelection().selectAllChildren(content.document);
|
||||
}
|
||||
this.reset();
|
||||
},
|
||||
|
||||
_onPaste: function _onPaste() {
|
||||
// paste text if this is an input control
|
||||
if (this._isTextInput(this._target)) {
|
||||
let edit = this._target.QueryInterface(Ci.nsIDOMNSEditableElement);
|
||||
if (edit) {
|
||||
edit.editor.paste(Ci.nsIClipboard.kGlobalClipboard);
|
||||
} else {
|
||||
Util.dumpLn("error: target element does not support nsIDOMNSEditableElement");
|
||||
}
|
||||
}
|
||||
this.reset();
|
||||
},
|
||||
|
||||
_onCopyImage: function _onCopyImage() {
|
||||
Util.copyImageToClipboard(this._target);
|
||||
},
|
||||
|
||||
/*
|
||||
* Utility routines used in testing for various
|
||||
* HTML element types.
|
||||
*/
|
||||
|
||||
_isTextInput: function _isTextInput(element) {
|
||||
return ((element instanceof Ci.nsIDOMHTMLInputElement &&
|
||||
element.mozIsTextField(false)) ||
|
||||
|
@ -315,6 +350,35 @@ var ContextMenuHandler = {
|
|||
element instanceof Ci.nsIDOMHTMLBodyElement);
|
||||
},
|
||||
|
||||
_getLinkURL: function ch_getLinkURL(aLink) {
|
||||
let href = aLink.href;
|
||||
if (href)
|
||||
return href;
|
||||
|
||||
href = aLink.getAttributeNS(kXLinkNamespace, "href");
|
||||
if (!href || !href.match(/\S/)) {
|
||||
// Without this we try to save as the current doc,
|
||||
// for example, HTML case also throws if empty
|
||||
throw "Empty href";
|
||||
}
|
||||
|
||||
return Util.makeURLAbsolute(aLink.baseURI, href);
|
||||
},
|
||||
|
||||
_getURI: function ch_getURI(aURL) {
|
||||
try {
|
||||
return Util.makeURI(aURL);
|
||||
} catch (ex) { }
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
_getProtocol: function ch_getProtocol(aURI) {
|
||||
if (aURI)
|
||||
return aURI.scheme;
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* For add-ons to add new types and data to the ContextMenu message.
|
||||
*
|
||||
|
|
|
@ -26,6 +26,8 @@ BROWSER_TESTS = \
|
|||
browser_context_menu_tests.js \
|
||||
browser_context_menu_tests_01.html \
|
||||
browser_context_menu_tests_02.html \
|
||||
browser_context_menu_tests_03.html \
|
||||
text-block.html \
|
||||
$(NULL)
|
||||
|
||||
BROWSER_TEST_RESOURCES = \
|
||||
|
|
|
@ -26,7 +26,6 @@ function emptyClipboard() {
|
|||
.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
|
||||
}
|
||||
|
||||
// Image context menu tests
|
||||
gTests.push({
|
||||
desc: "text context menu",
|
||||
run: function test() {
|
||||
|
@ -397,6 +396,119 @@ gTests.push({
|
|||
}
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "tests for subframe positioning",
|
||||
run: function test() {
|
||||
info(chromeRoot + "browser_context_menu_tests_03.html");
|
||||
yield addTab(chromeRoot + "browser_context_menu_tests_03.html");
|
||||
|
||||
let win = Browser.selectedTab.browser.contentWindow;
|
||||
|
||||
// Sometimes the context ui is visible, sometimes it isn't.
|
||||
try {
|
||||
yield waitForCondition(function () {
|
||||
return ContextUI.isVisible;
|
||||
}, 500, 50);
|
||||
} catch (ex) {}
|
||||
|
||||
ContextUI.dismiss();
|
||||
|
||||
let frame1 = win.document.getElementById("frame1");
|
||||
let link1 = frame1.contentDocument.getElementById("link1");
|
||||
|
||||
let promise = waitForEvent(document, "popupshown");
|
||||
sendContextMenuClickToElement(frame1.contentDocument.defaultView, link1, 85, 10);
|
||||
yield promise;
|
||||
ok(promise && !(promise instanceof Error), "promise error");
|
||||
|
||||
// should be visible
|
||||
ok(ContextMenuUI._menuPopup._visible, "is visible");
|
||||
|
||||
ok(ContextMenuUI._panel.left > 375 && ContextMenuUI._panel.left < 390, "position");
|
||||
ok(ContextMenuUI._panel.top > 235 && ContextMenuUI._panel.top < 245, "position");
|
||||
|
||||
promise = waitForEvent(document, "popuphidden");
|
||||
ContextMenuUI.hide();
|
||||
yield promise;
|
||||
ok(promise && !(promise instanceof Error), "promise error");
|
||||
|
||||
frame1.contentDocument.defaultView.scrollBy(0, 200);
|
||||
|
||||
promise = waitForEvent(document, "popupshown");
|
||||
sendContextMenuClickToElement(frame1.contentDocument.defaultView, link1, 85, 10);
|
||||
yield promise;
|
||||
ok(promise && !(promise instanceof Error), "promise error");
|
||||
|
||||
// should be visible
|
||||
ok(ContextMenuUI._menuPopup._visible, "is visible");
|
||||
|
||||
ok(ContextMenuUI._panel.left > 375 && ContextMenuUI._panel.left < 390, "position");
|
||||
ok(ContextMenuUI._panel.top > 35 && ContextMenuUI._panel.top < 45, "position");
|
||||
|
||||
promise = waitForEvent(document, "popuphidden");
|
||||
ContextMenuUI.hide();
|
||||
yield promise;
|
||||
ok(promise && !(promise instanceof Error), "promise error");
|
||||
|
||||
let rlink1 = win.document.getElementById("rlink1");
|
||||
|
||||
promise = waitForEvent(document, "popupshown");
|
||||
sendContextMenuClickToElement(win, rlink1, 40, 10);
|
||||
yield promise;
|
||||
ok(promise && !(promise instanceof Error), "promise error");
|
||||
|
||||
// should be visible
|
||||
ok(ContextMenuUI._menuPopup._visible, "is visible");
|
||||
|
||||
ok(ContextMenuUI._panel.left > 730 && ContextMenuUI._panel.left < 745, "position");
|
||||
ok(ContextMenuUI._panel.top > 600 && ContextMenuUI._panel.top < 610, "position");
|
||||
|
||||
promise = waitForEvent(document, "popuphidden");
|
||||
ContextMenuUI.hide();
|
||||
yield promise;
|
||||
ok(promise && !(promise instanceof Error), "promise error");
|
||||
|
||||
win.scrollBy(0, 200);
|
||||
|
||||
promise = waitForEvent(document, "popupshown");
|
||||
sendContextMenuClickToElement(win, rlink1, 40, 10);
|
||||
yield promise;
|
||||
ok(promise && !(promise instanceof Error), "promise error");
|
||||
|
||||
// should be visible
|
||||
ok(ContextMenuUI._menuPopup._visible, "is visible");
|
||||
|
||||
ok(ContextMenuUI._panel.left > 730 && ContextMenuUI._panel.left < 745, "position");
|
||||
ok(ContextMenuUI._panel.top > 400 && ContextMenuUI._panel.top < 410, "position");
|
||||
|
||||
promise = waitForEvent(document, "popuphidden");
|
||||
ContextMenuUI.hide();
|
||||
yield promise;
|
||||
ok(promise && !(promise instanceof Error), "promise error");
|
||||
|
||||
let link2 = frame1.contentDocument.getElementById("link2");
|
||||
|
||||
promise = waitForEvent(document, "popupshown");
|
||||
sendContextMenuClickToElement(frame1.contentDocument.defaultView, link2, 85, 10);
|
||||
yield promise;
|
||||
ok(promise && !(promise instanceof Error), "promise error");
|
||||
|
||||
// should be visible
|
||||
ok(ContextMenuUI._menuPopup._visible, "is visible");
|
||||
|
||||
info(ContextMenuUI._panel.left);
|
||||
info(ContextMenuUI._panel.top);
|
||||
|
||||
ok(ContextMenuUI._panel.left > 380 && ContextMenuUI._panel.left < 390, "position");
|
||||
ok(ContextMenuUI._panel.top > 170 && ContextMenuUI._panel.top < 185, "position");
|
||||
|
||||
promise = waitForEvent(document, "popuphidden");
|
||||
ContextMenuUI.hide();
|
||||
yield promise;
|
||||
ok(promise && !(promise instanceof Error), "promise error");
|
||||
}
|
||||
});
|
||||
|
||||
function test() {
|
||||
runTests();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body bgcolor=white>
|
||||
|
||||
Hello there. <a href="#hello">hello there.</a>
|
||||
<center>
|
||||
|
||||
<iframe id="frame1" width="800" height="600" src="text-block.html"></iframe>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
Hello there. <a id="rlink1" href="#hello">hello there.</a>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
|
||||
</center>
|
||||
</body></html>
|
|
@ -0,0 +1,64 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<p>(start of paragraph)
|
||||
Alice was beginning to get very (break)<br>
|
||||
tired of sitting by her sister on the bank, and of having nothing to do: once or twice she (span)
|
||||
<span>(start of span) had peeped into the book her sister was reading (end of span)</span>,
|
||||
but it had no pictures or conversations in it, `and what is the use of a book,' thought Alice
|
||||
`without pictures or conversation?' (break)<br>
|
||||
(end of paragraph)</p>
|
||||
(in between paragraphs)
|
||||
<p>(start of paragraph)
|
||||
Alice was beginning to get very
|
||||
tired of sitting by her sister on the bank, and of having nothing to do: once or twice she
|
||||
had peeped into the book her sister was reading, but it had no pictures or conversations
|
||||
in it, `and what is the use of a book,' thought Alice `without pictures or conversation?'(break)<br>
|
||||
(end of paragraph)</p>
|
||||
|
||||
<p><a id="link1" href="#hello">(start of paragraph)</a>
|
||||
So she was considering in her own mind (as well as she could, for the hot day made her
|
||||
feel very sleepy and stupid), whether the pleasure of making a daisy-chain would be worth
|
||||
the trouble of getting up and picking the daisies, when suddenly a White Rabbit with pink
|
||||
eyes ran close by her. <br>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
There was nothing so VERY remarkable in that; nor did Alice think it so VERY much out of
|
||||
the way to hear the Rabbit say to itself, `Oh dear! Oh dear! I shall be late!' (when she
|
||||
thought it over afterwards, it occurred to her that she ought to have wondered at this,
|
||||
but at the time it all seemed quite natural); but when the Rabbit actually TOOK A WATCH
|
||||
OUT OF ITS WAISTCOAT- POCKET, and looked at it, and then hurried on, Alice started to her
|
||||
feet, for it flashed across her mind that she had never before seen a rabbit with either a
|
||||
waistcoat-pocket, or a watch to take out of it, and burning with curiosity, she ran across
|
||||
the field after it, and fortunately was just in time to see it pop down a large
|
||||
rabbit-hole under the hedge. <br>
|
||||
<br>
|
||||
In another moment down went Alice after it, never once considering how in the world she
|
||||
was to get out again. <br>
|
||||
<br>
|
||||
The rabbit-hole went straight on like a tunnel for some way, and then dipped suddenly
|
||||
down, so suddenly that Alice had not a moment to think about stopping herself before she
|
||||
found herself falling down a very deep well. <br>
|
||||
<br><a id="link2" href="#hello">(start of paragraph)</a>
|
||||
Either the well was very deep, or she fell very slowly, for she had plenty of time as she
|
||||
went down to look about her and to wonder what was going to happen next. First, she tried
|
||||
to look down and make out what she was coming to, but it was too dark to see anything;
|
||||
then she looked at the sides of the well, and noticed that they were filled with cupboards
|
||||
and book-shelves; here and there she saw maps and pictures hung upon pegs. She took down a
|
||||
jar from one of the shelves as she passed; it was labelled `ORANGE MARMALADE', but to her
|
||||
great disappointment it was empty: she did not like to drop the jar for fear of killing
|
||||
somebody, so managed to put it into one of the cupboards as she fell past it. <br>
|
||||
<br>
|
||||
`Well!' thought Alice to herself, `after such a fall as this, I shall think nothing of
|
||||
tumbling down stairs! How brave they'll all think me at home! Why, I wouldn't say anything
|
||||
about it, even if I fell off the top of the house!' (Which was very likely true.) <br>
|
||||
<br>
|
||||
Down, down, down.
|
||||
<br>
|
||||
</div>
|
||||
</body></html>
|
|
@ -57,13 +57,12 @@ this.Social = {
|
|||
return this._provider;
|
||||
},
|
||||
set provider(val) {
|
||||
// Changes triggered by the public setter should notify of an engine change.
|
||||
this._setProvider(val, true);
|
||||
this._setProvider(val);
|
||||
},
|
||||
|
||||
// Sets the current provider and enables it. Also disables the
|
||||
// previously set provider, and optionally notifies observers of the change.
|
||||
_setProvider: function (provider, notify) {
|
||||
// previously set provider, and notifies observers of the change.
|
||||
_setProvider: function (provider) {
|
||||
if (this._provider == provider)
|
||||
return;
|
||||
|
||||
|
@ -84,10 +83,8 @@ this.Social = {
|
|||
Services.prefs.setBoolPref("social.enabled", enabled);
|
||||
}
|
||||
|
||||
if (notify) {
|
||||
let origin = this._provider && this._provider.origin;
|
||||
Services.obs.notifyObservers(null, "social:provider-set", origin);
|
||||
}
|
||||
let origin = this._provider && this._provider.origin;
|
||||
Services.obs.notifyObservers(null, "social:provider-set", origin);
|
||||
},
|
||||
|
||||
get defaultProvider() {
|
||||
|
@ -97,41 +94,37 @@ this.Social = {
|
|||
return provider || this.providers[0];
|
||||
},
|
||||
|
||||
init: function Social_init(callback) {
|
||||
init: function Social_init() {
|
||||
this._disabledForSafeMode = Services.appinfo.inSafeMode && this.enabled;
|
||||
|
||||
if (this.providers) {
|
||||
schedule(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the current set of providers, and set the current provider.
|
||||
SocialService.getProviderList(function (providers) {
|
||||
// We don't want to notify about a provider change when we're setting
|
||||
// this.provider for the first time, so pass false here.
|
||||
this._updateProviderCache(providers, false);
|
||||
callback();
|
||||
this._updateProviderCache(providers);
|
||||
}.bind(this));
|
||||
|
||||
// Register an observer for changes to the provider list
|
||||
SocialService.registerProviderListener(function providerListener(topic, data) {
|
||||
// An engine change caused by adding/removing a provider should notify
|
||||
if (topic == "provider-added" || topic == "provider-removed") {
|
||||
this._updateProviderCache(data, true);
|
||||
this._updateProviderCache(data);
|
||||
Services.obs.notifyObservers(null, "social:providers-changed", null);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
// Called to update our cache of providers and set the current provider
|
||||
_updateProviderCache: function (providers, notifyProviderChange) {
|
||||
_updateProviderCache: function (providers) {
|
||||
this.providers = providers;
|
||||
|
||||
// If social is currently disabled there's nothing else to do.
|
||||
if (!SocialService.enabled)
|
||||
return;
|
||||
// Otherwise set the provider.
|
||||
this._setProvider(this.defaultProvider, notifyProviderChange);
|
||||
this._setProvider(this.defaultProvider);
|
||||
},
|
||||
|
||||
set enabled(val) {
|
||||
|
|
|
@ -825,6 +825,14 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t
|
|||
env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1'
|
||||
env['NS_TRACE_MALLOC_DISABLE_STACKS'] = '1'
|
||||
|
||||
# Additional temporary logging while we try to debug some intermittent
|
||||
# WebRTC conditions. This is necessary to troubleshoot bugs 841496,
|
||||
# 841150, and 839677 (at least)
|
||||
env['NSPR_LOG_MODULES'] = 'signaling:3,mtransport:3'
|
||||
env['R_LOG_LEVEL'] = '5'
|
||||
env['R_LOG_DESTINATION'] = 'stderr'
|
||||
env['R_LOG_VERBOSE'] = '1'
|
||||
|
||||
# ASan specific environment stuff
|
||||
if self.IS_ASAN and (self.IS_LINUX or self.IS_MAC):
|
||||
try:
|
||||
|
|
|
@ -37,7 +37,7 @@ interface nsIFrame;
|
|||
* Web IDL interfaces to mirror this interface when changing it.
|
||||
*/
|
||||
|
||||
[scriptable, builtinclass, uuid(497bfb9b-d996-4d1e-a647-8137b0cfc876)]
|
||||
[scriptable, builtinclass, uuid(e3968acd-b796-4ca3-aec0-e7f0880f2219)]
|
||||
interface nsIImageLoadingContent : imgINotificationObserver
|
||||
{
|
||||
/**
|
||||
|
@ -159,4 +159,12 @@ interface nsIImageLoadingContent : imgINotificationObserver
|
|||
* as PR_FALSE to revert ImageState() to its original behaviour.
|
||||
*/
|
||||
void forceImageState(in boolean aForce, in unsigned long long aState);
|
||||
|
||||
/**
|
||||
* A visible count is stored, if it is non-zero then this image is considered
|
||||
* visible. These methods increment, decrement, or return the visible coount.
|
||||
*/
|
||||
[noscript, notxpcom] void IncrementVisibleCount();
|
||||
[noscript, notxpcom] void DecrementVisibleCount();
|
||||
[noscript, notxpcom] uint32_t GetVisibleCount();
|
||||
};
|
||||
|
|
|
@ -86,7 +86,8 @@ nsImageLoadingContent::nsImageLoadingContent()
|
|||
mNewRequestsWillNeedAnimationReset(false),
|
||||
mStateChangerDepth(0),
|
||||
mCurrentRequestRegistered(false),
|
||||
mPendingRequestRegistered(false)
|
||||
mPendingRequestRegistered(false),
|
||||
mVisibleCount(0)
|
||||
{
|
||||
if (!nsContentUtils::GetImgLoaderForChannel(nullptr)) {
|
||||
mLoadingEnabled = false;
|
||||
|
@ -121,6 +122,11 @@ nsImageLoadingContent::Notify(imgIRequest* aRequest,
|
|||
return OnImageIsAnimated(aRequest);
|
||||
}
|
||||
|
||||
if (aType == imgINotificationObserver::UNLOCKED_DRAW) {
|
||||
OnUnlockedDraw();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aType == imgINotificationObserver::LOAD_COMPLETE) {
|
||||
// We should definitely have a request here
|
||||
NS_ABORT_IF_FALSE(aRequest, "no request?");
|
||||
|
@ -228,6 +234,25 @@ nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsImageLoadingContent::OnUnlockedDraw()
|
||||
{
|
||||
if (mVisibleCount > 0) {
|
||||
// We should already be marked as visible, there is nothing more we can do.
|
||||
return;
|
||||
}
|
||||
|
||||
nsPresContext* presContext = GetFramePresContext();
|
||||
if (!presContext)
|
||||
return;
|
||||
|
||||
nsIPresShell* presShell = presContext->PresShell();
|
||||
if (!presShell)
|
||||
return;
|
||||
|
||||
presShell->EnsureImageInVisibleList(this);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsImageLoadingContent::OnImageIsAnimated(imgIRequest *aRequest)
|
||||
{
|
||||
|
@ -375,25 +400,23 @@ nsImageLoadingContent::FrameCreated(nsIFrame* aFrame)
|
|||
{
|
||||
NS_ASSERTION(aFrame, "aFrame is null");
|
||||
|
||||
if (aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
|
||||
// Assume all images in popups are visible.
|
||||
IncrementVisibleCount();
|
||||
}
|
||||
|
||||
nsPresContext* presContext = aFrame->PresContext();
|
||||
if (mVisibleCount == 0) {
|
||||
presContext->PresShell()->EnsureImageInVisibleList(this);
|
||||
}
|
||||
|
||||
// We pass the SKIP_FRAME_CHECK flag to TrackImage here because our primary
|
||||
// frame pointer hasn't been setup yet when this is caled.
|
||||
TrackImage(mCurrentRequest, SKIP_FRAME_CHECK);
|
||||
TrackImage(mPendingRequest, SKIP_FRAME_CHECK);
|
||||
|
||||
// We need to make sure that our image request is registered, if it should
|
||||
// be registered.
|
||||
nsPresContext* presContext = aFrame->PresContext();
|
||||
|
||||
if (mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
nsIDocument* doc = GetOurCurrentDoc();
|
||||
if (doc) {
|
||||
mCurrentRequestFlags |= REQUEST_IS_TRACKED;
|
||||
doc->AddImage(mCurrentRequest);
|
||||
}
|
||||
}
|
||||
if (mPendingRequest && !(mPendingRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
nsIDocument* doc = GetOurCurrentDoc();
|
||||
if (doc) {
|
||||
mPendingRequestFlags |= REQUEST_IS_TRACKED;
|
||||
doc->AddImage(mPendingRequest);
|
||||
}
|
||||
}
|
||||
|
||||
if (mCurrentRequest) {
|
||||
nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mCurrentRequest,
|
||||
&mCurrentRequestRegistered);
|
||||
|
@ -423,19 +446,13 @@ nsImageLoadingContent::FrameDestroyed(nsIFrame* aFrame)
|
|||
&mPendingRequestRegistered);
|
||||
}
|
||||
|
||||
if (mCurrentRequest && (mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
nsIDocument* doc = GetOurCurrentDoc();
|
||||
if (doc) {
|
||||
mCurrentRequestFlags &= ~REQUEST_IS_TRACKED;
|
||||
doc->RemoveImage(mCurrentRequest);
|
||||
}
|
||||
}
|
||||
if (mPendingRequest && (mPendingRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
nsIDocument* doc = GetOurCurrentDoc();
|
||||
if (doc) {
|
||||
mPendingRequestFlags &= ~REQUEST_IS_TRACKED;
|
||||
doc->RemoveImage(mPendingRequest);
|
||||
}
|
||||
UntrackImage(mCurrentRequest);
|
||||
UntrackImage(mPendingRequest);
|
||||
|
||||
if (aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
|
||||
// We assume all images in popups are visible, so this decrement balances
|
||||
// out the increment in FrameCreated above.
|
||||
DecrementVisibleCount();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -614,6 +631,34 @@ nsImageLoadingContent::UnblockOnload(imgIRequest* aRequest)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsImageLoadingContent::IncrementVisibleCount()
|
||||
{
|
||||
mVisibleCount++;
|
||||
if (mVisibleCount == 1) {
|
||||
TrackImage(mCurrentRequest);
|
||||
TrackImage(mPendingRequest);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsImageLoadingContent::DecrementVisibleCount()
|
||||
{
|
||||
NS_ASSERTION(mVisibleCount > 0, "visible count should be positive here");
|
||||
mVisibleCount--;
|
||||
|
||||
if (mVisibleCount == 0) {
|
||||
UntrackImage(mCurrentRequest);
|
||||
UntrackImage(mPendingRequest);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsImageLoadingContent::GetVisibleCount()
|
||||
{
|
||||
return mVisibleCount;
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-interface methods
|
||||
*/
|
||||
|
@ -1101,7 +1146,7 @@ nsImageLoadingContent::ClearCurrentRequest(nsresult aReason)
|
|||
&mCurrentRequestRegistered);
|
||||
|
||||
// Clean up the request.
|
||||
UntrackImage(mCurrentRequest);
|
||||
UntrackImage(mCurrentRequest, REQUEST_DISCARD);
|
||||
mCurrentRequest->CancelAndForgetObserver(aReason);
|
||||
mCurrentRequest = nullptr;
|
||||
mCurrentRequestFlags = 0;
|
||||
|
@ -1124,7 +1169,7 @@ nsImageLoadingContent::ClearPendingRequest(nsresult aReason)
|
|||
nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mPendingRequest,
|
||||
&mPendingRequestRegistered);
|
||||
|
||||
UntrackImage(mPendingRequest);
|
||||
UntrackImage(mPendingRequest, REQUEST_DISCARD);
|
||||
mPendingRequest->CancelAndForgetObserver(aReason);
|
||||
mPendingRequest = nullptr;
|
||||
mPendingRequestFlags = 0;
|
||||
|
@ -1183,16 +1228,8 @@ nsImageLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
nsCxPusher pusher;
|
||||
pusher.PushNull();
|
||||
|
||||
if (GetOurPrimaryFrame()) {
|
||||
if (mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
mCurrentRequestFlags |= REQUEST_IS_TRACKED;
|
||||
aDocument->AddImage(mCurrentRequest);
|
||||
}
|
||||
if (mPendingRequest && !(mPendingRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
mPendingRequestFlags |= REQUEST_IS_TRACKED;
|
||||
aDocument->AddImage(mPendingRequest);
|
||||
}
|
||||
}
|
||||
TrackImage(mCurrentRequest);
|
||||
TrackImage(mPendingRequest);
|
||||
|
||||
if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
|
||||
aDocument->BlockOnload();
|
||||
|
@ -1211,21 +1248,15 @@ nsImageLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
nsCxPusher pusher;
|
||||
pusher.PushNull();
|
||||
|
||||
if (mCurrentRequest && (mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
mCurrentRequestFlags &= ~REQUEST_IS_TRACKED;
|
||||
doc->RemoveImage(mCurrentRequest);
|
||||
}
|
||||
if (mPendingRequest && (mPendingRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
mPendingRequestFlags &= ~REQUEST_IS_TRACKED;
|
||||
doc->RemoveImage(mPendingRequest);
|
||||
}
|
||||
UntrackImage(mCurrentRequest);
|
||||
UntrackImage(mPendingRequest);
|
||||
|
||||
if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
|
||||
doc->UnblockOnload(false);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsImageLoadingContent::TrackImage(imgIRequest* aImage)
|
||||
nsImageLoadingContent::TrackImage(imgIRequest* aImage, uint32_t aFlags /* = 0 */)
|
||||
{
|
||||
if (!aImage)
|
||||
return NS_OK;
|
||||
|
@ -1234,7 +1265,8 @@ nsImageLoadingContent::TrackImage(imgIRequest* aImage)
|
|||
"Why haven't we heard of this request?");
|
||||
|
||||
nsIDocument* doc = GetOurCurrentDoc();
|
||||
if (doc && GetOurPrimaryFrame()) {
|
||||
if (doc && ((aFlags & SKIP_FRAME_CHECK) || GetOurPrimaryFrame()) &&
|
||||
(mVisibleCount > 0)) {
|
||||
if (aImage == mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
mCurrentRequestFlags |= REQUEST_IS_TRACKED;
|
||||
doc->AddImage(mCurrentRequest);
|
||||
|
@ -1248,7 +1280,7 @@ nsImageLoadingContent::TrackImage(imgIRequest* aImage)
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsImageLoadingContent::UntrackImage(imgIRequest* aImage)
|
||||
nsImageLoadingContent::UntrackImage(imgIRequest* aImage, uint32_t aFlags /* = 0 */)
|
||||
{
|
||||
if (!aImage)
|
||||
return NS_OK;
|
||||
|
@ -1263,11 +1295,13 @@ nsImageLoadingContent::UntrackImage(imgIRequest* aImage)
|
|||
if (doc) {
|
||||
if (aImage == mCurrentRequest && (mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
mCurrentRequestFlags &= ~REQUEST_IS_TRACKED;
|
||||
doc->RemoveImage(mCurrentRequest, nsIDocument::REQUEST_DISCARD);
|
||||
doc->RemoveImage(mCurrentRequest,
|
||||
(aFlags & REQUEST_DISCARD) ? nsIDocument::REQUEST_DISCARD : 0);
|
||||
}
|
||||
if (aImage == mPendingRequest && (mPendingRequestFlags & REQUEST_IS_TRACKED)) {
|
||||
mPendingRequestFlags &= ~REQUEST_IS_TRACKED;
|
||||
doc->RemoveImage(mPendingRequest, nsIDocument::REQUEST_DISCARD);
|
||||
doc->RemoveImage(mPendingRequest,
|
||||
(aFlags & REQUEST_DISCARD) ? nsIDocument::REQUEST_DISCARD : 0);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
|
|
|
@ -183,6 +183,7 @@ protected:
|
|||
void UnbindFromTree(bool aDeep, bool aNullParent);
|
||||
|
||||
nsresult OnStopRequest(imgIRequest* aRequest, nsresult aStatus);
|
||||
void OnUnlockedDraw();
|
||||
nsresult OnImageIsAnimated(imgIRequest *aRequest);
|
||||
|
||||
private:
|
||||
|
@ -326,9 +327,21 @@ protected:
|
|||
* Adds/Removes a given imgIRequest from our document's tracker.
|
||||
*
|
||||
* No-op if aImage is null.
|
||||
*
|
||||
* SKIP_FRAME_CHECK passed to TrackImage means we skip the check if we have a
|
||||
* frame, there is only one valid use of this: when calling from FrameCreated.
|
||||
*
|
||||
* REQUEST_DISCARD passed to UntrackImage means we request the discard of the
|
||||
* decoded data of the image.
|
||||
*/
|
||||
nsresult TrackImage(imgIRequest* aImage);
|
||||
nsresult UntrackImage(imgIRequest* aImage);
|
||||
enum {
|
||||
SKIP_FRAME_CHECK = 0x1
|
||||
};
|
||||
nsresult TrackImage(imgIRequest* aImage, uint32_t aFlags = 0);
|
||||
enum {
|
||||
REQUEST_DISCARD = 0x1
|
||||
};
|
||||
nsresult UntrackImage(imgIRequest* aImage, uint32_t aFlags = 0);
|
||||
|
||||
/* MEMBERS */
|
||||
nsRefPtr<imgRequestProxy> mCurrentRequest;
|
||||
|
@ -404,6 +417,8 @@ private:
|
|||
// registered with the refresh driver.
|
||||
bool mCurrentRequestRegistered;
|
||||
bool mPendingRequestRegistered;
|
||||
|
||||
uint32_t mVisibleCount;
|
||||
};
|
||||
|
||||
#endif // nsImageLoadingContent_h__
|
||||
|
|
|
@ -119,6 +119,31 @@ BluetoothOppManagerObserver::Observe(nsISupports* aSubject,
|
|||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
class SendSocketDataTask : public nsRunnable
|
||||
{
|
||||
public:
|
||||
SendSocketDataTask(uint8_t* aStream, uint32_t aSize)
|
||||
: mStream(aStream)
|
||||
, mSize(aSize)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
sInstance->SendPutRequest(mStream, mSize);
|
||||
sSentFileLength += mSize;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsAutoArrayPtr<uint8_t> mStream;
|
||||
uint32_t mSize;
|
||||
};
|
||||
|
||||
class ReadFileTask : public nsRunnable
|
||||
{
|
||||
public:
|
||||
|
@ -135,13 +160,13 @@ public:
|
|||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
uint32_t numRead;
|
||||
nsAutoArrayPtr<char> buf;
|
||||
buf = new char[mAvailablePacketSize];
|
||||
char* buf = new char[mAvailablePacketSize];
|
||||
|
||||
// function inputstream->Read() only works on non-main thread
|
||||
nsresult rv = mInputStream->Read(buf.get(), mAvailablePacketSize, &numRead);
|
||||
nsresult rv = mInputStream->Read(buf, mAvailablePacketSize, &numRead);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Needs error handling here
|
||||
NS_WARNING("Failed to read from input stream");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -149,8 +174,13 @@ public:
|
|||
if (sSentFileLength + numRead >= sFileLength) {
|
||||
sWaitingToSendPutFinal = true;
|
||||
}
|
||||
sInstance->SendPutRequest((uint8_t*)buf.get(), numRead);
|
||||
sSentFileLength += numRead;
|
||||
|
||||
nsRefPtr<SendSocketDataTask> task =
|
||||
new SendSocketDataTask((uint8_t*)buf, numRead);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(task))) {
|
||||
NS_WARNING("Failed to dispatch to main thread!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -361,6 +391,7 @@ BluetoothOppManager::SendFile(BlobParent* aActor)
|
|||
|
||||
SendConnectRequest();
|
||||
mTransferMode = false;
|
||||
StartFileTransfer();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -797,24 +828,26 @@ BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage)
|
|||
packetLength = (((int)aMessage->mData[1]) << 8) | aMessage->mData[2];
|
||||
}
|
||||
|
||||
// Check response code
|
||||
if (mLastCommand == ObexRequestCode::Put &&
|
||||
opCode != ObexResponseCode::Continue) {
|
||||
NS_WARNING("[OPP] Put(0x02) failed");
|
||||
SendDisconnectRequest();
|
||||
return;
|
||||
} else if (mLastCommand == ObexRequestCode::Abort ||
|
||||
mLastCommand == ObexRequestCode::Connect ||
|
||||
mLastCommand == ObexRequestCode::Disconnect ||
|
||||
mLastCommand == ObexRequestCode::PutFinal){
|
||||
if (opCode != ObexResponseCode::Success) {
|
||||
nsAutoCString str;
|
||||
str += "[OPP] 0x";
|
||||
str += mLastCommand;
|
||||
str += " failed";
|
||||
NS_WARNING(str.get());
|
||||
return;
|
||||
// Check response code and send out system message as finished if the reponse
|
||||
// code is somehow incorrect.
|
||||
uint8_t expectedOpCode = ObexResponseCode::Success;
|
||||
if (mLastCommand == ObexRequestCode::Put) {
|
||||
expectedOpCode = ObexResponseCode::Continue;
|
||||
}
|
||||
|
||||
if (opCode != expectedOpCode) {
|
||||
if (mLastCommand == ObexRequestCode::Put ||
|
||||
mLastCommand == ObexRequestCode::Abort ||
|
||||
mLastCommand == ObexRequestCode::PutFinal) {
|
||||
SendDisconnectRequest();
|
||||
}
|
||||
nsAutoCString str;
|
||||
str += "[OPP] 0x";
|
||||
str.AppendInt(mLastCommand, 16);
|
||||
str += " failed";
|
||||
NS_WARNING(str.get());
|
||||
FileTransferComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mLastCommand == ObexRequestCode::PutFinal) {
|
||||
|
@ -849,7 +882,6 @@ BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage)
|
|||
*/
|
||||
if (ExtractBlobHeaders()) {
|
||||
sInstance->SendPutHeaderRequest(sFileName, sFileLength);
|
||||
StartFileTransfer();
|
||||
}
|
||||
} else if (mLastCommand == ObexRequestCode::Put) {
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ BluetoothScoManager::Init()
|
|||
mSocketStatus = GetConnectionStatus();
|
||||
|
||||
sScoObserver = new BluetoothScoManagerObserver();
|
||||
if (sScoObserver->Init()) {
|
||||
if (!sScoObserver->Init()) {
|
||||
NS_WARNING("Cannot set up SCO observers!");
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -2466,11 +2466,9 @@ BluetoothDBusService::Connect(const nsAString& aDeviceAddress,
|
|||
errorStr.AssignLiteral("BluetoothOppManager has connected/is connecting!");
|
||||
DispatchBluetoothReply(aRunnable, v, errorStr);
|
||||
}
|
||||
} else {
|
||||
NS_WARNING("Unknown Profile");
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
NS_WARNING("Unknown Profile");
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -21,7 +21,7 @@ function handlePrompt(e) {
|
|||
|
||||
alert('parent:ready');
|
||||
}
|
||||
else if (numPrompts > 3) {
|
||||
else if (numPrompts == 4 || numPrompts == 5) {
|
||||
alert(e.detail.message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -390,6 +390,14 @@ ContactManager.prototype = {
|
|||
return contacts;
|
||||
},
|
||||
|
||||
_fireSuccessOrDone: function(aCursor, aResult) {
|
||||
if (aResult == null) {
|
||||
Services.DOMRequest.fireDone(aCursor);
|
||||
} else {
|
||||
Services.DOMRequest.fireSuccess(aCursor, aResult);
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
if (DEBUG) debug("receiveMessage: " + aMessage.name);
|
||||
let msg = aMessage.json;
|
||||
|
@ -407,12 +415,15 @@ ContactManager.prototype = {
|
|||
}
|
||||
break;
|
||||
case "Contacts:GetAll:Next":
|
||||
let cursor = this._cursorData[msg.cursorId];
|
||||
let data = this._cursorData[msg.cursorId];
|
||||
let contact = msg.contact ? this._convertContact(msg.contact) : null;
|
||||
if (contact == null) {
|
||||
Services.DOMRequest.fireDone(cursor);
|
||||
if (data.waitingForNext) {
|
||||
if (DEBUG) debug("cursor waiting for contact, sending");
|
||||
data.waitingForNext = false;
|
||||
this._fireSuccessOrDone(data.cursor, contact);
|
||||
} else {
|
||||
Services.DOMRequest.fireSuccess(cursor, contact);
|
||||
if (DEBUG) debug("cursor not waiting, saving");
|
||||
data.cachedContacts.push(contact);
|
||||
}
|
||||
break;
|
||||
case "Contacts:GetSimContacts:Return:OK":
|
||||
|
@ -591,12 +602,16 @@ ContactManager.prototype = {
|
|||
|
||||
createCursor: function CM_createCursor(aRequest) {
|
||||
let id = this._getRandomId();
|
||||
let cursor = Services.DOMRequest.createCursor(this._window, function() {
|
||||
this.handleContinue(id);
|
||||
}.bind(this));
|
||||
let data = {
|
||||
cursor: Services.DOMRequest.createCursor(this._window, function() {
|
||||
this.handleContinue(id);
|
||||
}.bind(this)),
|
||||
cachedContacts: [],
|
||||
waitingForNext: true,
|
||||
};
|
||||
if (DEBUG) debug("saved cursor id: " + id);
|
||||
this._cursorData[id] = cursor;
|
||||
return [id, cursor];
|
||||
this._cursorData[id] = data;
|
||||
return [id, data.cursor];
|
||||
},
|
||||
|
||||
getAll: function CM_getAll(aOptions) {
|
||||
|
@ -612,9 +627,14 @@ ContactManager.prototype = {
|
|||
|
||||
handleContinue: function CM_handleContinue(aCursorId) {
|
||||
if (DEBUG) debug("handleContinue: " + aCursorId);
|
||||
cpmm.sendAsyncMessage("Contacts:GetAll:Continue", {
|
||||
cursorId: aCursorId
|
||||
});
|
||||
let data = this._cursorData[aCursorId];
|
||||
if (data.cachedContacts.length > 0) {
|
||||
if (DEBUG) debug("contact in cache");
|
||||
this._fireSuccessOrDone(data.cursor, data.cachedContacts.shift());
|
||||
} else {
|
||||
if (DEBUG) debug("waiting for contact");
|
||||
data.waitingForNext = true;
|
||||
}
|
||||
},
|
||||
|
||||
remove: function removeContact(aRecord) {
|
||||
|
|
|
@ -30,8 +30,6 @@ this.ContactDB = function ContactDB(aGlobal) {
|
|||
ContactDB.prototype = {
|
||||
__proto__: IndexedDBHelper.prototype,
|
||||
|
||||
cursorData: {},
|
||||
|
||||
upgradeSchema: function upgradeSchema(aTransaction, aDb, aOldVersion, aNewVersion) {
|
||||
if (DEBUG) debug("upgrade schema from: " + aOldVersion + " to " + aNewVersion + " called!");
|
||||
let db = aDb;
|
||||
|
@ -472,82 +470,81 @@ ContactDB.prototype = {
|
|||
}, aSuccessCb, aErrorCb);
|
||||
},
|
||||
|
||||
getObjectById: function CDB_getObjectById(aStore, aObjectId, aCallback) {
|
||||
if (DEBUG) debug("getObjectById: " + aStore + ":" + aObjectId);
|
||||
this.newTxn("readonly", aStore, function (txn, store) {
|
||||
let req = store.get(aObjectId);
|
||||
req.onsuccess = function (event) {
|
||||
aCallback(event.target.result);
|
||||
};
|
||||
req.onerror = function (event) {
|
||||
aCallback(null);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
getCacheForQuery: function CDB_getCacheForQuery(aQuery, aCursorId, aSuccessCb) {
|
||||
if (DEBUG) debug("getCacheForQuery");
|
||||
// Here we try to get the cached results for query `aQuery'. If they don't
|
||||
// exist, it means the cache was invalidated and needs to be recreated, so
|
||||
// we do that. Otherwise, we just return the existing cache.
|
||||
this.getObjectById(SAVED_GETALL_STORE_NAME, aQuery, function (aCache) {
|
||||
if (!aCache) {
|
||||
if (DEBUG) debug("creating cache for query " + aQuery);
|
||||
this.createCacheForQuery(aQuery, aCursorId, aSuccessCb);
|
||||
} else {
|
||||
if (DEBUG) debug("cache exists");
|
||||
if (!this.cursorData[aCursorId]) {
|
||||
this.cursorData[aCursorId] = aCache;
|
||||
}
|
||||
aSuccessCb(aCache);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
setCacheForQuery: function CDB_setCacheForQuery(aQuery, aCache, aCallback) {
|
||||
this.newTxn("readwrite", SAVED_GETALL_STORE_NAME, function (txn, store) {
|
||||
let req = store.put(aCache, aQuery);
|
||||
if (!aCallback) {
|
||||
return;
|
||||
}
|
||||
req.onsuccess = function () {
|
||||
aCallback(true);
|
||||
};
|
||||
req.onerror = function () {
|
||||
aCallback(false);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
createCacheForQuery: function CDB_createCacheForQuery(aQuery, aCursorId, aSuccessCb, aFailureCb) {
|
||||
createCacheForQuery: function CDB_createCacheForQuery(aQuery, aSuccessCb, aFailureCb) {
|
||||
this.find(function (aContacts) {
|
||||
if (aContacts) {
|
||||
let contactsArray = [];
|
||||
for (let i in aContacts) {
|
||||
contactsArray.push(aContacts[i].id);
|
||||
contactsArray.push(aContacts[i]);
|
||||
}
|
||||
|
||||
this.setCacheForQuery(aQuery, contactsArray);
|
||||
this.cursorData[aCursorId] = contactsArray;
|
||||
aSuccessCb(contactsArray);
|
||||
// save contact ids in cache
|
||||
this.newTxn("readwrite", SAVED_GETALL_STORE_NAME, function(txn, store) {
|
||||
store.put(contactsArray.map(function(el) el.id), aQuery);
|
||||
});
|
||||
|
||||
// send full contacts
|
||||
aSuccessCb(contactsArray, true);
|
||||
} else {
|
||||
aSuccessCb(null);
|
||||
aSuccessCb([], true);
|
||||
}
|
||||
}.bind(this),
|
||||
function (aErrorMsg) { aFailureCb(aErrorMsg); },
|
||||
JSON.parse(aQuery));
|
||||
},
|
||||
|
||||
getAll: function CDB_getAll(aSuccessCb, aFailureCb, aOptions, aCursorId) {
|
||||
// Recreate the cache for this query if needed
|
||||
getCacheForQuery: function CDB_getCacheForQuery(aQuery, aSuccessCb) {
|
||||
if (DEBUG) debug("getCacheForQuery");
|
||||
// Here we try to get the cached results for query `aQuery'. If they don't
|
||||
// exist, it means the cache was invalidated and needs to be recreated, so
|
||||
// we do that. Otherwise, we just return the existing cache.
|
||||
this.newTxn("readonly", SAVED_GETALL_STORE_NAME, function(txn, store) {
|
||||
let req = store.get(aQuery);
|
||||
req.onsuccess = function(e) {
|
||||
if (e.target.result) {
|
||||
if (DEBUG) debug("cache exists");
|
||||
debug("sending: " + JSON.stringify(e.target.result));
|
||||
aSuccessCb(e.target.result, false);
|
||||
} else {
|
||||
if (DEBUG) debug("creating cache for query " + aQuery);
|
||||
this.createCacheForQuery(aQuery, aSuccessCb);
|
||||
}
|
||||
}.bind(this);
|
||||
req.onerror = function() {
|
||||
|
||||
};
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
getAll: function CDB_getAll(aSuccessCb, aFailureCb, aOptions) {
|
||||
let optionStr = JSON.stringify(aOptions);
|
||||
this.getCacheForQuery(optionStr, aCursorId, function (aCachedResults) {
|
||||
this.getCacheForQuery(optionStr, function(aCachedResults, aFullContacts) {
|
||||
// aFullContacts is true if the cache didn't exist and had to be created.
|
||||
// In that case, we receive the full contacts since we already have them
|
||||
// in memory to create the cache anyway. This allows us to avoid accessing
|
||||
// the main object store again.
|
||||
if (aCachedResults && aCachedResults.length > 0) {
|
||||
if (DEBUG) debug("query returned at least one contact");
|
||||
this.getObjectById(STORE_NAME, aCachedResults[0], function (aContact) {
|
||||
this.cursorData[aCursorId].shift();
|
||||
aSuccessCb(aContact);
|
||||
}.bind(this));
|
||||
if (aFullContacts) {
|
||||
if (DEBUG) debug("full contacts: " + aCachedResults.length);
|
||||
for (let i = 0; i < aCachedResults.length; ++i) {
|
||||
aSuccessCb(aCachedResults[i]);
|
||||
}
|
||||
aSuccessCb(null);
|
||||
} else {
|
||||
let count = 0;
|
||||
this.newTxn("readonly", STORE_NAME, function(txn, store) {
|
||||
for (let i = 0; i < aCachedResults.length; ++i) {
|
||||
store.get(aCachedResults[i]).onsuccess = function(e) {
|
||||
aSuccessCb(e.target.result);
|
||||
count++;
|
||||
if (count == aCachedResults.length) {
|
||||
aSuccessCb(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
} else { // no contacts
|
||||
if (DEBUG) debug("query returned no contacts");
|
||||
aSuccessCb(null);
|
||||
|
@ -555,34 +552,6 @@ ContactDB.prototype = {
|
|||
}.bind(this));
|
||||
},
|
||||
|
||||
getNext: function CDB_getNext(aSuccessCb, aFailureCb, aCursorId) {
|
||||
if (DEBUG) debug("ContactDB:getNext: " + aCursorId);
|
||||
let aCachedResults = this.cursorData[aCursorId];
|
||||
if (DEBUG) debug("got transient cache");
|
||||
if (aCachedResults.length > 0) {
|
||||
this.getObjectById(STORE_NAME, aCachedResults[0], function(aContact) {
|
||||
this.cursorData[aCursorId].shift();
|
||||
if (aContact) {
|
||||
aSuccessCb(aContact);
|
||||
} else {
|
||||
// If the contact ID in cache is invalid, it was removed recently and
|
||||
// the cache hasn't been updated to reflect the change, so we skip it.
|
||||
if (DEBUG) debug("invalid contact in cache: " + aCachedResults[0]);
|
||||
return this.getNext(aSuccessCb, aFailureCb, aCursorId);
|
||||
}
|
||||
}.bind(this));
|
||||
} else { // last contact
|
||||
delete this.cursorData[aCursorId];
|
||||
aSuccessCb(null);
|
||||
}
|
||||
},
|
||||
|
||||
releaseCursors: function CDB_releaseCursors(aCursors) {
|
||||
for (let i of aCursors) {
|
||||
delete this.cursorData[i];
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Sorting the contacts by sortBy field. aSortBy can either be familyName or givenName.
|
||||
* If 2 entries have the same sortyBy field or no sortBy field is present, we continue
|
||||
|
|
|
@ -37,12 +37,9 @@ XPCOMUtils.defineLazyGetter(this, "mRIL", function () {
|
|||
let myGlobal = this;
|
||||
|
||||
this.DOMContactManager = {
|
||||
// maps children to their live cursors so we can cleanup on shutdown/crash
|
||||
_liveCursors: {},
|
||||
|
||||
init: function() {
|
||||
if (DEBUG) debug("Init");
|
||||
this._messages = ["Contacts:Find", "Contacts:GetAll", "Contacts:GetAll:Continue", "Contacts:Clear", "Contact:Save",
|
||||
this._messages = ["Contacts:Find", "Contacts:GetAll", "Contacts:Clear", "Contact:Save",
|
||||
"Contact:Remove", "Contacts:GetSimContacts",
|
||||
"Contacts:RegisterForMessages", "child-process-shutdown"];
|
||||
this._children = [];
|
||||
|
@ -119,23 +116,6 @@ this.DOMContactManager = {
|
|||
},
|
||||
function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Find:Return:KO", { errorMsg: aErrorMsg }); },
|
||||
msg.findOptions, msg.cursorId);
|
||||
if (Array.isArray(this._liveCursors[mm])) {
|
||||
this._liveCursors[mm].push(msg.cursorId);
|
||||
} else {
|
||||
this._liveCursors[mm] = [msg.cursorId];
|
||||
}
|
||||
break;
|
||||
case "Contacts:GetAll:Continue":
|
||||
this._db.getNext(
|
||||
function(aContact) {
|
||||
if (aContact == null) { // last contact, release the cursor
|
||||
let cursors = this._liveCursors[mm];
|
||||
cursors.splice(cursors.indexOf(msg.cursorId), 1);
|
||||
}
|
||||
mm.sendAsyncMessage("Contacts:GetAll:Next", {cursorId: msg.cursorId, contact: aContact});
|
||||
}.bind(this),
|
||||
function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Find:Return:KO", { errorMsg: aErrorMsg }); },
|
||||
msg.cursorId);
|
||||
break;
|
||||
case "Contact:Save":
|
||||
if (msg.options.reason === "create") {
|
||||
|
@ -210,10 +190,6 @@ this.DOMContactManager = {
|
|||
break;
|
||||
case "child-process-shutdown":
|
||||
if (DEBUG) debug("Unregister");
|
||||
if (this._liveCursors[mm]) {
|
||||
this._db.releaseCursors(this._liveCursors[mm]);
|
||||
delete this._liveCursors[mm];
|
||||
}
|
||||
let index = this._children.indexOf(mm);
|
||||
if (index != -1) {
|
||||
if (DEBUG) debug("Unregister index: " + index);
|
||||
|
|
|
@ -266,7 +266,7 @@ let steps = [
|
|||
add20Contacts,
|
||||
|
||||
function() {
|
||||
ok(true, "Test cache invalidation between getAll and getNext");
|
||||
ok(true, "Test cache consistency when deleting contact during getAll");
|
||||
req = mozContacts.find({});
|
||||
req.onsuccess = function(e) {
|
||||
let lastContact = e.target.result[e.target.result.length-1];
|
||||
|
@ -290,8 +290,7 @@ let steps = [
|
|||
count++;
|
||||
req.continue();
|
||||
} else {
|
||||
is(count, 19, "19 contacts returned");
|
||||
ok(true, "last contact");
|
||||
is(count, 20, "last contact - 20 contacts returned");
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
@ -303,7 +302,7 @@ let steps = [
|
|||
add20Contacts,
|
||||
|
||||
function() {
|
||||
ok(true, "Delete the currect contact while iterating");
|
||||
ok(true, "Delete the current contact while iterating");
|
||||
req = mozContacts.getAll({});
|
||||
let count = 0;
|
||||
let previousId = null;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "SimToolKit.idl"
|
||||
|
||||
interface nsIDOMEventListener;
|
||||
interface nsIDOMDOMRequest;
|
||||
|
||||
[scriptable, builtinclass, uuid(9d898c66-3485-4cd5-ab8d-92ef2988887b)]
|
||||
interface nsIDOMMozIccManager : nsIDOMEventTarget
|
||||
|
@ -272,4 +273,46 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget
|
|||
* ICC.
|
||||
*/
|
||||
[implicit_jscontext] attribute jsval onstksessionend;
|
||||
|
||||
// UICC Secure Element Interfaces
|
||||
|
||||
/**
|
||||
* A secure element is a smart card chip that can hold
|
||||
* several different applications with the necessary security.
|
||||
* The most known secure element is the Universal Integrated Circuit Card (UICC)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Send request to open a logical channel defined by its
|
||||
* application identifier (AID)
|
||||
*
|
||||
* @param aid
|
||||
* The Application Identifier of the Applet to be selected on this channel
|
||||
* return value : An instance of Channel (channelID) if available or null.
|
||||
*/
|
||||
nsIDOMDOMRequest iccOpenChannel(in DOMString aid);
|
||||
|
||||
/**
|
||||
* Interface, used to communicate with an applet through the
|
||||
* Application Data Protocol Units (APDUs) and is
|
||||
* used for all data that is exchanged between the UICC card and the terminal (ME).
|
||||
*
|
||||
* @param channel
|
||||
* The Application Identifier of the Applet to which APDU is directed
|
||||
* @param apdu
|
||||
* Application Protocol Data Unit
|
||||
* return value : Response APDU
|
||||
*/
|
||||
nsIDOMDOMRequest iccExchangeAPDU(in long channel, in jsval apdu);
|
||||
|
||||
/**
|
||||
* Send request to close the selected logical channel identified by its
|
||||
* application identifier (AID)
|
||||
*
|
||||
* @param aid
|
||||
* The Application Identifier of the Applet , to be closed
|
||||
*/
|
||||
nsIDOMDOMRequest iccCloseChannel(in long channel);
|
||||
|
||||
// End of UICC Secure Element Interfaces
|
||||
};
|
||||
|
|
|
@ -157,6 +157,37 @@ IccManager::SendStkEventDownload(const JS::Value& aEvent)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IccManager::IccOpenChannel(const nsAString& aAid, nsIDOMDOMRequest** aRequest)
|
||||
{
|
||||
if (!mProvider) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return mProvider->IccOpenChannel(GetOwner(), aAid, aRequest);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IccManager::IccExchangeAPDU(int32_t aChannel, const jsval& aApdu, nsIDOMDOMRequest** aRequest)
|
||||
{
|
||||
if (!mProvider) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return mProvider->IccExchangeAPDU(GetOwner(), aChannel, aApdu, aRequest);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
IccManager::IccCloseChannel(int32_t aChannel, nsIDOMDOMRequest** aRequest)
|
||||
{
|
||||
if (!mProvider) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return mProvider->IccCloseChannel(GetOwner(), aChannel, aRequest);
|
||||
}
|
||||
|
||||
|
||||
NS_IMPL_EVENT_HANDLER(IccManager, stkcommand)
|
||||
NS_IMPL_EVENT_HANDLER(IccManager, stksessionend)
|
||||
|
||||
|
|
|
@ -1580,6 +1580,9 @@ const MMS_PDU_TYPES = (function () {
|
|||
"to",
|
||||
"date",
|
||||
"x-mms-status"]);
|
||||
add(MMS_PDU_TYPE_ACKNOWLEDGE_IND, false, ["x-mms-message-type",
|
||||
"x-mms-transaction-id",
|
||||
"x-mms-mms-version"]);
|
||||
|
||||
return pdus;
|
||||
})();
|
||||
|
|
|
@ -34,6 +34,12 @@ const CONFIG_SEND_REPORT_ALWAYS = 3;
|
|||
const TIME_TO_BUFFER_MMS_REQUESTS = 30000;
|
||||
const TIME_TO_RELEASE_MMS_CONNECTION = 30000;
|
||||
|
||||
|
||||
const PREF_RETRIEVAL_MODE = 'dom.mms.retrieval_mode';
|
||||
const RETRIEVAL_MODE_MANUAL = "manual";
|
||||
const RETRIEVAL_MODE_AUTOMATIC = "automatic";
|
||||
const RETRIEVAL_MODE_NEVER = "never";
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gpps",
|
||||
"@mozilla.org/network/protocol-proxy-service;1",
|
||||
"nsIProtocolProxyService");
|
||||
|
@ -683,6 +689,48 @@ SendTransaction.prototype = {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Send M-acknowledge.ind back to MMSC.
|
||||
*
|
||||
* @param transactionId
|
||||
* X-Mms-Transaction-ID of the message.
|
||||
* @param reportAllowed
|
||||
* X-Mms-Report-Allowed of the response.
|
||||
*
|
||||
* @see OMA-TS-MMS_ENC-V1_3-20110913-A section 6.4
|
||||
*/
|
||||
function AcknowledgeTransaction(transactionId, reportAllowed) {
|
||||
let headers = {};
|
||||
|
||||
// Mandatory fields
|
||||
headers["x-mms-message-type"] = MMS.MMS_PDU_TYPE_ACKNOWLEDGE_IND;
|
||||
headers["x-mms-transaction-id"] = transactionId;
|
||||
headers["x-mms-mms-version"] = MMS.MMS_VERSION;
|
||||
// Optional fields
|
||||
headers["x-mms-report-allowed"] = reportAllowed;
|
||||
|
||||
this.istream = MMS.PduHelper.compose(null, {headers: headers});
|
||||
}
|
||||
AcknowledgeTransaction.prototype = {
|
||||
/**
|
||||
* @param callback [optional]
|
||||
* A callback function that takes one argument -- the http status.
|
||||
*/
|
||||
run: function run(callback) {
|
||||
let requestCallback;
|
||||
if (callback) {
|
||||
requestCallback = function (httpStatus, data) {
|
||||
// `The MMS Client SHOULD ignore the associated HTTP POST response
|
||||
// from the MMS Proxy-Relay.` ~ OMA-TS-MMS_CTR-V1_3-20110913-A
|
||||
// section 8.2.3 "Retrieving an MM".
|
||||
callback(httpStatus);
|
||||
};
|
||||
}
|
||||
gMmsTransactionHelper.sendRequest("POST", gMmsConnection.mmsc,
|
||||
this.istream, requestCallback);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* MmsService
|
||||
*/
|
||||
|
@ -755,34 +803,53 @@ MmsService.prototype = {
|
|||
*/
|
||||
handleNotificationIndication: function handleNotificationIndication(notification) {
|
||||
// TODO: bug 839436 - make DB be able to save MMS messages
|
||||
// TODO: bug 810067 - support automatic/manual/never retrieval modes
|
||||
|
||||
let url = notification.headers["x-mms-content-location"].uri;
|
||||
// TODO: bug 810091 - don't download message twice on receiving duplicated
|
||||
// notification
|
||||
this.retrieveMessage(url, (function (mmsStatus, retrievedMsg) {
|
||||
debug("retrievedMsg = " + JSON.stringify(retrievedMsg));
|
||||
if (this.isTransientError(mmsStatus)) {
|
||||
// TODO: remove this check after bug 810097 is landed.
|
||||
return;
|
||||
}
|
||||
|
||||
let transactionId = notification.headers["x-mms-transaction-id"];
|
||||
let retrievalMode = RETRIEVAL_MODE_MANUAL;
|
||||
try {
|
||||
retrievalMode = Services.prefs.getCharPref(PREF_RETRIEVAL_MODE);
|
||||
} catch (e) {}
|
||||
|
||||
// For X-Mms-Report-Allowed
|
||||
let wish = notification.headers["x-mms-delivery-report"];
|
||||
// `The absence of the field does not indicate any default value.`
|
||||
// So we go checking the same field in retrieved message instead.
|
||||
if ((wish == null) && retrievedMsg) {
|
||||
wish = retrievedMsg.headers["x-mms-delivery-report"];
|
||||
}
|
||||
let reportAllowed =
|
||||
this.getReportAllowed(this.confSendDeliveryReport, wish);
|
||||
if (RETRIEVAL_MODE_AUTOMATIC === retrievalMode) {
|
||||
this.retrieveMessage(url, (function responseNotify(mmsStatus, retrievedMsg) {
|
||||
debug("retrievedMsg = " + JSON.stringify(retrievedMsg));
|
||||
if (this.isTransientError(mmsStatus)) {
|
||||
// TODO: remove this check after bug 810097 is landed.
|
||||
return;
|
||||
}
|
||||
|
||||
let transaction =
|
||||
new NotifyResponseTransaction(transactionId, mmsStatus, reportAllowed);
|
||||
transaction.run();
|
||||
}).bind(this));
|
||||
let transactionId = notification.headers["x-mms-transaction-id"];
|
||||
|
||||
// For X-Mms-Report-Allowed
|
||||
let wish = notification.headers["x-mms-delivery-report"];
|
||||
// `The absence of the field does not indicate any default value.`
|
||||
// So we go checking the same field in retrieved message instead.
|
||||
if ((wish == null) && retrievedMsg) {
|
||||
wish = retrievedMsg.headers["x-mms-delivery-report"];
|
||||
}
|
||||
let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport, wish);
|
||||
|
||||
let transaction =
|
||||
new NotifyResponseTransaction(transactionId, mmsStatus, reportAllowed);
|
||||
transaction.run();
|
||||
}).bind(this));
|
||||
return;
|
||||
}
|
||||
|
||||
let transactionId = notification.headers["x-mms-transaction-id"];
|
||||
let mmsStatus = RETRIEVAL_MODE_NEVER === retrievalMode ?
|
||||
MMS.MMS_PDU_STATUS_REJECTED : MMS.MMS_PDU_STATUS_DEFERRED;
|
||||
// For X-Mms-Report-Allowed
|
||||
let wish = notification.headers["x-mms-delivery-report"];
|
||||
let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport, wish);
|
||||
|
||||
let transaction = new NotifyResponseTransaction(transactionId,
|
||||
mmsStatus,
|
||||
reportAllowed);
|
||||
transaction.run();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -60,4 +60,17 @@ interface nsIMobileConnectionProvider : nsISupports
|
|||
in unsigned short reason);
|
||||
nsIDOMDOMRequest setCallForwardingOption(in nsIDOMWindow window,
|
||||
in nsIDOMMozMobileCFInfo CFInfo);
|
||||
|
||||
/**
|
||||
* Secure Card Icc communication channel
|
||||
*/
|
||||
nsIDOMDOMRequest iccOpenChannel(in nsIDOMWindow window,
|
||||
in DOMString aid);
|
||||
|
||||
nsIDOMDOMRequest iccExchangeAPDU(in nsIDOMWindow window,
|
||||
in long channel,
|
||||
in jsval apdu);
|
||||
|
||||
nsIDOMDOMRequest iccCloseChannel(in nsIDOMWindow window,
|
||||
in long channel);
|
||||
};
|
||||
|
|
|
@ -22,5 +22,7 @@ EXTRA_JS_MODULES += \
|
|||
Payment.jsm \
|
||||
$(NULL)
|
||||
|
||||
TEST_DIRS += tests
|
||||
|
||||
include $(topsrcdir)/config/config.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# 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
|
||||
|
||||
DIRS = \
|
||||
$(NULL)
|
||||
|
||||
MODULE = test_dom_payment
|
||||
|
||||
XPCSHELL_TESTS = unit
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,38 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
let subscriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader);
|
||||
|
||||
/**
|
||||
* Start a new payment module.
|
||||
*
|
||||
* @param custom_ns
|
||||
* Namespace with symbols to be injected into the new payment module
|
||||
* namespace.
|
||||
*
|
||||
* @return an object that represents the payment module's namespace.
|
||||
*/
|
||||
function newPaymentModule(custom_ns) {
|
||||
let payment_ns = {
|
||||
importScripts: function fakeImportScripts() {
|
||||
Array.slice(arguments).forEach(function (script) {
|
||||
subscriptLoader.loadSubScript("resource://gre/modules/" + script, this);
|
||||
}, this);
|
||||
},
|
||||
};
|
||||
|
||||
// Copy the custom definitions over.
|
||||
for (let key in custom_ns) {
|
||||
payment_ns[key] = custom_ns[key];
|
||||
}
|
||||
|
||||
// Load the payment module itself.
|
||||
payment_ns.importScripts("Payment.jsm");
|
||||
|
||||
return payment_ns;
|
||||
}
|
|
@ -0,0 +1,348 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
function getPaymentHelper() {
|
||||
let error;
|
||||
let paym = newPaymentModule();
|
||||
|
||||
paym.PaymentManager.paymentFailed = function paymentFailed(aRequestId,
|
||||
errorMsg) {
|
||||
error = errorMsg;
|
||||
};
|
||||
|
||||
return {
|
||||
get paymentModule() {
|
||||
return paym;
|
||||
},
|
||||
get error() {
|
||||
return error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function testGetPaymentRequest(paymentProviders, test) {
|
||||
let helper = getPaymentHelper();
|
||||
let paym = helper.paymentModule;
|
||||
|
||||
paym.PaymentManager.registeredProviders = paymentProviders;
|
||||
|
||||
let ret = paym.PaymentManager.getPaymentRequestInfo("", test.jwt);
|
||||
if (!test.result) {
|
||||
test.ret ? do_check_true(ret) : do_check_false(ret);
|
||||
}
|
||||
if (test.error !== null) {
|
||||
do_check_eq(helper.error, test.error);
|
||||
} else {
|
||||
do_check_eq(typeof ret, "object");
|
||||
do_check_eq(ret.jwt, test.jwt);
|
||||
do_check_eq(ret.type, test.result.type);
|
||||
do_check_eq(ret.providerName, test.result.providerName);
|
||||
}
|
||||
}
|
||||
|
||||
add_test(function test_successfull_request() {
|
||||
let providers = {};
|
||||
let type = "mock/payments/inapp/v1";
|
||||
providers[type] = {
|
||||
name: "mockprovider",
|
||||
description: "Mock Payment Provider",
|
||||
uri: "https://mockpayprovider.phpfogapp.com/?req=",
|
||||
requestMethod: "GET"
|
||||
};
|
||||
|
||||
// Payload
|
||||
// {
|
||||
// "aud": "mockpayprovider.phpfogapp.com",
|
||||
// "iss": "Enter you app key here!",
|
||||
// "request": {
|
||||
// "name": "Piece of Cake",
|
||||
// "price": "10.50",
|
||||
// "priceTier": 1,
|
||||
// "productdata": "transaction_id=86",
|
||||
// "currencyCode": "USD",
|
||||
// "description": "Virtual chocolate cake to fill your virtual tummy"
|
||||
// },
|
||||
// "exp": 1352232792,
|
||||
// "iat": 1352229192,
|
||||
// "typ": "mock/payments/inapp/v1"
|
||||
// }
|
||||
let jwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJhdWQiOiAibW9j" +
|
||||
"a3BheXByb3ZpZGVyLnBocGZvZ2FwcC5jb20iLCAiaXNzIjogIkVudGVyI" +
|
||||
"HlvdSBhcHAga2V5IGhlcmUhIiwgInJlcXVlc3QiOiB7Im5hbWUiOiAiUG" +
|
||||
"llY2Ugb2YgQ2FrZSIsICJwcmljZSI6ICIxMC41MCIsICJwcmljZVRpZXI" +
|
||||
"iOiAxLCAicHJvZHVjdGRhdGEiOiAidHJhbnNhY3Rpb25faWQ9ODYiLCAi" +
|
||||
"Y3VycmVuY3lDb2RlIjogIlVTRCIsICJkZXNjcmlwdGlvbiI6ICJWaXJ0d" +
|
||||
"WFsIGNob2NvbGF0ZSBjYWtlIHRvIGZpbGwgeW91ciB2aXJ0dWFsIHR1bW" +
|
||||
"15In0sICJleHAiOiAxMzUyMjMyNzkyLCAiaWF0IjogMTM1MjIyOTE5Miw" +
|
||||
"gInR5cCI6ICJtb2NrL3BheW1lbnRzL2luYXBwL3YxIn0.QZxc62USCy4U" +
|
||||
"IyKIC1TKelVhNklvk-Ou1l_daKntaFI";
|
||||
|
||||
testGetPaymentRequest(providers, {
|
||||
jwt: jwt,
|
||||
ret: true,
|
||||
error: null,
|
||||
result: {
|
||||
type: type,
|
||||
providerName: providers[type].name
|
||||
}
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_successfull_request_html_description() {
|
||||
let providers = {};
|
||||
let type = "mozilla/payments/pay/v1";
|
||||
providers[type] = {
|
||||
name: "webpay",
|
||||
description: "Mozilla Payment Provider",
|
||||
uri: "https://marketplace.firefox.com/mozpay/?req=",
|
||||
requestMethod: "GET"
|
||||
};
|
||||
|
||||
// Payload
|
||||
// {
|
||||
// "aud": "marketplace.firefox.com",
|
||||
// "iss": "marketplace-dev.allizom.org",
|
||||
// "request": {
|
||||
// "name": "Krupa's paid app 1",
|
||||
// "chargebackURL": "http://localhost:8002/telefonica/services/webpay/"
|
||||
// "chargeback",
|
||||
// "postbackURL": "http://localhost:8002/telefonica/services/webpay/"
|
||||
// "postback",
|
||||
// "productData": "addon_id=85&seller_uuid=d4855df9-6ce0-45cd-81cb-"
|
||||
// "cf8737e1e7aa&contrib_uuid=201868b7ac2cda410a99b3"
|
||||
// "ed4c11a8ea",
|
||||
// "pricePoint": 1,
|
||||
// "id": "maude:85",
|
||||
// "description": "This app has been automatically generated by <a href="
|
||||
// "\"http://outgoing.mozilla.org/v1/ba7f373ae16789eff3ab"
|
||||
// "fd95ca8d3c15d18dc9009afa204dc43f85a55b1f6ef1/http%3A/"
|
||||
// "/testmanifest.com\" rel=\"nofollow\">testmanifest.com"
|
||||
// "</a>"
|
||||
// },
|
||||
// "exp": 1358379147,
|
||||
// "iat": 1358375547,
|
||||
// "typ": "mozilla/payments/pay/v1"
|
||||
// }
|
||||
let jwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJhdWQiOiAibWFya2V0cGx" +
|
||||
"hY2UuZmlyZWZveC5jb20iLCAiaXNzIjogIm1hcmtldHBsYWNlLWRldi5hbGxpem9" +
|
||||
"tLm9yZyIsICJyZXF1ZXN0IjogeyJuYW1lIjogIktydXBhJ3MgcGFpZCBhcHAgMSI" +
|
||||
"sICJjaGFyZ2ViYWNrVVJMIjogImh0dHA6Ly9sb2NhbGhvc3Q6ODAwMi90ZWxlZm9" +
|
||||
"uaWNhL3NlcnZpY2VzL3dlYnBheS9jaGFyZ2ViYWNrIiwgInBvc3RiYWNrVVJMIjo" +
|
||||
"gImh0dHA6Ly9sb2NhbGhvc3Q6ODAwMi90ZWxlZm9uaWNhL3NlcnZpY2VzL3dlYnB" +
|
||||
"heS9wb3N0YmFjayIsICJwcm9kdWN0RGF0YSI6ICJhZGRvbl9pZD04NSZzZWxsZXJ" +
|
||||
"fdXVpZD1kNDg1NWRmOS02Y2UwLTQ1Y2QtODFjYi1jZjg3MzdlMWU3YWEmY29udHJ" +
|
||||
"pYl91dWlkPTIwMTg2OGI3YWMyY2RhNDEwYTk5YjNlZDRjMTFhOGVhIiwgInByaWN" +
|
||||
"lUG9pbnQiOiAxLCAiaWQiOiAibWF1ZGU6ODUiLCAiZGVzY3JpcHRpb24iOiAiVGh" +
|
||||
"pcyBhcHAgaGFzIGJlZW4gYXV0b21hdGljYWxseSBnZW5lcmF0ZWQgYnkgPGEgaHJ" +
|
||||
"lZj1cImh0dHA6Ly9vdXRnb2luZy5tb3ppbGxhLm9yZy92MS9iYTdmMzczYWUxNjc" +
|
||||
"4OWVmZjNhYmZkOTVjYThkM2MxNWQxOGRjOTAwOWFmYTIwNGRjNDNmODVhNTViMWY" +
|
||||
"2ZWYxL2h0dHAlM0EvL3Rlc3RtYW5pZmVzdC5jb21cIiByZWw9XCJub2ZvbGxvd1w" +
|
||||
"iPnRlc3RtYW5pZmVzdC5jb208L2E-In0sICJleHAiOiAxMzU4Mzc5MTQ3LCAiaWF" +
|
||||
"0IjogMTM1ODM3NTU0NywgInR5cCI6ICJtb3ppbGxhL3BheW1lbnRzL3BheS92MSJ" +
|
||||
"9.kgSt636OSRBezMGtm9QLeDxlEOevL4xcOoDj8VRJyD8";
|
||||
|
||||
testGetPaymentRequest(providers, {
|
||||
jwt: jwt,
|
||||
ret: true,
|
||||
error: null,
|
||||
result: {
|
||||
type: type,
|
||||
providerName: providers[type].name
|
||||
}
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_empty_jwt() {
|
||||
testGetPaymentRequest(null, {
|
||||
jwt: "",
|
||||
ret: true,
|
||||
error: "INTERNAL_ERROR_CALL_WITH_MISSING_JWT"
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_wrong_segments_count() {
|
||||
// 1 segment JWT
|
||||
let OneSegJwt = "eyJhbGciOiJIUzI1NiJ9";
|
||||
testGetPaymentRequest(null, {
|
||||
jwt: OneSegJwt,
|
||||
ret: true,
|
||||
error: "PAY_REQUEST_ERROR_WRONG_SEGMENTS_COUNT"
|
||||
});
|
||||
|
||||
// 2 segments JWT
|
||||
let TwoSegJwt = "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIwNTg2NDkwMTM2NTY2N" +
|
||||
"zU1ODY2MSIsImF1ZCI6Ikdvb2dsZSIsInR5cCI6Imdvb2dsZS9" +
|
||||
"wYXltZW50cy9pbmFwcC9pdGVtL3YxIiwiaWF0IjoxMzUyMjIwM" +
|
||||
"jEyLCJleHAiOjEzNTIzMDY2MTIsInJlcXVlc3QiOnsiY3VycmV" +
|
||||
"uY3lDb2RlIjoiVVNEIiwicHJpY2UiOiIzLjAwIiwibmFtZSI6I" +
|
||||
"kdvbGQgU3RhciIsInNlbGxlckRhdGEiOiJzb21lIG9wYXF1ZSB" +
|
||||
"kYXRhIiwiZGVzY3JpcHRpb24iOiJBIHNoaW5pbmcgYmFkZ2Ugb" +
|
||||
"2YgZGlzdGluY3Rpb24ifX0";
|
||||
|
||||
testGetPaymentRequest(null, {
|
||||
jwt: TwoSegJwt,
|
||||
ret: true,
|
||||
error: "PAY_REQUEST_ERROR_WRONG_SEGMENTS_COUNT"
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_empty_payload() {
|
||||
let EmptyPayloadJwt = "eyJhbGciOiJIUzI1NiJ9..eyJpc3MiOiIwNTg2NDkwMTM2NTY2N";
|
||||
|
||||
testGetPaymentRequest(null, {
|
||||
jwt: EmptyPayloadJwt,
|
||||
ret: true,
|
||||
error: "PAY_REQUEST_ERROR_EMPTY_PAYLOAD"
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_missing_typ_parameter() {
|
||||
// Payload
|
||||
// {
|
||||
// "iss": "640ae477-df33-45cd-83b8-6f1f910a6494",
|
||||
// "iat": 1361203745,
|
||||
// "request": {
|
||||
// "description": "detailed description",
|
||||
// "id": "799db970-7afa-4028-bdb7-8b045eb8babc",
|
||||
// "postbackURL": "http://inapp-pay-test.farmdev.com/postback",
|
||||
// "productData": "transaction_id=58",
|
||||
// "pricePoint": 1,
|
||||
// "chargebackURL": "http://inapp-pay-test.farmdev.com/chargeback",
|
||||
// "name": "The Product"
|
||||
// },
|
||||
// "aud": "marketplace-dev.allizom.org",
|
||||
// "exp": 1361207345
|
||||
// }
|
||||
let missingTypJwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9." +
|
||||
"eyJpc3MiOiAiNjQwYWU0NzctZGYzMy00NWNkLTgzY" +
|
||||
"jgtNmYxZjkxMGE2NDk0IiwgImlhdCI6IDEzNjEyMD" +
|
||||
"M3NDUsICJyZXF1ZXN0IjogeyJkZXNjcmlwdGlvbiI" +
|
||||
"6ICJkZXRhaWxlZCBkZXNjcmlwdGlvbiIsICJpZCI6" +
|
||||
"ICI3OTlkYjk3MC03YWZhLTQwMjgtYmRiNy04YjA0N" +
|
||||
"WViOGJhYmMiLCAicG9zdGJhY2tVUkwiOiAiaHR0cD" +
|
||||
"ovL2luYXBwLXBheS10ZXN0LmZhcm1kZXYuY29tL3B" +
|
||||
"vc3RiYWNrIiwgInByb2R1Y3REYXRhIjogInRyYW5z" +
|
||||
"YWN0aW9uX2lkPTU4IiwgInByaWNlUG9pbnQiOiAxL" +
|
||||
"CAiY2hhcmdlYmFja1VSTCI6ICJodHRwOi8vaW5hcH" +
|
||||
"AtcGF5LXRlc3QuZmFybWRldi5jb20vY2hhcmdlYmF" +
|
||||
"jayIsICJuYW1lIjogIlRoZSBQcm9kdWN0In0sICJh" +
|
||||
"dWQiOiAibWFya2V0cGxhY2UtZGV2LmFsbGl6b20ub" +
|
||||
"3JnIiwgImV4cCI6IDEzNjEyMDczNDV9.KAHsJX1Hy" +
|
||||
"fmwNvAckdVUqlpPvdHggpx9yX276TWacRg";
|
||||
testGetPaymentRequest(null, {
|
||||
jwt: missingTypJwt,
|
||||
ret: true,
|
||||
error: "PAY_REQUEST_ERROR_NO_TYP_PARAMETER"
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_missing_request_parameter() {
|
||||
// Payload
|
||||
// {
|
||||
// "iss": "Enter you app key here!",
|
||||
// "iat": 1352225299,
|
||||
// "typ": "mock/payments/inapp/v1",
|
||||
// "aud": "mockpayprovider.phpfogapp.com",
|
||||
// "exp": 1352228899
|
||||
// }
|
||||
let missingRequestJwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9." +
|
||||
"eyJpc3MiOiAiRW50ZXIgeW91IGFwcCBrZXkgaGVyZ" +
|
||||
"SEiLCAiaWF0IjogMTM1MjIyNTI5OSwgInR5cCI6IC" +
|
||||
"Jtb2NrL3BheW1lbnRzL2luYXBwL3YxIiwgImF1ZCI" +
|
||||
"6ICJtb2NrcGF5cHJvdmlkZXIucGhwZm9nYXBwLmNv" +
|
||||
"bSIsICJleHAiOiAxMzUyMjI4ODk5fQ.yXGinvZiUs" +
|
||||
"v9JWvdfM6zPD0iOX9DgCPcIwIbCrL4tcs";
|
||||
|
||||
testGetPaymentRequest(null, {
|
||||
jwt: missingRequestJwt,
|
||||
ret: true,
|
||||
error: "PAY_REQUEST_ERROR_NO_REQUEST_PARAMETER"
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_jwt_decoding_error() {
|
||||
let wrongJwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.^eyJhdWQiOiAibW9" +
|
||||
"a3BheXByb3ZpZGVyLnBocGZvZ2FwcC5jb20iLCAiaXNzIjogIkVudGVyI" +
|
||||
"HlvdSBhcHAga2V5IGhlcmUhIiwgInJlcXVlc3QiOiB7Im5hbWUiOiAiUG" +
|
||||
"llY2Ugb2YgQ2FrZSIsICJwcmljZSI6ICIxMC41MCIsICJwcmljZVRpZXI" +
|
||||
"iOiAxLCAicHJvZHVjdGRhdGEiOiAidHJhbnNhY3Rpb25faWQ9ODYiLCAi" +
|
||||
"Y3VycmVuY3lDb2RlIjogIlVTRCIsICJkZXNjcmlwdGlvbiI6ICJWaXJ0d" +
|
||||
"WFsIGNob2NvbGF0ZSBjYWtlIHRvIGZpbGwgeW91ciB2aXJ0dWFsIHR1bW" +
|
||||
"15In0sICJleHAiOiAxMzUyMjMyNzkyLCAiaWF0IjogMTM1MjIyOTE5Miw" +
|
||||
"gInR5cCI6ICJtb2NrL3BheW1lbnRzL2luYXBwL3YxIn0.QZxc62USCy4U" +
|
||||
"IyKIC1TKelVhNklvk-Ou1l_daKntaFI";
|
||||
|
||||
testGetPaymentRequest(null, {
|
||||
jwt: wrongJwt,
|
||||
ret: true,
|
||||
error: "PAY_REQUEST_ERROR_ERROR_DECODING_JWT"
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_non_https_provider() {
|
||||
let providers = {};
|
||||
let type = "mock/payments/inapp/v1";
|
||||
providers[type] = {
|
||||
name: "mockprovider",
|
||||
description: "Mock Payment Provider",
|
||||
uri: "http://mockpayprovider.phpfogapp.com/?req=",
|
||||
requestMethod: "GET"
|
||||
};
|
||||
|
||||
// Payload
|
||||
// {
|
||||
// "aud": "mockpayprovider.phpfogapp.com",
|
||||
// "iss": "Enter you app key here!",
|
||||
// "request": {
|
||||
// "name": "Piece of Cake",
|
||||
// "price": "10.50",
|
||||
// "priceTier": 1,
|
||||
// "productdata": "transaction_id=86",
|
||||
// "currencyCode": "USD",
|
||||
// "description": "Virtual chocolate cake to fill your virtual tummy"
|
||||
// },
|
||||
// "exp": 1352232792,
|
||||
// "iat": 1352229192,
|
||||
// "typ": "mock/payments/inapp/v1"
|
||||
// }
|
||||
let jwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJhdWQiOiAibW9j" +
|
||||
"a3BheXByb3ZpZGVyLnBocGZvZ2FwcC5jb20iLCAiaXNzIjogIkVudGVyI" +
|
||||
"HlvdSBhcHAga2V5IGhlcmUhIiwgInJlcXVlc3QiOiB7Im5hbWUiOiAiUG" +
|
||||
"llY2Ugb2YgQ2FrZSIsICJwcmljZSI6ICIxMC41MCIsICJwcmljZVRpZXI" +
|
||||
"iOiAxLCAicHJvZHVjdGRhdGEiOiAidHJhbnNhY3Rpb25faWQ9ODYiLCAi" +
|
||||
"Y3VycmVuY3lDb2RlIjogIlVTRCIsICJkZXNjcmlwdGlvbiI6ICJWaXJ0d" +
|
||||
"WFsIGNob2NvbGF0ZSBjYWtlIHRvIGZpbGwgeW91ciB2aXJ0dWFsIHR1bW" +
|
||||
"15In0sICJleHAiOiAxMzUyMjMyNzkyLCAiaWF0IjogMTM1MjIyOTE5Miw" +
|
||||
"gInR5cCI6ICJtb2NrL3BheW1lbnRzL2luYXBwL3YxIn0.QZxc62USCy4U" +
|
||||
"IyKIC1TKelVhNklvk-Ou1l_daKntaFI";
|
||||
|
||||
testGetPaymentRequest(providers, {
|
||||
jwt: jwt,
|
||||
ret: true,
|
||||
error: "INTERNAL_ERROR_NON_HTTPS_PROVIDER_URI"
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
[DEFAULT]
|
||||
head = header_helper.js
|
||||
tail =
|
||||
|
||||
[test_paymanager_get_payment_request.js]
|
|
@ -1621,7 +1621,7 @@ NPObjWrapper_NewResolve(JSContext *cx, JSHandleObject obj, JSHandleId id, unsign
|
|||
NS_ASSERTION(JSID_IS_STRING(id) || JSID_IS_INT(id),
|
||||
"id must be either string or int!\n");
|
||||
if (!::JS_DefinePropertyById(cx, obj, id, JSVAL_VOID, nullptr,
|
||||
nullptr, JSPROP_ENUMERATE)) {
|
||||
nullptr, JSPROP_ENUMERATE | JSPROP_SHARED)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,10 @@ const RIL_IPC_MSG_NAMES = [
|
|||
"RIL:SetCallForwardingOption",
|
||||
"RIL:GetCallForwardingOption",
|
||||
"RIL:CellBroadcastReceived",
|
||||
"RIL:CfStateChanged"
|
||||
"RIL:CfStateChanged",
|
||||
"RIL:IccOpenChannel",
|
||||
"RIL:IccCloseChannel",
|
||||
"RIL:IccExchangeAPDU"
|
||||
];
|
||||
|
||||
const kVoiceChangedTopic = "mobile-connection-voice-changed";
|
||||
|
@ -611,6 +614,54 @@ RILContentHelper.prototype = {
|
|||
cpmm.sendAsyncMessage("RIL:SendStkEventDownload", {event: event});
|
||||
},
|
||||
|
||||
iccOpenChannel: function iccOpenChannel(window,
|
||||
aid) {
|
||||
if (window == null) {
|
||||
throw Components.Exception("Can't get window object",
|
||||
Cr.NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
let request = Services.DOMRequest.createRequest(window);
|
||||
let requestId = this.getRequestId(request);
|
||||
|
||||
cpmm.sendAsyncMessage("RIL:IccOpenChannel", {requestId: requestId,
|
||||
aid: aid});
|
||||
return request;
|
||||
},
|
||||
|
||||
iccExchangeAPDU: function iccExchangeAPDU(window,
|
||||
channel,
|
||||
apdu) {
|
||||
if (window == null) {
|
||||
throw Components.Exception("Can't get window object",
|
||||
Cr.NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
let request = Services.DOMRequest.createRequest(window);
|
||||
let requestId = this.getRequestId(request);
|
||||
|
||||
//Potentially you need serialization here and can't pass the jsval through
|
||||
cpmm.sendAsyncMessage("RIL:IccExchangeAPDU", {requestId: requestId,
|
||||
channel: channel,
|
||||
apdu: apdu});
|
||||
return request;
|
||||
},
|
||||
|
||||
iccCloseChannel: function iccCloseChannel(window,
|
||||
channel) {
|
||||
if (window == null) {
|
||||
throw Components.Exception("Can't get window object",
|
||||
Cr.NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
let request = Services.DOMRequest.createRequest(window);
|
||||
let requestId = this.getRequestId(request);
|
||||
|
||||
cpmm.sendAsyncMessage("RIL:IccCloseChannel", {requestId: requestId,
|
||||
channel: channel});
|
||||
return request;
|
||||
},
|
||||
|
||||
getCallForwardingOption: function getCallForwardingOption(window, reason) {
|
||||
if (window == null) {
|
||||
throw Components.Exception("Can't get window object",
|
||||
|
@ -988,6 +1039,15 @@ RILContentHelper.prototype = {
|
|||
case "RIL:StkSessionEnd":
|
||||
Services.obs.notifyObservers(null, kStkSessionEndTopic, null);
|
||||
break;
|
||||
case "RIL:IccOpenChannel":
|
||||
this.handleIccOpenChannel(msg.json);
|
||||
break;
|
||||
case "RIL:IccCloseChannel":
|
||||
this.handleIccCloseChannel(msg.json);
|
||||
break;
|
||||
case "RIL:IccExchangeAPDU":
|
||||
this.handleIccExchangeAPDU(msg.json);
|
||||
break;
|
||||
case "RIL:DataError":
|
||||
this.updateConnectionInfo(msg.json, this.rilContext.dataConnectionInfo);
|
||||
Services.obs.notifyObservers(null, kDataErrorTopic, msg.json.error);
|
||||
|
@ -1075,6 +1135,31 @@ RILContentHelper.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
handleIccOpenChannel: function handleIccOpenChannel(message) {
|
||||
if (message.error) {
|
||||
this.fireRequestError(message.requestId, message.error);
|
||||
} else {
|
||||
this.fireRequestSuccess(message.requestId, message.channel);
|
||||
}
|
||||
},
|
||||
|
||||
handleIccCloseChannel: function handleIccCloseChannel(message) {
|
||||
if (message.error) {
|
||||
this.fireRequestError(message.requestId, message.error);
|
||||
} else {
|
||||
this.fireRequestSuccess(message.requestId, null);
|
||||
}
|
||||
},
|
||||
|
||||
handleIccExchangeAPDU: function handleIccExchangeAPDU(message) {
|
||||
if (message.error) {
|
||||
this.fireRequestError(message.requestId, message.error);
|
||||
} else {
|
||||
var result = [message.sw1, message.sw2, message.simResponse];
|
||||
this.fireRequestSuccess(message.requestId, result);
|
||||
}
|
||||
},
|
||||
|
||||
handleVoicemailNotification: function handleVoicemailNotification(message) {
|
||||
let changed = false;
|
||||
if (!this.voicemailStatus) {
|
||||
|
|
|
@ -96,6 +96,9 @@ const RIL_IPC_MOBILECONNECTION_MSG_NAMES = [
|
|||
"RIL:SendStkMenuSelection",
|
||||
"RIL:SendStkTimerExpiration",
|
||||
"RIL:SendStkEventDownload",
|
||||
"RIL:IccOpenChannel",
|
||||
"RIL:IccExchangeAPDU",
|
||||
"RIL:IccCloseChannel",
|
||||
"RIL:RegisterMobileConnectionMsg",
|
||||
"RIL:SetCallForwardingOption",
|
||||
"RIL:GetCallForwardingOption"
|
||||
|
@ -493,6 +496,18 @@ RadioInterfaceLayer.prototype = {
|
|||
case "RIL:SendStkEventDownload":
|
||||
this.sendStkEventDownload(msg.json);
|
||||
break;
|
||||
case "RIL:IccOpenChannel":
|
||||
this.saveRequestTarget(msg);
|
||||
this.iccOpenChannel(msg.json);
|
||||
break;
|
||||
case "RIL:IccCloseChannel":
|
||||
this.saveRequestTarget(msg);
|
||||
this.iccCloseChannel(msg.json);
|
||||
break;
|
||||
case "RIL:IccExchangeAPDU":
|
||||
this.saveRequestTarget(msg);
|
||||
this.iccExchangeAPDU(msg.json);
|
||||
break;
|
||||
case "RIL:RegisterMobileConnectionMsg":
|
||||
this.registerMessageTarget("mobileconnection", msg.target);
|
||||
break;
|
||||
|
@ -551,6 +566,15 @@ RadioInterfaceLayer.prototype = {
|
|||
case "callError":
|
||||
this.handleCallError(message);
|
||||
break;
|
||||
case "iccOpenChannel":
|
||||
this.handleIccOpenChannel(message);
|
||||
break;
|
||||
case "iccCloseChannel":
|
||||
this.handleIccCloseChannel(message);
|
||||
break;
|
||||
case "iccExchangeAPDU":
|
||||
this.handleIccExchangeAPDU(message);
|
||||
break;
|
||||
case "getAvailableNetworks":
|
||||
this.handleGetAvailableNetworks(message);
|
||||
break;
|
||||
|
@ -1364,6 +1388,30 @@ RadioInterfaceLayer.prototype = {
|
|||
this._sendRequestResults("RIL:EnumerateCalls", options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Open Logical UICC channel (aid) for Secure Element access
|
||||
*/
|
||||
handleIccOpenChannel: function handleIccOpenChannel(message) {
|
||||
debug("handleIccOpenChannel: " + JSON.stringify(message));
|
||||
this._sendRequestResults("RIL:IccOpenChannel", message);
|
||||
},
|
||||
|
||||
/**
|
||||
* Close Logical UICC channel
|
||||
*/
|
||||
handleIccCloseChannel: function handleIccCloseChannel(message) {
|
||||
debug("handleIccCloseChannel: " + JSON.stringify(message));
|
||||
this._sendRequestResults("RIL:IccCloseChannel", message);
|
||||
},
|
||||
|
||||
/**
|
||||
* Exchange APDU data on an open Logical UICC channel
|
||||
*/
|
||||
handleIccExchangeAPDU: function handleIccExchangeAPDU(message) {
|
||||
debug("handleIccExchangeAPDU: " + JSON.stringify(message));
|
||||
this._sendRequestResults("RIL:IccExchangeAPDU", message);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle available networks returned by the 'getAvailableNetworks' request.
|
||||
*/
|
||||
|
@ -2098,6 +2146,24 @@ RadioInterfaceLayer.prototype = {
|
|||
this.worker.postMessage(message);
|
||||
},
|
||||
|
||||
iccOpenChannel: function iccOpenChannel(message) {
|
||||
debug("ICC Open Channel");
|
||||
message.rilMessageType = "iccOpenChannel";
|
||||
this.worker.postMessage(message);
|
||||
},
|
||||
|
||||
iccCloseChannel: function iccCloseChannel(message) {
|
||||
debug("ICC Close Channel");
|
||||
message.rilMessageType = "iccCloseChannel";
|
||||
this.worker.postMessage(message);
|
||||
},
|
||||
|
||||
iccExchangeAPDU: function iccExchangeAPDU(message) {
|
||||
debug("ICC Exchange APDU");
|
||||
message.rilMessageType = "iccExchangeAPDU";
|
||||
this.worker.postMessage(message);
|
||||
},
|
||||
|
||||
setCallForwardingOption: function setCallForwardingOption(message) {
|
||||
debug("setCallForwardingOption: " + JSON.stringify(message));
|
||||
message.rilMessageType = "setCallForward";
|
||||
|
|
|
@ -149,6 +149,11 @@ this.REQUEST_MODIFY_QOS = 118;
|
|||
this.REQUEST_SUSPEND_QOS = 119;
|
||||
this.REQUEST_RESUME_QOS = 120;
|
||||
|
||||
// UICC Secure Access
|
||||
this.REQUEST_SIM_OPEN_CHANNEL = 121;
|
||||
this.REQUEST_SIM_CLOSE_CHANNEL = 122;
|
||||
this.REQUEST_SIM_ACCESS_CHANNEL = 123;
|
||||
|
||||
this.RESPONSE_TYPE_SOLICITED = 0;
|
||||
this.RESPONSE_TYPE_UNSOLICITED = 1;
|
||||
|
||||
|
|
|
@ -1470,6 +1470,70 @@ let RIL = {
|
|||
Buf.simpleRequest(REQUEST_SET_NETWORK_SELECTION_AUTOMATIC, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Open Logical UICC channel (aid) for Secure Element access
|
||||
*/
|
||||
iccOpenChannel: function iccOpenChannel(options) {
|
||||
if (DEBUG) {
|
||||
debug("iccOpenChannel: " + JSON.stringify(options));
|
||||
}
|
||||
|
||||
let token = Buf.newParcel(REQUEST_SIM_OPEN_CHANNEL, options);
|
||||
Buf.writeString(options.aid);
|
||||
Buf.sendParcel();
|
||||
},
|
||||
|
||||
/**
|
||||
* Exchange APDU data on an open Logical UICC channel
|
||||
*/
|
||||
iccExchangeAPDU: function iccExchangeAPDU(options) {
|
||||
if (DEBUG) debug("iccExchangeAPDU: " + JSON.stringify(options));
|
||||
|
||||
var cla = options.apdu.cla;
|
||||
var command = options.apdu.command;
|
||||
var channel = options.channel;
|
||||
var path = options.apdu.path;
|
||||
var data = options.apdu.data;
|
||||
var data2 = options.apdu.data2;
|
||||
if (path == null || path === undefined) {
|
||||
var path = "";
|
||||
}
|
||||
if (data == null || data === undefined) {
|
||||
var data = "";
|
||||
}
|
||||
if (data2 == null || data2 === undefined) {
|
||||
var data2 = "";
|
||||
}
|
||||
var p1 = options.apdu.p1;
|
||||
var p2 = options.apdu.p2;
|
||||
var p3 = options.apdu.p3; // Extra
|
||||
|
||||
Buf.newParcel(REQUEST_SIM_ACCESS_CHANNEL, options);
|
||||
Buf.writeUint32(cla);
|
||||
Buf.writeUint32(command);
|
||||
Buf.writeUint32(channel);
|
||||
Buf.writeString(path); // path
|
||||
Buf.writeUint32(p1);
|
||||
Buf.writeUint32(p2);
|
||||
Buf.writeUint32(p3);
|
||||
Buf.writeString(data); // generic data field.
|
||||
Buf.writeString(data2);
|
||||
|
||||
Buf.sendParcel();
|
||||
},
|
||||
|
||||
/**
|
||||
* Close Logical UICC channel
|
||||
*/
|
||||
iccCloseChannel: function iccCloseChannel(options) {
|
||||
if (DEBUG) debug("iccCloseChannel: " + JSON.stringify(options));
|
||||
|
||||
Buf.newParcel(REQUEST_SIM_CLOSE_CHANNEL, options);
|
||||
Buf.writeUint32(1);
|
||||
Buf.writeUint32(options.channel);
|
||||
Buf.sendParcel();
|
||||
},
|
||||
|
||||
/**
|
||||
* Tell the radio to choose a specific voice/data network
|
||||
*/
|
||||
|
@ -4690,6 +4754,42 @@ RIL[REQUEST_SET_FACILITY_LOCK] = function REQUEST_SET_FACILITY_LOCK(length, opti
|
|||
this.sendDOMMessage(options);
|
||||
};
|
||||
RIL[REQUEST_CHANGE_BARRING_PASSWORD] = null;
|
||||
RIL[REQUEST_SIM_OPEN_CHANNEL] = function REQUEST_SIM_OPEN_CHANNEL(length, options) {
|
||||
if (options.rilRequestError) {
|
||||
options.error = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
|
||||
this.sendDOMMessage(options);
|
||||
return;
|
||||
}
|
||||
|
||||
options.channel = Buf.readUint32();
|
||||
if (DEBUG) debug("Setting channel number in options: " + options.channel);
|
||||
this.sendDOMMessage(options);
|
||||
};
|
||||
RIL[REQUEST_SIM_CLOSE_CHANNEL] = function REQUEST_SIM_CLOSE_CHANNEL(length, options) {
|
||||
if (options.rilRequestError) {
|
||||
options.error = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
|
||||
this.sendDOMMessage(options);
|
||||
return;
|
||||
}
|
||||
|
||||
// No return value
|
||||
this.sendDOMMessage(options);
|
||||
};
|
||||
RIL[REQUEST_SIM_ACCESS_CHANNEL] = function REQUEST_SIM_ACCESS_CHANNEL(length, options) {
|
||||
if (options.rilRequestError) {
|
||||
options.error = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
|
||||
this.sendDOMMessage(options);
|
||||
}
|
||||
|
||||
options.sw1 = Buf.readUint32();
|
||||
options.sw2 = Buf.readUint32();
|
||||
options.simResponse = Buf.readString();
|
||||
if (DEBUG) {
|
||||
debug("Setting return values for RIL[REQUEST_SIM_ACCESS_CHANNEL]: ["
|
||||
+ options.sw1 + "," + options.sw2 + ", " + options.simResponse + "]");
|
||||
}
|
||||
this.sendDOMMessage(options);
|
||||
};
|
||||
RIL[REQUEST_QUERY_NETWORK_SELECTION_MODE] = function REQUEST_QUERY_NETWORK_SELECTION_MODE(length, options) {
|
||||
this._receivedNetworkInfo(NETWORK_INFO_NETWORK_SELECTION_MODE);
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "nsString.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nspr.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
extern PRLogModuleInfo *MCD;
|
||||
|
||||
|
@ -242,12 +243,12 @@ nsresult nsReadConfig::openAndEvaluateJSFile(const char *aFileName, int32_t obsc
|
|||
nsCOMPtr<nsIInputStream> inStr;
|
||||
if (isBinDir) {
|
||||
nsCOMPtr<nsIFile> jsFile;
|
||||
rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
|
||||
rv = NS_GetSpecialDirectory(XRE_EXECUTABLE_FILE,
|
||||
getter_AddRefs(jsFile));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
rv = jsFile->AppendNative(nsDependentCString(aFileName));
|
||||
rv = jsFile->SetNativeLeafName(nsDependentCString(aFileName));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ interface imgIRequest;
|
|||
|
||||
[ptr] native nsIntRect(nsIntRect);
|
||||
|
||||
[scriptable, builtinclass, uuid(90b3d21c-317d-4d96-93c0-12add64a26bf)]
|
||||
[scriptable, builtinclass, uuid(ac65c702-7771-4f6d-b18b-1c7d806ce3c1)]
|
||||
interface imgINotificationObserver : nsISupports
|
||||
{
|
||||
const long SIZE_AVAILABLE = 1;
|
||||
|
@ -23,7 +23,8 @@ interface imgINotificationObserver : nsISupports
|
|||
const long LOAD_COMPLETE = 4;
|
||||
const long DECODE_COMPLETE = 5;
|
||||
const long DISCARD = 6;
|
||||
const long IS_ANIMATED = 7;
|
||||
const long UNLOCKED_DRAW = 7;
|
||||
const long IS_ANIMATED = 8;
|
||||
|
||||
[noscript] void notify(in imgIRequest aProxy, in long aType, [const] in nsIntRect aRect);
|
||||
};
|
||||
|
|
|
@ -3107,6 +3107,15 @@ RasterImage::Draw(gfxContext *aContext,
|
|||
DiscardTracker::Reset(&mDiscardTrackerNode);
|
||||
}
|
||||
|
||||
// We would like to just check if we have a zero lock count, but we can't do
|
||||
// that for animated images because in EnsureAnimExists we lock the image and
|
||||
// never unlock so that animated images always have their lock count >= 1. In
|
||||
// that case we use our animation consumers count as a proxy for lock count.
|
||||
if (mLockCount == 0 || (mAnim && mAnimationConsumers == 0)) {
|
||||
if (mStatusTracker)
|
||||
mStatusTracker->GetDecoderObserver()->OnUnlockedDraw();
|
||||
}
|
||||
|
||||
// We use !mDecoded && mHasSourceData to mean discarded.
|
||||
if (!mDecoded && mHasSourceData) {
|
||||
mDrawStartTime = TimeStamp::Now();
|
||||
|
|
|
@ -109,6 +109,11 @@ public:
|
|||
* image will initiate a new series of progressive decode notifications.
|
||||
*/
|
||||
virtual void OnDiscard() = 0;
|
||||
|
||||
/**
|
||||
* Called when we are asked to Draw an image that is not locked.
|
||||
*/
|
||||
virtual void OnUnlockedDraw() = 0;
|
||||
};
|
||||
|
||||
// We must define a destructor because derived classes call our destructor from
|
||||
|
|
|
@ -767,6 +767,17 @@ void imgRequestProxy::OnDiscard()
|
|||
}
|
||||
}
|
||||
|
||||
void imgRequestProxy::OnUnlockedDraw()
|
||||
{
|
||||
LOG_FUNC(GetImgLog(), "imgRequestProxy::OnUnlockedDraw");
|
||||
|
||||
if (mListener && !mCanceled) {
|
||||
// Hold a ref to the listener while we call it, just in case.
|
||||
nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
|
||||
mListener->Notify(this, imgINotificationObserver::UNLOCKED_DRAW, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void imgRequestProxy::OnImageIsAnimated()
|
||||
{
|
||||
LOG_FUNC(GetImgLog(), "imgRequestProxy::OnImageIsAnimated");
|
||||
|
|
|
@ -143,6 +143,7 @@ protected:
|
|||
void OnStopFrame ();
|
||||
void OnStopDecode ();
|
||||
void OnDiscard ();
|
||||
void OnUnlockedDraw ();
|
||||
void OnImageIsAnimated ();
|
||||
|
||||
/* non-virtual sort-of-nsIRequestObserver methods */
|
||||
|
|
|
@ -165,6 +165,18 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
virtual void OnUnlockedDraw()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mTracker->GetImage(),
|
||||
"OnUnlockedDraw callback before we've created our image");
|
||||
mTracker->RecordUnlockedDraw();
|
||||
|
||||
nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
|
||||
while (iter.HasMore()) {
|
||||
mTracker->SendUnlockedDraw(iter.GetNext());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnImageIsAnimated()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mTracker->GetImage(),
|
||||
|
@ -568,6 +580,13 @@ imgStatusTracker::RecordDiscard()
|
|||
mImageStatus &= ~statusBitsToClear;
|
||||
}
|
||||
|
||||
void
|
||||
imgStatusTracker::RecordUnlockedDraw()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mImage,
|
||||
"RecordUnlockedDraw called before we have an Image");
|
||||
}
|
||||
|
||||
void
|
||||
imgStatusTracker::SendImageIsAnimated(imgRequestProxy* aProxy)
|
||||
{
|
||||
|
@ -593,6 +612,13 @@ imgStatusTracker::SendDiscard(imgRequestProxy* aProxy)
|
|||
aProxy->OnDiscard();
|
||||
}
|
||||
|
||||
void
|
||||
imgStatusTracker::SendUnlockedDraw(imgRequestProxy* aProxy)
|
||||
{
|
||||
if (!aProxy->NotificationsDeferred())
|
||||
aProxy->OnUnlockedDraw();
|
||||
}
|
||||
|
||||
void
|
||||
imgStatusTracker::RecordFrameChanged(const nsIntRect* aDirtyRect)
|
||||
{
|
||||
|
|
|
@ -145,6 +145,8 @@ public:
|
|||
void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus);
|
||||
void RecordDiscard();
|
||||
void SendDiscard(imgRequestProxy* aProxy);
|
||||
void RecordUnlockedDraw();
|
||||
void SendUnlockedDraw(imgRequestProxy* aProxy);
|
||||
void RecordImageIsAnimated();
|
||||
void SendImageIsAnimated(imgRequestProxy *aProxy);
|
||||
|
||||
|
|
|
@ -479,10 +479,19 @@ class Vector : private AllocPolicy
|
|||
void replaceRawBuffer(T *p, size_t length);
|
||||
|
||||
/*
|
||||
* Places |val| at position |p|, shifting existing elements
|
||||
* from |p| onward one position higher.
|
||||
* Places |val| at position |p|, shifting existing elements from |p|
|
||||
* onward one position higher. On success, |p| should not be reused
|
||||
* because it will be a dangling pointer if reallocation of the vector
|
||||
* storage occurred; the return value should be used instead. On failure,
|
||||
* NULL is returned.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* if (!(p = vec.insert(p, val)))
|
||||
* <handle failure>
|
||||
* <keep working with p>
|
||||
*/
|
||||
bool insert(T *p, const T &val);
|
||||
T *insert(T *p, const T &val);
|
||||
|
||||
/*
|
||||
* Removes the element |t|, which must fall in the bounds [begin, end),
|
||||
|
@ -863,24 +872,25 @@ Vector<T,N,AP>::internalAppendN(const T &t, size_t needed)
|
|||
}
|
||||
|
||||
template <class T, size_t N, class AP>
|
||||
inline bool
|
||||
inline T *
|
||||
Vector<T,N,AP>::insert(T *p, const T &val)
|
||||
{
|
||||
JS_ASSERT(begin() <= p && p <= end());
|
||||
size_t pos = p - begin();
|
||||
JS_ASSERT(pos <= mLength);
|
||||
size_t oldLength = mLength;
|
||||
if (pos == oldLength)
|
||||
return append(val);
|
||||
{
|
||||
if (pos == oldLength) {
|
||||
if (!append(val))
|
||||
return NULL;
|
||||
} else {
|
||||
T oldBack = back();
|
||||
if (!append(oldBack)) /* Dup the last element. */
|
||||
return false;
|
||||
return NULL;
|
||||
for (size_t i = oldLength; i > pos; --i)
|
||||
(*this)[i] = (*this)[i - 1];
|
||||
(*this)[pos] = val;
|
||||
}
|
||||
for (size_t i = oldLength; i > pos; --i)
|
||||
(*this)[i] = (*this)[i - 1];
|
||||
(*this)[pos] = val;
|
||||
return true;
|
||||
return begin() + pos;
|
||||
}
|
||||
|
||||
template<typename T, size_t N, class AP>
|
||||
|
|
|
@ -46,14 +46,6 @@
|
|||
#include "frontend/SharedContext-inl.h"
|
||||
#include "vm/Shape-inl.h"
|
||||
|
||||
/* Allocation chunk counts, must be powers of two in general. */
|
||||
#define BYTECODE_CHUNK_LENGTH 1024 /* initial bytecode chunk length */
|
||||
#define SRCNOTE_CHUNK_LENGTH 1024 /* initial srcnote chunk length */
|
||||
|
||||
/* Macros to compute byte sizes from typed element counts. */
|
||||
#define BYTECODE_SIZE(n) ((n) * sizeof(jsbytecode))
|
||||
#define SRCNOTE_SIZE(n) ((n) * sizeof(jssrcnote))
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
using namespace js::frontend;
|
||||
|
@ -100,12 +92,16 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, Shared
|
|||
: sc(sc),
|
||||
parent(parent),
|
||||
script(sc->context, script),
|
||||
prolog(sc->context, lineno),
|
||||
main(sc->context, lineno),
|
||||
current(&main),
|
||||
parser(parser),
|
||||
evalCaller(evalCaller),
|
||||
topStmt(NULL),
|
||||
topScopeStmt(NULL),
|
||||
blockChain(sc->context),
|
||||
atomIndices(sc->context),
|
||||
firstLine(lineno),
|
||||
stackDepth(0), maxStackDepth(0),
|
||||
tryNoteList(sc->context),
|
||||
arrayCompDepth(0),
|
||||
|
@ -118,10 +114,6 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, Shared
|
|||
hasGlobalScope(hasGlobalScope),
|
||||
selfHostingMode(selfHostingMode)
|
||||
{
|
||||
memset(&prolog, 0, sizeof prolog);
|
||||
memset(&main, 0, sizeof main);
|
||||
current = &main;
|
||||
firstLine = prolog.currentLine = main.currentLine = lineno;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -132,45 +124,17 @@ BytecodeEmitter::init()
|
|||
|
||||
BytecodeEmitter::~BytecodeEmitter()
|
||||
{
|
||||
js_free(prolog.base);
|
||||
js_free(prolog.notes);
|
||||
js_free(main.base);
|
||||
js_free(main.notes);
|
||||
}
|
||||
|
||||
static ptrdiff_t
|
||||
EmitCheck(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t delta)
|
||||
{
|
||||
jsbytecode *base = bce->base();
|
||||
jsbytecode *newbase;
|
||||
jsbytecode *next = bce->next();
|
||||
jsbytecode *limit = bce->limit();
|
||||
ptrdiff_t offset = next - base;
|
||||
size_t minlength = offset + delta;
|
||||
ptrdiff_t offset = bce->code().length();
|
||||
|
||||
if (next + delta > limit) {
|
||||
size_t newlength;
|
||||
if (!base) {
|
||||
JS_ASSERT(!next && !limit);
|
||||
newlength = BYTECODE_CHUNK_LENGTH;
|
||||
if (newlength < minlength) /* make it bigger if necessary */
|
||||
newlength = RoundUpPow2(minlength);
|
||||
newbase = (jsbytecode *) cx->malloc_(BYTECODE_SIZE(newlength));
|
||||
} else {
|
||||
JS_ASSERT(base <= next && next <= limit);
|
||||
newlength = (limit - base) * 2;
|
||||
if (newlength < minlength) /* make it bigger if necessary */
|
||||
newlength = RoundUpPow2(minlength);
|
||||
newbase = (jsbytecode *) cx->realloc_(base, BYTECODE_SIZE(newlength));
|
||||
}
|
||||
if (!newbase) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return -1;
|
||||
}
|
||||
JS_ASSERT(newlength >= size_t(offset + delta));
|
||||
bce->current->base = newbase;
|
||||
bce->current->limit = newbase + newlength;
|
||||
bce->current->next = newbase + offset;
|
||||
jsbytecode dummy = 0;
|
||||
if (!bce->code().appendN(dummy, delta)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return -1;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
@ -190,7 +154,6 @@ UpdateDepth(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
|
|||
JSOp op = (JSOp) *pc;
|
||||
const JSCodeSpec *cs = &js_CodeSpec[op];
|
||||
|
||||
|
||||
if (cs->format & JOF_TMPSLOT_MASK) {
|
||||
/*
|
||||
* An opcode may temporarily consume stack space during execution.
|
||||
|
@ -239,11 +202,12 @@ ptrdiff_t
|
|||
frontend::Emit1(JSContext *cx, BytecodeEmitter *bce, JSOp op)
|
||||
{
|
||||
ptrdiff_t offset = EmitCheck(cx, bce, 1);
|
||||
if (offset < 0)
|
||||
return -1;
|
||||
|
||||
if (offset >= 0) {
|
||||
*bce->current->next++ = (jsbytecode)op;
|
||||
UpdateDepth(cx, bce, offset);
|
||||
}
|
||||
jsbytecode *code = bce->code(offset);
|
||||
code[0] = jsbytecode(op);
|
||||
UpdateDepth(cx, bce, offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
@ -251,14 +215,13 @@ ptrdiff_t
|
|||
frontend::Emit2(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1)
|
||||
{
|
||||
ptrdiff_t offset = EmitCheck(cx, bce, 2);
|
||||
if (offset < 0)
|
||||
return -1;
|
||||
|
||||
if (offset >= 0) {
|
||||
jsbytecode *next = bce->next();
|
||||
next[0] = (jsbytecode)op;
|
||||
next[1] = op1;
|
||||
bce->current->next = next + 2;
|
||||
UpdateDepth(cx, bce, offset);
|
||||
}
|
||||
jsbytecode *code = bce->code(offset);
|
||||
code[0] = jsbytecode(op);
|
||||
code[1] = op1;
|
||||
UpdateDepth(cx, bce, offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
@ -271,15 +234,14 @@ frontend::Emit3(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1,
|
|||
JS_ASSERT(!IsLocalOp(op));
|
||||
|
||||
ptrdiff_t offset = EmitCheck(cx, bce, 3);
|
||||
if (offset < 0)
|
||||
return -1;
|
||||
|
||||
if (offset >= 0) {
|
||||
jsbytecode *next = bce->next();
|
||||
next[0] = (jsbytecode)op;
|
||||
next[1] = op1;
|
||||
next[2] = op2;
|
||||
bce->current->next = next + 3;
|
||||
UpdateDepth(cx, bce, offset);
|
||||
}
|
||||
jsbytecode *code = bce->code(offset);
|
||||
code[0] = jsbytecode(op);
|
||||
code[1] = op1;
|
||||
code[2] = op2;
|
||||
UpdateDepth(cx, bce, offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
@ -288,20 +250,20 @@ frontend::EmitN(JSContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra)
|
|||
{
|
||||
ptrdiff_t length = 1 + (ptrdiff_t)extra;
|
||||
ptrdiff_t offset = EmitCheck(cx, bce, length);
|
||||
if (offset < 0)
|
||||
return -1;
|
||||
|
||||
if (offset >= 0) {
|
||||
jsbytecode *next = bce->next();
|
||||
*next = (jsbytecode)op;
|
||||
memset(next + 1, 0, BYTECODE_SIZE(extra));
|
||||
bce->current->next = next + length;
|
||||
jsbytecode *code = bce->code(offset);
|
||||
code[0] = jsbytecode(op);
|
||||
/* The remaining |extra| bytes are set by the caller */
|
||||
|
||||
/*
|
||||
* Don't UpdateDepth if op's use-count comes from the immediate
|
||||
* operand yet to be stored in the extra bytes after op.
|
||||
*/
|
||||
if (js_CodeSpec[op].nuses >= 0)
|
||||
UpdateDepth(cx, bce, offset);
|
||||
|
||||
/*
|
||||
* Don't UpdateDepth if op's use-count comes from the immediate
|
||||
* operand yet to be stored in the extra bytes after op.
|
||||
*/
|
||||
if (js_CodeSpec[op].nuses >= 0)
|
||||
UpdateDepth(cx, bce, offset);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
@ -309,14 +271,13 @@ static ptrdiff_t
|
|||
EmitJump(JSContext *cx, BytecodeEmitter *bce, JSOp op, ptrdiff_t off)
|
||||
{
|
||||
ptrdiff_t offset = EmitCheck(cx, bce, 5);
|
||||
if (offset < 0)
|
||||
return -1;
|
||||
|
||||
if (offset >= 0) {
|
||||
jsbytecode *next = bce->next();
|
||||
next[0] = (jsbytecode)op;
|
||||
SET_JUMP_OFFSET(next, off);
|
||||
bce->current->next = next + 5;
|
||||
UpdateDepth(cx, bce, offset);
|
||||
}
|
||||
jsbytecode *code = bce->code(offset);
|
||||
code[0] = jsbytecode(op);
|
||||
SET_JUMP_OFFSET(code, off);
|
||||
UpdateDepth(cx, bce, offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
@ -714,7 +675,7 @@ PopStatementBCE(JSContext *cx, BytecodeEmitter *bce)
|
|||
{
|
||||
StmtInfoBCE *stmt = bce->topStmt;
|
||||
if (!stmt->isTrying() &&
|
||||
(!BackPatch(cx, bce, stmt->breaks, bce->next(), JSOP_GOTO) ||
|
||||
(!BackPatch(cx, bce, stmt->breaks, bce->code().end(), JSOP_GOTO) ||
|
||||
!BackPatch(cx, bce, stmt->continues, bce->code(stmt->update), JSOP_GOTO)))
|
||||
{
|
||||
return false;
|
||||
|
@ -732,10 +693,9 @@ EmitIndex32(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
|
|||
if (offset < 0)
|
||||
return false;
|
||||
|
||||
jsbytecode *next = bce->next();
|
||||
next[0] = jsbytecode(op);
|
||||
SET_UINT32_INDEX(next, index);
|
||||
bce->current->next = next + len;
|
||||
jsbytecode *code = bce->code(offset);
|
||||
code[0] = jsbytecode(op);
|
||||
SET_UINT32_INDEX(code, index);
|
||||
UpdateDepth(cx, bce, offset);
|
||||
CheckTypeSet(cx, bce, op);
|
||||
return true;
|
||||
|
@ -750,10 +710,9 @@ EmitIndexOp(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
|
|||
if (offset < 0)
|
||||
return false;
|
||||
|
||||
jsbytecode *next = bce->next();
|
||||
next[0] = jsbytecode(op);
|
||||
SET_UINT32_INDEX(next, index);
|
||||
bce->current->next = next + len;
|
||||
jsbytecode *code = bce->code(offset);
|
||||
code[0] = jsbytecode(op);
|
||||
SET_UINT32_INDEX(code, index);
|
||||
UpdateDepth(cx, bce, offset);
|
||||
CheckTypeSet(cx, bce, op);
|
||||
return true;
|
||||
|
@ -799,10 +758,9 @@ EmitAtomIncDec(JSContext *cx, JSAtom *atom, JSOp op, BytecodeEmitter *bce)
|
|||
if (offset < 0)
|
||||
return false;
|
||||
|
||||
jsbytecode *next = bce->next();
|
||||
next[0] = jsbytecode(op);
|
||||
SET_UINT32_INDEX(next, index);
|
||||
bce->current->next = next + len;
|
||||
jsbytecode *code = bce->code(offset);
|
||||
code[0] = jsbytecode(op);
|
||||
SET_UINT32_INDEX(code, index);
|
||||
UpdateDepth(cx, bce, offset);
|
||||
CheckTypeSet(cx, bce, op);
|
||||
return true;
|
||||
|
@ -2370,10 +2328,10 @@ EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
unsigned noteCount, noteCountDelta;
|
||||
|
||||
/* Switch note's second offset is to first JSOP_CASE. */
|
||||
noteCount = bce->noteCount();
|
||||
noteCount = bce->notes().length();
|
||||
if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 1, off - top))
|
||||
return false;
|
||||
noteCountDelta = bce->noteCount() - noteCount;
|
||||
noteCountDelta = bce->notes().length() - noteCount;
|
||||
if (noteCountDelta != 0)
|
||||
caseNoteIndex += noteCountDelta;
|
||||
beforeCases = false;
|
||||
|
@ -2506,7 +2464,7 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
|
|||
|
||||
FunctionBox *funbox = bce->sc->asFunctionBox();
|
||||
if (funbox->argumentsHasLocalBinding()) {
|
||||
JS_ASSERT(bce->next() == bce->base()); /* See JSScript::argumentsBytecode. */
|
||||
JS_ASSERT(bce->offset() == 0); /* See JSScript::argumentsBytecode. */
|
||||
bce->switchToProlog();
|
||||
if (Emit1(cx, bce, JSOP_ARGUMENTS) < 0)
|
||||
return false;
|
||||
|
@ -3422,13 +3380,12 @@ EmitNewInit(JSContext *cx, BytecodeEmitter *bce, JSProtoKey key, ParseNode *pn)
|
|||
if (offset < 0)
|
||||
return false;
|
||||
|
||||
jsbytecode *next = bce->next();
|
||||
next[0] = JSOP_NEWINIT;
|
||||
next[1] = jsbytecode(key);
|
||||
next[2] = 0;
|
||||
next[3] = 0;
|
||||
next[4] = 0;
|
||||
bce->current->next = next + len;
|
||||
jsbytecode *code = bce->code(offset);
|
||||
code[0] = JSOP_NEWINIT;
|
||||
code[1] = jsbytecode(key);
|
||||
code[2] = 0;
|
||||
code[3] = 0;
|
||||
code[4] = 0;
|
||||
UpdateDepth(cx, bce, offset);
|
||||
CheckTypeSet(cx, bce, JSOP_NEWINIT);
|
||||
return true;
|
||||
|
@ -3808,7 +3765,7 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
* Fix up the gosubs that might have been emitted before non-local
|
||||
* jumps to the finally code.
|
||||
*/
|
||||
if (!BackPatch(cx, bce, stmtInfo.gosubs(), bce->next(), JSOP_GOSUB))
|
||||
if (!BackPatch(cx, bce, stmtInfo.gosubs(), bce->code().end(), JSOP_GOSUB))
|
||||
return false;
|
||||
|
||||
finallyStart = bce->offset();
|
||||
|
@ -3833,7 +3790,7 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
return false;
|
||||
|
||||
/* Fix up the end-of-try/catch jumps to come here. */
|
||||
if (!BackPatch(cx, bce, catchJump, bce->next(), JSOP_GOTO))
|
||||
if (!BackPatch(cx, bce, catchJump, bce->code().end(), JSOP_GOTO))
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
@ -4653,7 +4610,7 @@ EmitReturn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
if (!EmitNonLocalJumpFixup(cx, bce, NULL))
|
||||
return false;
|
||||
if (top + JSOP_RETURN_LENGTH != bce->offset()) {
|
||||
bce->base()[top] = JSOP_SETRVAL;
|
||||
bce->code()[top] = JSOP_SETRVAL;
|
||||
if (Emit1(cx, bce, JSOP_RETRVAL) < 0)
|
||||
return false;
|
||||
}
|
||||
|
@ -5231,7 +5188,7 @@ EmitObject(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
* ignore setters and to avoid dup'ing and popping the object as each
|
||||
* property is added, as JSOP_SETELEM/JSOP_SETPROP would do.
|
||||
*/
|
||||
ptrdiff_t offset = bce->next() - bce->base();
|
||||
ptrdiff_t offset = bce->offset();
|
||||
if (!EmitNewInit(cx, bce, JSProto_Object, pn))
|
||||
return false;
|
||||
|
||||
|
@ -5905,78 +5862,51 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
}
|
||||
|
||||
static int
|
||||
AllocSrcNote(JSContext *cx, BytecodeEmitter *bce)
|
||||
AllocSrcNote(JSContext *cx, SrcNotesVector ¬es)
|
||||
{
|
||||
jssrcnote *notes = bce->notes();
|
||||
jssrcnote *newnotes;
|
||||
unsigned index = bce->noteCount();
|
||||
unsigned max = bce->noteLimit();
|
||||
|
||||
if (index == max) {
|
||||
size_t newlength;
|
||||
if (!notes) {
|
||||
JS_ASSERT(!index && !max);
|
||||
newlength = SRCNOTE_CHUNK_LENGTH;
|
||||
newnotes = (jssrcnote *) cx->malloc_(SRCNOTE_SIZE(newlength));
|
||||
} else {
|
||||
JS_ASSERT(index <= max);
|
||||
newlength = max * 2;
|
||||
newnotes = (jssrcnote *) cx->realloc_(notes, SRCNOTE_SIZE(newlength));
|
||||
}
|
||||
if (!newnotes) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return -1;
|
||||
}
|
||||
bce->current->notes = newnotes;
|
||||
bce->current->noteLimit = newlength;
|
||||
jssrcnote dummy = 0;
|
||||
if (!notes.append(dummy)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bce->current->noteCount = index + 1;
|
||||
return (int)index;
|
||||
return notes.length() - 1;
|
||||
}
|
||||
|
||||
int
|
||||
frontend::NewSrcNote(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type)
|
||||
{
|
||||
int index, n;
|
||||
jssrcnote *sn;
|
||||
ptrdiff_t offset, delta, xdelta;
|
||||
SrcNotesVector ¬es = bce->notes();
|
||||
int index;
|
||||
|
||||
/*
|
||||
* Claim a note slot in bce->notes() by growing it if necessary and then
|
||||
* incrementing bce->noteCount().
|
||||
*/
|
||||
index = AllocSrcNote(cx, bce);
|
||||
index = AllocSrcNote(cx, notes);
|
||||
if (index < 0)
|
||||
return -1;
|
||||
sn = &bce->notes()[index];
|
||||
|
||||
/*
|
||||
* Compute delta from the last annotated bytecode's offset. If it's too
|
||||
* big to fit in sn, allocate one or more xdelta notes and reset sn.
|
||||
*/
|
||||
offset = bce->offset();
|
||||
delta = offset - bce->lastNoteOffset();
|
||||
ptrdiff_t offset = bce->offset();
|
||||
ptrdiff_t delta = offset - bce->lastNoteOffset();
|
||||
bce->current->lastNoteOffset = offset;
|
||||
if (delta >= SN_DELTA_LIMIT) {
|
||||
do {
|
||||
xdelta = Min(delta, SN_XDELTA_MASK);
|
||||
SN_MAKE_XDELTA(sn, xdelta);
|
||||
ptrdiff_t xdelta = Min(delta, SN_XDELTA_MASK);
|
||||
SN_MAKE_XDELTA(¬es[index], xdelta);
|
||||
delta -= xdelta;
|
||||
index = AllocSrcNote(cx, bce);
|
||||
index = AllocSrcNote(cx, notes);
|
||||
if (index < 0)
|
||||
return -1;
|
||||
sn = &bce->notes()[index];
|
||||
} while (delta >= SN_DELTA_LIMIT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize type and delta, then allocate the minimum number of notes
|
||||
* needed for type's arity. Usually, we won't need more, but if an offset
|
||||
* does take two bytes, SetSrcNoteOffset will grow bce->notes().
|
||||
* does take two bytes, SetSrcNoteOffset will grow notes.
|
||||
*/
|
||||
SN_MAKE_NOTE(sn, type, delta);
|
||||
for (n = (int)js_SrcNoteSpec[type].arity; n > 0; n--) {
|
||||
SN_MAKE_NOTE(¬es[index], type, delta);
|
||||
for (int n = (int)js_SrcNoteSpec[type].arity; n > 0; n--) {
|
||||
if (NewSrcNote(cx, bce, SRC_NULL) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
@ -6012,26 +5942,9 @@ frontend::NewSrcNote3(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptr
|
|||
return index;
|
||||
}
|
||||
|
||||
static bool
|
||||
GrowSrcNotes(JSContext *cx, BytecodeEmitter *bce)
|
||||
{
|
||||
size_t newlength = bce->noteLimit() * 2;
|
||||
jssrcnote *newnotes = (jssrcnote *) cx->realloc_(bce->notes(), newlength);
|
||||
if (!newnotes) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
bce->current->notes = newnotes;
|
||||
bce->current->noteLimit = newlength;
|
||||
return true;
|
||||
}
|
||||
|
||||
jssrcnote *
|
||||
bool
|
||||
frontend::AddToSrcNoteDelta(JSContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta)
|
||||
{
|
||||
ptrdiff_t base, limit, newdelta, diff;
|
||||
int index;
|
||||
|
||||
/*
|
||||
* Called only from FinishTakingSrcNotes to add to main script note
|
||||
* deltas, and only by a small positive amount.
|
||||
|
@ -6039,40 +5952,33 @@ frontend::AddToSrcNoteDelta(JSContext *cx, BytecodeEmitter *bce, jssrcnote *sn,
|
|||
JS_ASSERT(bce->current == &bce->main);
|
||||
JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT);
|
||||
|
||||
base = SN_DELTA(sn);
|
||||
limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT;
|
||||
newdelta = base + delta;
|
||||
ptrdiff_t base = SN_DELTA(sn);
|
||||
ptrdiff_t limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT;
|
||||
ptrdiff_t newdelta = base + delta;
|
||||
if (newdelta < limit) {
|
||||
SN_SET_DELTA(sn, newdelta);
|
||||
} else {
|
||||
index = sn - bce->main.notes;
|
||||
if (bce->main.noteCount == bce->main.noteLimit) {
|
||||
if (!GrowSrcNotes(cx, bce))
|
||||
return NULL;
|
||||
sn = bce->main.notes + index;
|
||||
}
|
||||
diff = bce->main.noteCount - index;
|
||||
bce->main.noteCount++;
|
||||
memmove(sn + 1, sn, SRCNOTE_SIZE(diff));
|
||||
SN_MAKE_XDELTA(sn, delta);
|
||||
sn++;
|
||||
jssrcnote xdelta;
|
||||
SN_MAKE_XDELTA(&xdelta, delta);
|
||||
if (!(sn = bce->main.notes.insert(sn, xdelta)))
|
||||
return false;
|
||||
}
|
||||
return sn;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which, ptrdiff_t offset)
|
||||
SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which,
|
||||
ptrdiff_t offset)
|
||||
{
|
||||
jssrcnote *sn;
|
||||
ptrdiff_t diff;
|
||||
|
||||
if (size_t(offset) > SN_MAX_OFFSET) {
|
||||
ReportStatementTooLarge(cx, bce->topStmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
SrcNotesVector ¬es = bce->notes();
|
||||
|
||||
/* Find the offset numbered which (i.e., skip exactly which offsets). */
|
||||
sn = &bce->notes()[index];
|
||||
jssrcnote *sn = notes.begin() + index;
|
||||
JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
|
||||
JS_ASSERT((int) which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
|
||||
for (sn++; which; sn++, which--) {
|
||||
|
@ -6088,25 +5994,14 @@ SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, unsigned index, unsigned w
|
|||
if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK || (*sn & SN_3BYTE_OFFSET_FLAG)) {
|
||||
/* Maybe this offset was already set to a three-byte value. */
|
||||
if (!(*sn & SN_3BYTE_OFFSET_FLAG)) {
|
||||
/* Losing, need to insert another two bytes for this offset. */
|
||||
index = sn - bce->notes();
|
||||
|
||||
/*
|
||||
* Test to see if the source note array must grow to accommodate
|
||||
* either the first or second byte of additional storage required
|
||||
* by this 3-byte offset.
|
||||
*/
|
||||
if (bce->noteCount() + 1 >= bce->noteLimit()) {
|
||||
if (!GrowSrcNotes(cx, bce))
|
||||
return false;
|
||||
sn = bce->notes() + index;
|
||||
/* Insert two dummy bytes that will be overwritten shortly. */
|
||||
jssrcnote dummy = 0;
|
||||
if (!(sn = notes.insert(sn, dummy)) ||
|
||||
!(sn = notes.insert(sn, dummy)))
|
||||
{
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
bce->current->noteCount += 2;
|
||||
|
||||
diff = bce->noteCount() - (index + 3);
|
||||
JS_ASSERT(diff >= 0);
|
||||
if (diff > 0)
|
||||
memmove(sn + 3, sn + 1, SRCNOTE_SIZE(diff));
|
||||
}
|
||||
*sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16));
|
||||
*sn++ = (jssrcnote)(offset >> 8);
|
||||
|
@ -6156,18 +6051,14 @@ DumpSrcNoteSizeHist()
|
|||
bool
|
||||
frontend::FinishTakingSrcNotes(JSContext *cx, BytecodeEmitter *bce, jssrcnote *notes)
|
||||
{
|
||||
unsigned prologCount, mainCount, totalCount;
|
||||
ptrdiff_t offset, delta;
|
||||
jssrcnote *sn;
|
||||
|
||||
JS_ASSERT(bce->current == &bce->main);
|
||||
|
||||
prologCount = bce->prolog.noteCount;
|
||||
unsigned prologCount = bce->prolog.notes.length();
|
||||
if (prologCount && bce->prolog.currentLine != bce->firstLine) {
|
||||
bce->switchToProlog();
|
||||
if (NewSrcNote2(cx, bce, SRC_SETLINE, (ptrdiff_t)bce->firstLine) < 0)
|
||||
return false;
|
||||
prologCount = bce->prolog.noteCount;
|
||||
prologCount = bce->prolog.notes.length();
|
||||
bce->switchToMain();
|
||||
} else {
|
||||
/*
|
||||
|
@ -6177,14 +6068,14 @@ frontend::FinishTakingSrcNotes(JSContext *cx, BytecodeEmitter *bce, jssrcnote *n
|
|||
* prepending SRC_XDELTA notes to it to account for prolog bytecodes
|
||||
* that came at and after the last annotated bytecode.
|
||||
*/
|
||||
offset = bce->prologOffset() - bce->prolog.lastNoteOffset;
|
||||
ptrdiff_t offset = bce->prologOffset() - bce->prolog.lastNoteOffset;
|
||||
JS_ASSERT(offset >= 0);
|
||||
if (offset > 0 && bce->main.noteCount != 0) {
|
||||
if (offset > 0 && bce->main.notes.length() != 0) {
|
||||
/* NB: Use as much of the first main note's delta as we can. */
|
||||
sn = bce->main.notes;
|
||||
delta = SN_IS_XDELTA(sn)
|
||||
? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
|
||||
: SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
|
||||
jssrcnote *sn = bce->main.notes.begin();
|
||||
ptrdiff_t delta = SN_IS_XDELTA(sn)
|
||||
? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
|
||||
: SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
|
||||
if (offset < delta)
|
||||
delta = offset;
|
||||
for (;;) {
|
||||
|
@ -6194,16 +6085,16 @@ frontend::FinishTakingSrcNotes(JSContext *cx, BytecodeEmitter *bce, jssrcnote *n
|
|||
if (offset == 0)
|
||||
break;
|
||||
delta = Min(offset, SN_XDELTA_MASK);
|
||||
sn = bce->main.notes;
|
||||
sn = bce->main.notes.begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mainCount = bce->main.noteCount;
|
||||
totalCount = prologCount + mainCount;
|
||||
unsigned mainCount = bce->main.notes.length();
|
||||
unsigned totalCount = prologCount + mainCount;
|
||||
if (prologCount)
|
||||
PodCopy(notes, bce->prolog.notes, prologCount);
|
||||
PodCopy(notes + prologCount, bce->main.notes, mainCount);
|
||||
PodCopy(notes, bce->prolog.notes.begin(), prologCount);
|
||||
PodCopy(notes + prologCount, bce->main.notes.begin(), mainCount);
|
||||
SN_MAKE_TERMINATOR(¬es[totalCount]);
|
||||
|
||||
return true;
|
||||
|
|
|
@ -59,6 +59,11 @@ class CGConstList {
|
|||
|
||||
struct StmtInfoBCE;
|
||||
|
||||
// Use zero inline elements because these go on the stack and affect how many
|
||||
// nested functions are possible.
|
||||
typedef Vector<jsbytecode, 0> BytecodeVector;
|
||||
typedef Vector<jssrcnote, 0> SrcNotesVector;
|
||||
|
||||
struct BytecodeEmitter
|
||||
{
|
||||
typedef StmtInfoBCE StmtInfo;
|
||||
|
@ -69,18 +74,24 @@ struct BytecodeEmitter
|
|||
|
||||
Rooted<JSScript*> script; /* the JSScript we're ultimately producing */
|
||||
|
||||
struct {
|
||||
jsbytecode *base; /* base of JS bytecode vector */
|
||||
jsbytecode *limit; /* one byte beyond end of bytecode */
|
||||
jsbytecode *next; /* pointer to next free bytecode */
|
||||
jssrcnote *notes; /* source notes, see below */
|
||||
unsigned noteCount; /* number of source notes so far */
|
||||
unsigned noteLimit; /* limit number for source notes in notePool */
|
||||
struct EmitSection {
|
||||
BytecodeVector code; /* bytecode */
|
||||
SrcNotesVector notes; /* source notes, see below */
|
||||
ptrdiff_t lastNoteOffset; /* code offset for last source note */
|
||||
unsigned currentLine; /* line number for tree-based srcnote gen */
|
||||
unsigned lastColumn; /* zero-based column index on currentLine of
|
||||
last SRC_COLSPAN-annotated opcode */
|
||||
} prolog, main, *current;
|
||||
|
||||
EmitSection(JSContext *cx, unsigned lineno)
|
||||
: code(cx), notes(cx), lastNoteOffset(0), currentLine(lineno), lastColumn(0)
|
||||
{
|
||||
// Start them off moderately large, to avoid repeated resizings
|
||||
// early on.
|
||||
code.reserve(1024);
|
||||
notes.reserve(1024);
|
||||
}
|
||||
};
|
||||
EmitSection prolog, main, *current;
|
||||
|
||||
Parser *const parser; /* the parser */
|
||||
|
||||
|
@ -126,17 +137,17 @@ struct BytecodeEmitter
|
|||
don't ever get emitted. See the comment for
|
||||
the field |selfHostingMode| in Parser.h for details. */
|
||||
|
||||
/*
|
||||
* Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
|
||||
* space above their tempMark points. This means that you cannot alloc from
|
||||
* tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
|
||||
* destruction.
|
||||
*/
|
||||
BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, SharedContext *sc,
|
||||
HandleScript script, HandleScript evalCaller, bool hasGlobalScope,
|
||||
unsigned lineno, bool selfHostingMode = false);
|
||||
bool init();
|
||||
|
||||
/*
|
||||
* Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
|
||||
* space above their tempMark points. This means that you cannot alloc from
|
||||
* tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
|
||||
* destructor call.
|
||||
*/
|
||||
~BytecodeEmitter();
|
||||
|
||||
bool isAliasedName(ParseNode *pn);
|
||||
|
@ -166,19 +177,14 @@ struct BytecodeEmitter
|
|||
|
||||
TokenStream *tokenStream() { return &parser->tokenStream; }
|
||||
|
||||
jsbytecode *base() const { return current->base; }
|
||||
jsbytecode *limit() const { return current->limit; }
|
||||
jsbytecode *next() const { return current->next; }
|
||||
jsbytecode *code(ptrdiff_t offset) const { return base() + offset; }
|
||||
ptrdiff_t offset() const { return next() - base(); }
|
||||
jsbytecode *prologBase() const { return prolog.base; }
|
||||
ptrdiff_t prologOffset() const { return prolog.next - prolog.base; }
|
||||
BytecodeVector &code() const { return current->code; }
|
||||
jsbytecode *code(ptrdiff_t offset) const { return current->code.begin() + offset; }
|
||||
ptrdiff_t offset() const { return current->code.end() - current->code.begin(); }
|
||||
ptrdiff_t prologOffset() const { return prolog.code.end() - prolog.code.begin(); }
|
||||
void switchToMain() { current = &main; }
|
||||
void switchToProlog() { current = &prolog; }
|
||||
|
||||
jssrcnote *notes() const { return current->notes; }
|
||||
unsigned noteCount() const { return current->noteCount; }
|
||||
unsigned noteLimit() const { return current->noteLimit; }
|
||||
SrcNotesVector ¬es() const { return current->notes; }
|
||||
ptrdiff_t lastNoteOffset() const { return current->lastNoteOffset; }
|
||||
unsigned currentLine() const { return current->currentLine; }
|
||||
unsigned lastColumn() const { return current->lastColumn; }
|
||||
|
@ -379,10 +385,8 @@ int
|
|||
NewSrcNote3(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset1,
|
||||
ptrdiff_t offset2);
|
||||
|
||||
/*
|
||||
* NB: this function can add at most one extra extended delta note.
|
||||
*/
|
||||
jssrcnote *
|
||||
/* NB: this function can add at most one extra extended delta note. */
|
||||
bool
|
||||
AddToSrcNoteDelta(JSContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta);
|
||||
|
||||
bool
|
||||
|
@ -402,14 +406,14 @@ inline ptrdiff_t
|
|||
BytecodeEmitter::countFinalSourceNotes()
|
||||
{
|
||||
ptrdiff_t diff = prologOffset() - prolog.lastNoteOffset;
|
||||
ptrdiff_t cnt = prolog.noteCount + main.noteCount + 1;
|
||||
if (prolog.noteCount && prolog.currentLine != firstLine) {
|
||||
ptrdiff_t cnt = prolog.notes.length() + main.notes.length() + 1;
|
||||
if (prolog.notes.length() && prolog.currentLine != firstLine) {
|
||||
if (diff > SN_DELTA_MASK)
|
||||
cnt += JS_HOWMANY(diff - SN_DELTA_MASK, SN_XDELTA_MASK);
|
||||
cnt += 2 + ((firstLine > SN_3BYTE_OFFSET_MASK) << 1);
|
||||
} else if (diff > 0) {
|
||||
if (main.noteCount) {
|
||||
jssrcnote *sn = main.notes;
|
||||
if (main.notes.length()) {
|
||||
jssrcnote *sn = main.notes.begin();
|
||||
diff -= SN_IS_XDELTA(sn)
|
||||
? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
|
||||
: SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
|
||||
|
|
|
@ -1874,8 +1874,8 @@ JSScript::fullyInitFromEmitter(JSContext *cx, Handle<JSScript*> script, Bytecode
|
|||
return false;
|
||||
|
||||
jsbytecode *code = ssd->data;
|
||||
PodCopy<jsbytecode>(code, bce->prologBase(), prologLength);
|
||||
PodCopy<jsbytecode>(code + prologLength, bce->base(), mainLength);
|
||||
PodCopy<jsbytecode>(code, bce->prolog.code.begin(), prologLength);
|
||||
PodCopy<jsbytecode>(code + prologLength, bce->code().begin(), mainLength);
|
||||
if (!FinishTakingSrcNotes(cx, bce, (jssrcnote *)(code + script->length)))
|
||||
return false;
|
||||
InitAtomMap(cx, bce->atomIndices.getMap(), ssd->atoms(script->length, nsrcnotes));
|
||||
|
|
|
@ -125,6 +125,7 @@ public:
|
|||
PAINTING,
|
||||
EVENT_DELIVERY,
|
||||
PLUGIN_GEOMETRY,
|
||||
IMAGE_VISIBILITY,
|
||||
OTHER
|
||||
};
|
||||
nsDisplayListBuilder(nsIFrame* aReferenceFrame, Mode aMode, bool aBuildCaret);
|
||||
|
@ -158,6 +159,11 @@ public:
|
|||
* @return true if the display list is being built for painting.
|
||||
*/
|
||||
bool IsForPainting() { return mMode == PAINTING; }
|
||||
/**
|
||||
* @return true if the display list is being built for determining image
|
||||
* visibility.
|
||||
*/
|
||||
bool IsForImageVisibility() { return mMode == IMAGE_VISIBILITY; }
|
||||
bool WillComputePluginGeometry() { return mWillComputePluginGeometry; }
|
||||
/**
|
||||
* @return true if "painting is suppressed" during page load and we
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "nsInterfaceHashtable.h"
|
||||
#include "nsEventStates.h"
|
||||
#include "nsPresArena.h"
|
||||
#include "nsIImageLoadingContent.h"
|
||||
|
||||
class nsIContent;
|
||||
class nsIDocument;
|
||||
|
@ -120,10 +121,10 @@ typedef struct CapturingContentInfo {
|
|||
nsIContent* mContent;
|
||||
} CapturingContentInfo;
|
||||
|
||||
// da75e297-2c23-43c4-8d7f-96a668e96ba9
|
||||
// 835b3946-1a4f-4132-b3ce-2e2e8be377c8
|
||||
#define NS_IPRESSHELL_IID \
|
||||
{ 0xda75e297, 0x2c23, 0x43c4, \
|
||||
{ 0x8d, 0x7f, 0x96, 0xa6, 0x68, 0xe9, 0x6b, 0xa9 } }
|
||||
{ 0x835b3946, 0x1a4f, 0x4132, \
|
||||
{ 0xb3, 0xce, 0x2e, 0x2e, 0x8b, 0xe3, 0x77, 0xc8 } }
|
||||
|
||||
// debug VerifyReflow flags
|
||||
#define VERIFY_REFLOW_ON 0x01
|
||||
|
@ -1320,6 +1321,16 @@ public:
|
|||
|
||||
void InvalidatePresShellIfHidden();
|
||||
|
||||
// Schedule an update of the list of visible images.
|
||||
virtual void ScheduleImageVisibilityUpdate() = 0;
|
||||
|
||||
// Clears the current list of visible images on this presshell and replaces it
|
||||
// with images that are in the display list aList.
|
||||
virtual void RebuildImageVisibility(const nsDisplayList& aList) = 0;
|
||||
|
||||
// Ensures the image is in the list of visible images.
|
||||
virtual void EnsureImageInVisibleList(nsIImageLoadingContent* aImage) = 0;
|
||||
|
||||
/**
|
||||
* Refresh observer management.
|
||||
*/
|
||||
|
|
|
@ -182,6 +182,7 @@
|
|||
#include "nsTransitionManager.h"
|
||||
#include "LayerTreeInvalidation.h"
|
||||
#include "nsAsyncDOMEvent.h"
|
||||
#include "nsIImageLoadingContent.h"
|
||||
|
||||
#define ANCHOR_SCROLL_FLAGS \
|
||||
(nsIPresShell::SCROLL_OVERFLOW_HIDDEN | nsIPresShell::SCROLL_NO_PARENT_FRAMES)
|
||||
|
@ -1001,6 +1002,10 @@ PresShell::Destroy()
|
|||
|
||||
mSynthMouseMoveEvent.Revoke();
|
||||
|
||||
mUpdateImageVisibilityEvent.Revoke();
|
||||
|
||||
ClearVisibleImagesList();
|
||||
|
||||
if (mCaret) {
|
||||
mCaret->Terminate();
|
||||
mCaret = nullptr;
|
||||
|
@ -3613,8 +3618,10 @@ PresShell::UnsuppressAndInvalidate()
|
|||
if (win)
|
||||
win->SetReadyForFocus();
|
||||
|
||||
if (!mHaveShutDown)
|
||||
if (!mHaveShutDown) {
|
||||
SynthesizeMouseMove(false);
|
||||
ScheduleImageVisibilityUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -5278,6 +5285,190 @@ PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll)
|
|||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PresShell::MarkImagesInListVisible(const nsDisplayList& aList)
|
||||
{
|
||||
for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
|
||||
nsDisplayList* sublist = item->GetChildren();
|
||||
if (sublist) {
|
||||
MarkImagesInListVisible(*sublist);
|
||||
continue;
|
||||
}
|
||||
nsIFrame* f = item->GetUnderlyingFrame();
|
||||
// We could check the type of the display item, only a handful can hold an
|
||||
// image loading content.
|
||||
if (f) {
|
||||
// dont bother nscomptr here, it is wasteful
|
||||
nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(f->GetContent()));
|
||||
if (content) {
|
||||
// use the presshell containing the image
|
||||
PresShell* presShell = static_cast<PresShell*>(f->PresContext()->PresShell());
|
||||
if (presShell) {
|
||||
content->IncrementVisibleCount();
|
||||
presShell->mVisibleImages.AppendElement(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::RebuildImageVisibility(const nsDisplayList& aList)
|
||||
{
|
||||
MOZ_ASSERT(!mImageVisibilityVisited, "already visited?");
|
||||
mImageVisibilityVisited = true;
|
||||
nsTArray< nsCOMPtr<nsIImageLoadingContent > > beforeimagelist;
|
||||
beforeimagelist.SwapElements(mVisibleImages);
|
||||
MarkImagesInListVisible(aList);
|
||||
for (uint32_t i = 0; i < beforeimagelist.Length(); ++i) {
|
||||
beforeimagelist[i]->DecrementVisibleCount();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PresShell::ClearImageVisibilityVisited(nsView* aView, bool aClear)
|
||||
{
|
||||
nsViewManager* vm = aView->GetViewManager();
|
||||
if (aClear) {
|
||||
PresShell* presShell = static_cast<PresShell*>(vm->GetPresShell());
|
||||
if (!presShell->mImageVisibilityVisited) {
|
||||
presShell->ClearVisibleImagesList();
|
||||
}
|
||||
presShell->mImageVisibilityVisited = false;
|
||||
}
|
||||
for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
|
||||
ClearImageVisibilityVisited(v, v->GetViewManager() != vm);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::ClearVisibleImagesList()
|
||||
{
|
||||
for (uint32_t i = 0; i < mVisibleImages.Length(); ++i) {
|
||||
mVisibleImages[i]->DecrementVisibleCount();
|
||||
}
|
||||
mVisibleImages.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::UpdateImageVisibility()
|
||||
{
|
||||
MOZ_ASSERT(!mPresContext || mPresContext->IsRootContentDocument(),
|
||||
"updating image visibility on a non-root content document?");
|
||||
|
||||
mUpdateImageVisibilityEvent.Revoke();
|
||||
|
||||
if (mHaveShutDown || mIsDestroying) {
|
||||
return;
|
||||
}
|
||||
|
||||
// call update on that frame
|
||||
nsIFrame* rootFrame = GetRootFrame();
|
||||
if (!rootFrame) {
|
||||
ClearVisibleImagesList();
|
||||
return;
|
||||
}
|
||||
|
||||
// We could walk the frame tree directly and skip creating a display list for
|
||||
// better perf.
|
||||
nsRect updateRect(nsPoint(0, 0), rootFrame->GetSize());
|
||||
nsDisplayListBuilder builder(rootFrame, nsDisplayListBuilder::IMAGE_VISIBILITY, true);
|
||||
builder.IgnorePaintSuppression();
|
||||
builder.EnterPresShell(rootFrame, updateRect);
|
||||
nsDisplayList list;
|
||||
rootFrame->BuildDisplayListForStackingContext(&builder, updateRect, &list);
|
||||
builder.LeavePresShell(rootFrame, updateRect);
|
||||
|
||||
RebuildImageVisibility(list);
|
||||
|
||||
ClearImageVisibilityVisited(rootFrame->GetView(), true);
|
||||
|
||||
list.DeleteAll();
|
||||
}
|
||||
|
||||
static bool
|
||||
AssumeAllImagesVisible(nsPresContext* aPresContext, nsIDocument* aDocument)
|
||||
{
|
||||
static bool sImageVisibilityEnabled = true;
|
||||
static bool sImageVisibilityPrefCached = false;
|
||||
|
||||
if (!sImageVisibilityPrefCached) {
|
||||
Preferences::AddBoolVarCache(&sImageVisibilityEnabled,
|
||||
"layout.imagevisibility.enabled", true);
|
||||
sImageVisibilityPrefCached = true;
|
||||
}
|
||||
|
||||
|
||||
if (!sImageVisibilityEnabled || !aPresContext || !aDocument)
|
||||
return true;
|
||||
|
||||
// We assume all images are visible in print, print preview, chrome, xul, and
|
||||
// resource docs and don't keep track of them.
|
||||
if (aPresContext->Type() == nsPresContext::eContext_PrintPreview ||
|
||||
aPresContext->Type() == nsPresContext::eContext_Print ||
|
||||
aPresContext->IsChrome() ||
|
||||
aDocument->IsResourceDoc() ||
|
||||
aDocument->IsXUL()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::ScheduleImageVisibilityUpdate()
|
||||
{
|
||||
if (AssumeAllImagesVisible(mPresContext, mDocument))
|
||||
return;
|
||||
|
||||
if (!mPresContext->IsRootContentDocument()) {
|
||||
nsPresContext* presContext = mPresContext->GetToplevelContentDocumentPresContext();
|
||||
if (!presContext)
|
||||
return;
|
||||
MOZ_ASSERT(presContext->IsRootContentDocument(),
|
||||
"Didn't get a root prescontext from GetToplevelContentDocumentPresContext?");
|
||||
presContext->PresShell()->ScheduleImageVisibilityUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mHaveShutDown || mIsDestroying)
|
||||
return;
|
||||
|
||||
if (mUpdateImageVisibilityEvent.IsPending())
|
||||
return;
|
||||
|
||||
nsRefPtr<nsRunnableMethod<PresShell> > ev =
|
||||
NS_NewRunnableMethod(this, &PresShell::UpdateImageVisibility);
|
||||
if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
|
||||
mUpdateImageVisibilityEvent = ev;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::EnsureImageInVisibleList(nsIImageLoadingContent* aImage)
|
||||
{
|
||||
if (AssumeAllImagesVisible(mPresContext, mDocument)) {
|
||||
aImage->IncrementVisibleCount();
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// if it has a frame make sure its in this presshell
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aImage);
|
||||
if (content) {
|
||||
PresShell* shell = static_cast<PresShell*>(content->OwnerDoc()->GetShell());
|
||||
MOZ_ASSERT(!shell || shell == this, "wrong shell");
|
||||
}
|
||||
#endif
|
||||
|
||||
// nsImageLoadingContent doesn't call this function if it has a positive
|
||||
// visible count so the image shouldn't be in mVisibleImages. Either way it
|
||||
// doesn't hurt to put it in multiple times.
|
||||
MOZ_ASSERT(!mVisibleImages.Contains(aImage), "image already in the array");
|
||||
mVisibleImages.AppendElement(aImage);
|
||||
aImage->IncrementVisibleCount();
|
||||
}
|
||||
|
||||
class nsAutoNotifyDidPaint
|
||||
{
|
||||
public:
|
||||
|
@ -5304,6 +5495,8 @@ PresShell::Paint(nsView* aViewToPaint,
|
|||
NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
|
||||
NS_ASSERTION(aViewToPaint, "null view");
|
||||
|
||||
MOZ_ASSERT(!mImageVisibilityVisited, "should have been cleared");
|
||||
|
||||
if (!mIsActive || mIsZombie) {
|
||||
return;
|
||||
}
|
||||
|
@ -7281,6 +7474,8 @@ FreezeSubDocument(nsIDocument *aDocument, void *aData)
|
|||
void
|
||||
PresShell::Freeze()
|
||||
{
|
||||
mUpdateImageVisibilityEvent.Revoke();
|
||||
|
||||
MaybeReleaseCapturingContent();
|
||||
|
||||
mDocument->EnumerateFreezableElements(FreezeElement, nullptr);
|
||||
|
|
|
@ -335,6 +335,12 @@ public:
|
|||
uint32_t mContentToScrollToFlags;
|
||||
};
|
||||
|
||||
virtual void ScheduleImageVisibilityUpdate();
|
||||
|
||||
virtual void RebuildImageVisibility(const nsDisplayList& aList);
|
||||
|
||||
virtual void EnsureImageInVisibleList(nsIImageLoadingContent* aImage);
|
||||
|
||||
protected:
|
||||
virtual ~PresShell();
|
||||
|
||||
|
@ -703,6 +709,17 @@ protected:
|
|||
virtual void ThemeChanged() { mPresContext->ThemeChanged(); }
|
||||
virtual void BackingScaleFactorChanged() { mPresContext->UIResolutionChanged(); }
|
||||
|
||||
void UpdateImageVisibility();
|
||||
|
||||
nsRevocableEventPtr<nsRunnableMethod<PresShell> > mUpdateImageVisibilityEvent;
|
||||
|
||||
void ClearVisibleImagesList();
|
||||
static void ClearImageVisibilityVisited(nsView* aView, bool aClear);
|
||||
static void MarkImagesInListVisible(const nsDisplayList& aList);
|
||||
|
||||
// A list of images that are visible or almost visible.
|
||||
nsTArray< nsCOMPtr<nsIImageLoadingContent > > mVisibleImages;
|
||||
|
||||
#ifdef DEBUG
|
||||
// The reflow root under which we're currently reflowing. Null when
|
||||
// not in reflow.
|
||||
|
@ -792,6 +809,8 @@ protected:
|
|||
bool mAsyncResizeTimerIsActive : 1;
|
||||
bool mInResize : 1;
|
||||
|
||||
bool mImageVisibilityVisited : 1;
|
||||
|
||||
static bool sDisableNonTestMouseEvents;
|
||||
};
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ load 321224.xul
|
|||
load 322780-1.xul
|
||||
load 323381-1.html
|
||||
load 323381-2.html
|
||||
asserts-if(gtk2Widget,13-14) load 323386-1.html # Bug 575011
|
||||
asserts-if(gtk2Widget,13-15) load 323386-1.html # Bug 575011
|
||||
load 323389-1.html
|
||||
load 323389-2.html
|
||||
load 323493-1.html
|
||||
|
@ -296,8 +296,8 @@ load 465651-1.html
|
|||
load 467137-1.html
|
||||
load 467213-1.html
|
||||
load 467487-1.html
|
||||
asserts(6-9) load 467493-1.html
|
||||
asserts(4-8) load 467493-2.html
|
||||
asserts(6-10) load 467493-1.html
|
||||
asserts(4-9) load 467493-2.html
|
||||
load 467875-1.xhtml
|
||||
load 467914-1.html
|
||||
load 468207-1.html
|
||||
|
|
|
@ -54,6 +54,8 @@
|
|||
#include "nsRefreshDriver.h"
|
||||
#include "nsContentList.h"
|
||||
#include <algorithm>
|
||||
#include <cstdlib> // for std::abs(int/long)
|
||||
#include <cmath> // for std::abs(float/double)
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -1427,6 +1429,7 @@ nsGfxScrollFrameInner::nsGfxScrollFrameInner(nsContainerFrame* aOuter,
|
|||
, mRestorePos(-1, -1)
|
||||
, mLastPos(-1, -1)
|
||||
, mScrollPosForLayerPixelAlignment(-1, -1)
|
||||
, mLastUpdateImagesPos(-1, -1)
|
||||
, mNeverHasVerticalScrollbar(false)
|
||||
, mNeverHasHorizontalScrollbar(false)
|
||||
, mHasVerticalScrollbar(false)
|
||||
|
@ -1880,6 +1883,35 @@ nsGfxScrollFrameInner::ScrollToImpl(nsPoint aPt, const nsRect& aRange)
|
|||
return;
|
||||
}
|
||||
|
||||
bool needImageVisibilityUpdate = (mLastUpdateImagesPos == nsPoint(-1,-1));
|
||||
|
||||
static bool sImageVisPrefsCached = false;
|
||||
// The fraction of the scrollport we allow to scroll by before we schedule
|
||||
// an update of image visibility.
|
||||
static int32_t sHorzScrollFraction = 2;
|
||||
static int32_t sVertScrollFraction = 2;
|
||||
if (!sImageVisPrefsCached) {
|
||||
Preferences::AddIntVarCache(&sHorzScrollFraction,
|
||||
"layout.imagevisibility.amountscrollbeforeupdatehorizontal", 2);
|
||||
Preferences::AddIntVarCache(&sVertScrollFraction,
|
||||
"layout.imagevisibility.amountscrollbeforeupdatevertical", 2);
|
||||
sImageVisPrefsCached = true;
|
||||
}
|
||||
|
||||
nsPoint dist(std::abs(pt.x - mLastUpdateImagesPos.x),
|
||||
std::abs(pt.y - mLastUpdateImagesPos.y));
|
||||
nscoord horzAllowance = std::max(mScrollPort.width / std::max(sHorzScrollFraction, 1),
|
||||
nsPresContext::AppUnitsPerCSSPixel());
|
||||
nscoord vertAllowance = std::max(mScrollPort.height / std::max(sVertScrollFraction, 1),
|
||||
nsPresContext::AppUnitsPerCSSPixel());
|
||||
if (dist.x >= horzAllowance || dist.y >= vertAllowance) {
|
||||
needImageVisibilityUpdate = true;
|
||||
}
|
||||
|
||||
if (needImageVisibilityUpdate) {
|
||||
presContext->PresShell()->ScheduleImageVisibilityUpdate();
|
||||
}
|
||||
|
||||
// notify the listeners.
|
||||
for (uint32_t i = 0; i < mListeners.Length(); i++) {
|
||||
mListeners[i]->ScrollPositionWillChange(pt.x, pt.y);
|
||||
|
@ -1995,6 +2027,10 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
const nsRect& aDirtyRect,
|
||||
const nsDisplayListSet& aLists)
|
||||
{
|
||||
if (aBuilder->IsForImageVisibility()) {
|
||||
mLastUpdateImagesPos = GetScrollPosition();
|
||||
}
|
||||
|
||||
mOuter->DisplayBorderBackgroundOutline(aBuilder, aLists);
|
||||
|
||||
if (aBuilder->IsPaintingToWindow()) {
|
||||
|
@ -2061,6 +2097,33 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
dirtyRect = displayPort;
|
||||
}
|
||||
|
||||
if (aBuilder->IsForImageVisibility()) {
|
||||
static bool sImageVisPrefsCached = false;
|
||||
// The number of scrollports wide/high to expand when looking for images.
|
||||
static uint32_t sHorzExpandScrollPort = 0;
|
||||
static uint32_t sVertExpandScrollPort = 1;
|
||||
if (!sImageVisPrefsCached) {
|
||||
Preferences::AddUintVarCache(&sHorzExpandScrollPort,
|
||||
"layout.imagevisibility.numscrollportwidths", (uint32_t)0);
|
||||
Preferences::AddUintVarCache(&sVertExpandScrollPort,
|
||||
"layout.imagevisibility.numscrollportheights", 1);
|
||||
sImageVisPrefsCached = true;
|
||||
}
|
||||
|
||||
// We expand the dirty rect to catch images just outside of the scroll port.
|
||||
// We could be smarter and not expand the dirty rect in a direction in which
|
||||
// we are not able to scroll.
|
||||
nsRect dirtyRectBefore = dirtyRect;
|
||||
|
||||
nsPoint vertShift = nsPoint(0, sVertExpandScrollPort * dirtyRectBefore.height);
|
||||
dirtyRect = dirtyRect.Union(dirtyRectBefore - vertShift);
|
||||
dirtyRect = dirtyRect.Union(dirtyRectBefore + vertShift);
|
||||
|
||||
nsPoint horzShift = nsPoint(sHorzExpandScrollPort * dirtyRectBefore.width, 0);
|
||||
dirtyRect = dirtyRect.Union(dirtyRectBefore - horzShift);
|
||||
dirtyRect = dirtyRect.Union(dirtyRectBefore + horzShift);
|
||||
}
|
||||
|
||||
nsDisplayListCollection set;
|
||||
mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, dirtyRect, set);
|
||||
|
||||
|
|
|
@ -299,6 +299,9 @@ public:
|
|||
nsCOMPtr<nsITimer> mScrollActivityTimer;
|
||||
nsPoint mScrollPosForLayerPixelAlignment;
|
||||
|
||||
// The scroll position where we last updated image visibility.
|
||||
nsPoint mLastUpdateImagesPos;
|
||||
|
||||
bool mNeverHasVerticalScrollbar:1;
|
||||
bool mNeverHasHorizontalScrollbar:1;
|
||||
bool mHasVerticalScrollbar:1;
|
||||
|
|
|
@ -465,10 +465,15 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
childItems.AppendToTop(item);
|
||||
}
|
||||
|
||||
if (mIsInline) {
|
||||
WrapReplacedContentForBorderRadius(aBuilder, &childItems, aLists);
|
||||
if (aBuilder->IsForImageVisibility()) {
|
||||
// We don't add the childItems to the return list as we're dealing with them here.
|
||||
presShell->RebuildImageVisibility(childItems);
|
||||
} else {
|
||||
aLists.Content()->AppendToTop(&childItems);
|
||||
if (mIsInline) {
|
||||
WrapReplacedContentForBorderRadius(aBuilder, &childItems, aLists);
|
||||
} else {
|
||||
aLists.Content()->AppendToTop(&childItems);
|
||||
}
|
||||
}
|
||||
|
||||
// delete childItems in case of OOM
|
||||
|
|
|
@ -1568,7 +1568,7 @@ skip-if(B2G) fails-if(Android) random-if(layersGPUAccelerated) fails-if(/^Window
|
|||
== 582037-1b.html 582037-1-ref.html
|
||||
skip-if(B2G) == 582037-2a.html 582037-2-ref.html
|
||||
skip-if(B2G) == 582037-2b.html 582037-2-ref.html
|
||||
asserts(0-12) == 582146-1.html about:blank
|
||||
asserts(0-13) == 582146-1.html about:blank
|
||||
skip-if(B2G) == 582476-1.svg 582476-1-ref.svg
|
||||
== 584400-dash-length.svg 584400-dash-length-ref.svg
|
||||
== 584699-1.html 584699-1-ref.html
|
||||
|
|
|
@ -88,6 +88,7 @@ SVGFEImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
|
|||
|
||||
if (imageLoader) {
|
||||
imageLoader->FrameDestroyed(this);
|
||||
imageLoader->DecrementVisibleCount();
|
||||
}
|
||||
|
||||
SVGFEImageFrameBase::DestroyFrom(aDestructRoot);
|
||||
|
@ -108,6 +109,8 @@ SVGFEImageFrame::Init(nsIContent* aContent,
|
|||
|
||||
if (imageLoader) {
|
||||
imageLoader->FrameCreated(this);
|
||||
// We assume that feImage's are always visible.
|
||||
imageLoader->IncrementVisibleCount();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -153,7 +153,11 @@ static int nr_stun_server_get_password(void *arg, nr_stun_message *msg, Data **p
|
|||
ABORT(R_NOT_FOUND);
|
||||
}
|
||||
|
||||
r_log(NR_LOG_STUN,LOG_NOTICE,"STUN-SERVER(%s): Unable to find password for unknown user: %s",ctx->label,username_attribute->u.username);
|
||||
/* Although this is an exceptional condition, we'll already have seen a
|
||||
* NOTICE-level log message about the unknown user, so additional log
|
||||
* messages at any level higher than DEBUG are unnecessary. */
|
||||
|
||||
r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s): Unable to find password for unknown user: %s",ctx->label,username_attribute->u.username);
|
||||
ABORT(R_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,12 @@
|
|||
|
||||
#include "android/log.h"
|
||||
|
||||
#if !defined(MOZ_ANDROID_FROYO)
|
||||
#define DEFAULT_STAGEFRIGHT_FLAGS OMXCodec::kClientNeedsFramebuffer
|
||||
#else
|
||||
#define DEFAULT_STAGEFRIGHT_FLAGS 0
|
||||
#endif
|
||||
|
||||
#undef LOG
|
||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "OmxPlugin" , ## args)
|
||||
|
||||
|
@ -285,6 +291,9 @@ static uint32_t GetVideoCreationFlags(PluginHost* aPluginHost)
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
flags |= DEFAULT_STAGEFRIGHT_FLAGS;
|
||||
|
||||
return static_cast<uint32_t>(flags);
|
||||
#endif
|
||||
}
|
||||
|
@ -294,10 +303,10 @@ static sp<MediaSource> CreateVideoSource(PluginHost* aPluginHost,
|
|||
const sp<MediaSource>& aVideoTrack)
|
||||
{
|
||||
uint32_t flags = GetVideoCreationFlags(aPluginHost);
|
||||
if (flags == 0) {
|
||||
if (flags == DEFAULT_STAGEFRIGHT_FLAGS) {
|
||||
// Let Stagefright choose hardware or software decoder.
|
||||
sp<MediaSource> videoSource = OMXCodec::Create(aOmx, aVideoTrack->getFormat(),
|
||||
false, aVideoTrack, NULL, 0);
|
||||
false, aVideoTrack, NULL, flags);
|
||||
if (videoSource == NULL)
|
||||
return NULL;
|
||||
|
||||
|
@ -337,13 +346,13 @@ static sp<MediaSource> CreateVideoSource(PluginHost* aPluginHost,
|
|||
LOG("Falling back to software decoder");
|
||||
videoSource.clear();
|
||||
#if defined(MOZ_ANDROID_V2_X_X)
|
||||
flags = OMXCodec::kPreferSoftwareCodecs;
|
||||
flags = DEFAULT_STAGEFRIGHT_FLAGS | OMXCodec::kPreferSoftwareCodecs;
|
||||
#else
|
||||
flags = OMXCodec::kSoftwareCodecsOnly;
|
||||
flags = DEFAULT_STAGEFRIGHT_FLAGS | OMXCodec::kSoftwareCodecsOnly;
|
||||
#endif
|
||||
}
|
||||
|
||||
MOZ_ASSERT(flags != 0);
|
||||
MOZ_ASSERT(flags != DEFAULT_STAGEFRIGHT_FLAGS);
|
||||
return OMXCodec::Create(aOmx, aVideoTrack->getFormat(), false, aVideoTrack,
|
||||
NULL, flags);
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ cc_string_t lineLabels[MAX_CONFIG_LINES+1] = {0};
|
|||
|
||||
void ccsnap_set_line_label(int btn, cc_string_t label) {
|
||||
|
||||
CCAPP_ERROR(DEB_F_PREFIX"btn=%d label=%s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "ccsnap_set_line_label"), btn, label);
|
||||
CCAPP_DEBUG(DEB_F_PREFIX"btn=%d label=%s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "ccsnap_set_line_label"), btn, label);
|
||||
if ( btn > 0 && btn <= MAX_CONFIG_LINES+1 ) {
|
||||
if ( label == NULL ) {
|
||||
label = strlib_empty();
|
||||
|
|
|
@ -92,7 +92,7 @@ config_set_string (int id, char *buffer)
|
|||
}
|
||||
}
|
||||
|
||||
#define MAX_CONFIG_VAL_PRINT_LEN 256
|
||||
#define MAX_CONFIG_VAL_PRINT_LEN 258
|
||||
/*
|
||||
* Function: print_config_value()
|
||||
*
|
||||
|
|
|
@ -675,6 +675,8 @@ pref("app.orientation.default", "");
|
|||
// back to the system.
|
||||
pref("memory.free_dirty_pages", true);
|
||||
|
||||
pref("layout.imagevisibility.enabled", false);
|
||||
|
||||
#ifdef MOZ_PKG_SPECIAL
|
||||
// Disable webgl on ARMv6 because running the reftests takes
|
||||
// too long for some reason (bug 843738)
|
||||
|
|
|
@ -123,17 +123,23 @@ abstract public class BrowserApp extends GeckoApp
|
|||
invalidateOptionsMenu();
|
||||
}
|
||||
break;
|
||||
case PAGE_SHOW:
|
||||
handlePageShow(tab);
|
||||
break;
|
||||
case LINK_ADDED:
|
||||
handleLinkAdded(tab);
|
||||
break;
|
||||
case SECURITY_CHANGE:
|
||||
handleSecurityChange(tab);
|
||||
break;
|
||||
case READER_ENABLED:
|
||||
handleReaderEnabled(tab);
|
||||
break;
|
||||
}
|
||||
super.onTabChanged(tab, msg, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
void handlePageShow(final int tabId) {
|
||||
super.handlePageShow(tabId);
|
||||
final Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
void handlePageShow(final Tab tab) {
|
||||
mMainHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
loadFavicon(tab);
|
||||
|
@ -141,16 +147,7 @@ abstract public class BrowserApp extends GeckoApp
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
void handleLinkAdded(final int tabId, String rel, final String href, int size) {
|
||||
super.handleLinkAdded(tabId, rel, href, size);
|
||||
if (rel.indexOf("[icon]") == -1)
|
||||
return;
|
||||
|
||||
final Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
void handleLinkAdded(final Tab tab) {
|
||||
// If tab is not loading and the favicon is updated, we
|
||||
// want to load the image straight away. If tab is still
|
||||
// loading, we only load the favicon once the page's content
|
||||
|
@ -170,13 +167,7 @@ abstract public class BrowserApp extends GeckoApp
|
|||
updateAboutHomeTopSites();
|
||||
}
|
||||
|
||||
@Override
|
||||
void handleSecurityChange(final int tabId, final JSONObject identityData) {
|
||||
super.handleSecurityChange(tabId, identityData);
|
||||
final Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
void handleSecurityChange(final Tab tab) {
|
||||
mMainHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
if (Tabs.getInstance().isSelectedTab(tab))
|
||||
|
@ -185,12 +176,30 @@ abstract public class BrowserApp extends GeckoApp
|
|||
});
|
||||
}
|
||||
|
||||
void handleReaderEnabled(final int tabId) {
|
||||
super.handleReaderEnabled(tabId);
|
||||
final Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
if (tab == null)
|
||||
void handleReaderAdded(boolean success, final String title, final String url) {
|
||||
if (!success) {
|
||||
showToast(R.string.reading_list_failed, Toast.LENGTH_SHORT);
|
||||
return;
|
||||
}
|
||||
|
||||
GeckoAppShell.getHandler().post(new Runnable() {
|
||||
public void run() {
|
||||
BrowserDB.addReadingListItem(getContentResolver(), title, url);
|
||||
showToast(R.string.reading_list_added, Toast.LENGTH_SHORT);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void handleReaderRemoved(final String url) {
|
||||
GeckoAppShell.getHandler().post(new Runnable() {
|
||||
public void run() {
|
||||
BrowserDB.removeReadingListItemWithURL(getContentResolver(), url);
|
||||
showToast(R.string.reading_list_removed, Toast.LENGTH_SHORT);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void handleReaderEnabled(final Tab tab) {
|
||||
mMainHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
if (Tabs.getInstance().isSelectedTab(tab))
|
||||
|
@ -536,6 +545,19 @@ abstract public class BrowserApp extends GeckoApp
|
|||
Telemetry.HistogramAdd("PLACES_BOOKMARKS_COUNT", BrowserDB.getCount(getContentResolver(), "bookmarks"));
|
||||
Telemetry.HistogramAdd("FENNEC_FAVICONS_COUNT", BrowserDB.getCount(getContentResolver(), "favicons"));
|
||||
Telemetry.HistogramAdd("FENNEC_THUMBNAILS_COUNT", BrowserDB.getCount(getContentResolver(), "thumbnails"));
|
||||
} else if (event.equals("Reader:Added")) {
|
||||
final boolean success = message.getBoolean("success");
|
||||
final String title = message.getString("title");
|
||||
final String url = message.getString("url");
|
||||
handleReaderAdded(success, title, url);
|
||||
} else if (event.equals("Reader:Removed")) {
|
||||
final String url = message.getString("url");
|
||||
handleReaderRemoved(url);
|
||||
} else if (event.equals("Reader:Share")) {
|
||||
final String title = message.getString("title");
|
||||
final String url = message.getString("url");
|
||||
GeckoAppShell.openUriExternal(url, "text/plain", "", "",
|
||||
Intent.ACTION_SEND, title);
|
||||
} else {
|
||||
super.handleMessage(event, message);
|
||||
}
|
||||
|
|
|
@ -56,21 +56,41 @@ public final class Distribution {
|
|||
return;
|
||||
}
|
||||
|
||||
// This pref stores the path to the distribution directory. If it is null, Gecko
|
||||
// looks for distribution files in /data/data/org.mozilla.xxx/distribution.
|
||||
String pathKeyName = context.getPackageName() + ".distribution_path";
|
||||
String distPath = null;
|
||||
|
||||
// Send a message to Gecko if we've set a distribution.
|
||||
if (state == STATE_SET) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Distribution:Set", null));
|
||||
distPath = settings.getString(pathKeyName, null);
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Distribution:Set", distPath));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean distributionSet = false;
|
||||
try {
|
||||
// First, try copying distribution files out of the APK.
|
||||
distributionSet = copyFiles(context, packagePath);
|
||||
} catch (IOException e) {
|
||||
Log.e(LOGTAG, "Error copying distribution files", e);
|
||||
}
|
||||
|
||||
if (!distributionSet) {
|
||||
// If there aren't any distribution files in the APK, look in the /system directory.
|
||||
File distDir = new File("/system/" + context.getPackageName() + "/distribution");
|
||||
if (distDir.exists()) {
|
||||
distributionSet = true;
|
||||
distPath = distDir.getPath();
|
||||
settings.edit().putString(pathKeyName, distPath).commit();
|
||||
}
|
||||
}
|
||||
|
||||
Log.i("BOOM", "distributionSet: " + distributionSet);
|
||||
Log.i("BOOM", "distPath: " + distPath);
|
||||
|
||||
if (distributionSet) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Distribution:Set", null));
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Distribution:Set", distPath));
|
||||
settings.edit().putInt(keyName, STATE_SET).commit();
|
||||
} else {
|
||||
settings.edit().putInt(keyName, STATE_NONE).commit();
|
||||
|
|
|
@ -609,22 +609,6 @@ abstract public class GeckoApp
|
|||
outState.putString(SAVED_STATE_PRIVATE_SESSION, mPrivateBrowsingSession);
|
||||
}
|
||||
|
||||
void handleSecurityChange(final int tabId, final JSONObject identityData) {
|
||||
final Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
tab.updateIdentityData(identityData);
|
||||
}
|
||||
|
||||
void handleReaderEnabled(final int tabId) {
|
||||
final Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
tab.setReaderEnabled(true);
|
||||
}
|
||||
|
||||
void handleFaviconRequest(final String url) {
|
||||
(new UiAsyncTask<Void, Void, String>(mMainHandler, GeckoAppShell.getHandler()) {
|
||||
@Override
|
||||
|
@ -650,17 +634,6 @@ abstract public class GeckoApp
|
|||
}).execute();
|
||||
}
|
||||
|
||||
void handleLoadError(final int tabId, final String uri, final String title) {
|
||||
final Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
// When a load error occurs, the URLBar can get corrupt so we reset it
|
||||
Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.LOAD_ERROR);
|
||||
}
|
||||
|
||||
void handlePageShow(final int tabId) { }
|
||||
|
||||
void handleClearHistory() {
|
||||
BrowserDB.clearHistory(getContentResolver());
|
||||
}
|
||||
|
@ -776,60 +749,15 @@ abstract public class GeckoApp
|
|||
if (layerView != null && Tabs.getInstance().isSelectedTab(tab)) {
|
||||
layerView.setBackgroundColor(tab.getBackgroundColor());
|
||||
}
|
||||
} else if (event.equals("DOMTitleChanged")) {
|
||||
final int tabId = message.getInt("tabID");
|
||||
final String title = message.getString("title");
|
||||
handleTitleChanged(tabId, title);
|
||||
} else if (event.equals("DOMLinkAdded")) {
|
||||
final int tabId = message.getInt("tabID");
|
||||
final String rel = message.getString("rel");
|
||||
final String href = message.getString("href");
|
||||
final int size = message.getInt("size");
|
||||
handleLinkAdded(tabId, rel, href, size);
|
||||
} else if (event.equals("DOMWindowClose")) {
|
||||
final int tabId = message.getInt("tabID");
|
||||
handleWindowClose(tabId);
|
||||
} else if (event.equals("log")) {
|
||||
// generic log listener
|
||||
final String msg = message.getString("msg");
|
||||
Log.d(LOGTAG, "Log: " + msg);
|
||||
} else if (event.equals("Content:SecurityChange")) {
|
||||
final int tabId = message.getInt("tabID");
|
||||
final JSONObject identity = message.getJSONObject("identity");
|
||||
Log.i(LOGTAG, "Security Mode - " + identity.getString("mode"));
|
||||
handleSecurityChange(tabId, identity);
|
||||
} else if (event.equals("Content:ReaderEnabled")) {
|
||||
final int tabId = message.getInt("tabID");
|
||||
handleReaderEnabled(tabId);
|
||||
} else if (event.equals("Reader:FaviconRequest")) {
|
||||
final String url = message.getString("url");
|
||||
handleFaviconRequest(url);
|
||||
} else if (event.equals("Reader:GoToReadingList")) {
|
||||
showReadingList();
|
||||
} else if (event.equals("Content:StateChange")) {
|
||||
final int tabId = message.getInt("tabID");
|
||||
final String uri = message.getString("uri");
|
||||
final boolean success = message.getBoolean("success");
|
||||
int state = message.getInt("state");
|
||||
Log.d(LOGTAG, "State - " + state);
|
||||
if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) {
|
||||
if ((state & GeckoAppShell.WPL_STATE_START) != 0) {
|
||||
Log.d(LOGTAG, "Got a document start event.");
|
||||
final boolean showProgress = message.getBoolean("showProgress");
|
||||
handleDocumentStart(tabId, showProgress, uri);
|
||||
} else if ((state & GeckoAppShell.WPL_STATE_STOP) != 0) {
|
||||
Log.d(LOGTAG, "Got a document stop event.");
|
||||
handleDocumentStop(tabId, success);
|
||||
}
|
||||
}
|
||||
} else if (event.equals("Content:LoadError")) {
|
||||
final int tabId = message.getInt("tabID");
|
||||
final String uri = message.getString("uri");
|
||||
final String title = message.getString("title");
|
||||
handleLoadError(tabId, uri, title);
|
||||
} else if (event.equals("Content:PageShow")) {
|
||||
final int tabId = message.getInt("tabID");
|
||||
handlePageShow(tabId);
|
||||
} else if (event.equals("Gecko:Ready")) {
|
||||
mGeckoReadyStartupTimer.stop();
|
||||
connectGeckoLayerClient();
|
||||
|
@ -1041,38 +969,6 @@ abstract public class GeckoApp
|
|||
});
|
||||
}
|
||||
|
||||
void handleDocumentStart(int tabId, final boolean showProgress, String uri) {
|
||||
final Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
tab.setState(shouldShowProgress(uri) ? Tab.STATE_SUCCESS : Tab.STATE_LOADING);
|
||||
tab.updateIdentityData(null);
|
||||
tab.setReaderEnabled(false);
|
||||
Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.START, showProgress);
|
||||
}
|
||||
|
||||
void handleDocumentStop(int tabId, boolean success) {
|
||||
final Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
tab.setState(success ? Tab.STATE_SUCCESS : Tab.STATE_ERROR);
|
||||
|
||||
Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.STOP);
|
||||
|
||||
final String oldURL = tab.getURL();
|
||||
GeckoAppShell.getHandler().postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
// tab.getURL() may return null
|
||||
if (!TextUtils.equals(oldURL, tab.getURL()))
|
||||
return;
|
||||
|
||||
ThumbnailHelper.getInstance().getAndProcessThumbnailFor(tab);
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
public void showToast(final int resId, final int duration) {
|
||||
mMainHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
|
@ -1102,31 +998,6 @@ abstract public class GeckoApp
|
|||
Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.LOADED);
|
||||
}
|
||||
|
||||
void handleTitleChanged(int tabId, String title) {
|
||||
final Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
tab.updateTitle(title);
|
||||
}
|
||||
|
||||
void handleLinkAdded(final int tabId, String rel, final String href, int size) {
|
||||
if (rel.indexOf("[icon]") == -1)
|
||||
return;
|
||||
|
||||
final Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
tab.updateFaviconURL(href, size);
|
||||
}
|
||||
|
||||
void handleWindowClose(final int tabId) {
|
||||
Tabs tabs = Tabs.getInstance();
|
||||
Tab tab = tabs.getTab(tabId);
|
||||
tabs.closeTab(tab);
|
||||
}
|
||||
|
||||
private void addFullScreenPluginView(View view) {
|
||||
if (mFullScreenPluginView != null) {
|
||||
Log.w(LOGTAG, "Already have a fullscreen plugin view");
|
||||
|
@ -1766,15 +1637,10 @@ abstract public class GeckoApp
|
|||
|
||||
//register for events
|
||||
registerEventListener("DOMContentLoaded");
|
||||
registerEventListener("DOMTitleChanged");
|
||||
registerEventListener("DOMLinkAdded");
|
||||
registerEventListener("DOMWindowClose");
|
||||
registerEventListener("log");
|
||||
registerEventListener("Content:SecurityChange");
|
||||
registerEventListener("Content:ReaderEnabled");
|
||||
registerEventListener("Content:StateChange");
|
||||
registerEventListener("Content:LoadError");
|
||||
registerEventListener("Content:PageShow");
|
||||
registerEventListener("Reader:Added");
|
||||
registerEventListener("Reader:Removed");
|
||||
registerEventListener("Reader:Share");
|
||||
registerEventListener("Reader:FaviconRequest");
|
||||
registerEventListener("Reader:GoToReadingList");
|
||||
registerEventListener("onCameraCapture");
|
||||
|
@ -2167,15 +2033,10 @@ abstract public class GeckoApp
|
|||
GeckoAppShell.sendEventToGecko(GeckoEvent.createShutdownEvent());
|
||||
|
||||
unregisterEventListener("DOMContentLoaded");
|
||||
unregisterEventListener("DOMTitleChanged");
|
||||
unregisterEventListener("DOMLinkAdded");
|
||||
unregisterEventListener("DOMWindowClose");
|
||||
unregisterEventListener("log");
|
||||
unregisterEventListener("Content:SecurityChange");
|
||||
unregisterEventListener("Content:ReaderEnabled");
|
||||
unregisterEventListener("Content:StateChange");
|
||||
unregisterEventListener("Content:LoadError");
|
||||
unregisterEventListener("Content:PageShow");
|
||||
unregisterEventListener("Reader:Added");
|
||||
unregisterEventListener("Reader:Removed");
|
||||
unregisterEventListener("Reader:Share");
|
||||
unregisterEventListener("Reader:FaviconRequest");
|
||||
unregisterEventListener("Reader:GoToReadingList");
|
||||
unregisterEventListener("onCameraCapture");
|
||||
|
@ -2753,10 +2614,6 @@ abstract public class GeckoApp
|
|||
return false;
|
||||
}
|
||||
|
||||
public static boolean shouldShowProgress(String url) {
|
||||
return "about:home".equals(url) || ReaderModeUtils.isAboutReader(url);
|
||||
}
|
||||
|
||||
public static void assertOnUiThread() {
|
||||
Thread uiThread = mAppContext.getMainLooper().getThread();
|
||||
assertOnThread(uiThread);
|
||||
|
|
|
@ -1502,12 +1502,11 @@ public class GeckoAppShell
|
|||
boolean callback(int pid);
|
||||
}
|
||||
|
||||
static int sPidColumn = -1;
|
||||
static int sUserColumn = -1;
|
||||
private static void EnumerateGeckoProcesses(GeckoProcessesVisitor visiter) {
|
||||
int pidColumn = -1;
|
||||
int userColumn = -1;
|
||||
|
||||
try {
|
||||
|
||||
// run ps and parse its output
|
||||
java.lang.Process ps = Runtime.getRuntime().exec("ps");
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(ps.getInputStream()),
|
||||
|
@ -1516,30 +1515,28 @@ public class GeckoAppShell
|
|||
String headerOutput = in.readLine();
|
||||
|
||||
// figure out the column offsets. We only care about the pid and user fields
|
||||
if (sPidColumn == -1 || sUserColumn == -1) {
|
||||
StringTokenizer st = new StringTokenizer(headerOutput);
|
||||
|
||||
int tokenSoFar = 0;
|
||||
while(st.hasMoreTokens()) {
|
||||
String next = st.nextToken();
|
||||
if (next.equalsIgnoreCase("PID"))
|
||||
sPidColumn = tokenSoFar;
|
||||
else if (next.equalsIgnoreCase("USER"))
|
||||
sUserColumn = tokenSoFar;
|
||||
tokenSoFar++;
|
||||
}
|
||||
StringTokenizer st = new StringTokenizer(headerOutput);
|
||||
|
||||
int tokenSoFar = 0;
|
||||
while (st.hasMoreTokens()) {
|
||||
String next = st.nextToken();
|
||||
if (next.equalsIgnoreCase("PID"))
|
||||
pidColumn = tokenSoFar;
|
||||
else if (next.equalsIgnoreCase("USER"))
|
||||
userColumn = tokenSoFar;
|
||||
tokenSoFar++;
|
||||
}
|
||||
|
||||
// alright, the rest are process entries.
|
||||
String psOutput = null;
|
||||
while ((psOutput = in.readLine()) != null) {
|
||||
String[] split = psOutput.split("\\s+");
|
||||
if (split.length <= sPidColumn || split.length <= sUserColumn)
|
||||
if (split.length <= pidColumn || split.length <= userColumn)
|
||||
continue;
|
||||
int uid = android.os.Process.getUidForName(split[sUserColumn]);
|
||||
int uid = android.os.Process.getUidForName(split[userColumn]);
|
||||
if (uid == android.os.Process.myUid() &&
|
||||
!split[split.length - 1].equalsIgnoreCase("ps")) {
|
||||
int pid = Integer.parseInt(split[sPidColumn]);
|
||||
int pid = Integer.parseInt(split[pidColumn]);
|
||||
boolean keepGoing = visiter.callback(pid);
|
||||
if (keepGoing == false)
|
||||
break;
|
||||
|
|
|
@ -22,6 +22,7 @@ import android.graphics.Color;
|
|||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
|
@ -88,7 +89,7 @@ public class Tab {
|
|||
mZoomConstraints = new ZoomConstraints(false);
|
||||
mPluginViews = new ArrayList<View>();
|
||||
mPluginLayers = new HashMap<Object, Layer>();
|
||||
mState = GeckoApp.shouldShowProgress(url) ? STATE_SUCCESS : STATE_LOADING;
|
||||
mState = shouldShowProgress(url) ? STATE_SUCCESS : STATE_LOADING;
|
||||
}
|
||||
|
||||
private ContentResolver getContentResolver() {
|
||||
|
@ -529,6 +530,32 @@ public class Tab {
|
|||
Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.LOCATION_CHANGE, uri);
|
||||
}
|
||||
|
||||
private boolean shouldShowProgress(String url) {
|
||||
return "about:home".equals(url) || ReaderModeUtils.isAboutReader(url);
|
||||
}
|
||||
|
||||
void handleDocumentStart(boolean showProgress, String url) {
|
||||
setState(shouldShowProgress(url) ? STATE_SUCCESS : STATE_LOADING);
|
||||
updateIdentityData(null);
|
||||
setReaderEnabled(false);
|
||||
}
|
||||
|
||||
void handleDocumentStop(boolean success) {
|
||||
setState(success ? STATE_SUCCESS : STATE_ERROR);
|
||||
|
||||
final String oldURL = getURL();
|
||||
final Tab tab = this;
|
||||
GeckoAppShell.getHandler().postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
// tab.getURL() may return null
|
||||
if (!TextUtils.equals(oldURL, getURL()))
|
||||
return;
|
||||
|
||||
ThumbnailHelper.getInstance().getAndProcessThumbnailFor(tab);
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
protected void saveThumbnailToDB() {
|
||||
try {
|
||||
String url = getURL();
|
||||
|
|
|
@ -59,6 +59,7 @@ public class Tabs implements GeckoEventListener {
|
|||
private ContentObserver mContentObserver;
|
||||
|
||||
private Tabs() {
|
||||
registerEventListener("Session:RestoreEnd");
|
||||
registerEventListener("SessionHistory:New");
|
||||
registerEventListener("SessionHistory:Back");
|
||||
registerEventListener("SessionHistory:Forward");
|
||||
|
@ -68,10 +69,13 @@ public class Tabs implements GeckoEventListener {
|
|||
registerEventListener("Tab:Close");
|
||||
registerEventListener("Tab:Select");
|
||||
registerEventListener("Content:LocationChange");
|
||||
registerEventListener("Session:RestoreEnd");
|
||||
registerEventListener("Reader:Added");
|
||||
registerEventListener("Reader:Removed");
|
||||
registerEventListener("Reader:Share");
|
||||
registerEventListener("Content:SecurityChange");
|
||||
registerEventListener("Content:ReaderEnabled");
|
||||
registerEventListener("Content:StateChange");
|
||||
registerEventListener("Content:LoadError");
|
||||
registerEventListener("Content:PageShow");
|
||||
registerEventListener("DOMTitleChanged");
|
||||
registerEventListener("DOMLinkAdded");
|
||||
}
|
||||
|
||||
public void attachToActivity(GeckoApp activity) {
|
||||
|
@ -275,25 +279,25 @@ public class Tabs implements GeckoEventListener {
|
|||
|
||||
public void handleMessage(String event, JSONObject message) {
|
||||
try {
|
||||
if (event.startsWith("SessionHistory:")) {
|
||||
Tab tab = getTab(message.getInt("tabID"));
|
||||
if (tab != null) {
|
||||
event = event.substring("SessionHistory:".length());
|
||||
tab.handleSessionHistoryMessage(event, message);
|
||||
}
|
||||
} else if (event.equals("Tab:Added")) {
|
||||
if (event.equals("Session:RestoreEnd")) {
|
||||
notifyListeners(null, TabEvents.RESTORED);
|
||||
return;
|
||||
}
|
||||
|
||||
// All other events handled below should contain a tabID property
|
||||
int id = message.getInt("tabID");
|
||||
Tab tab = getTab(id);
|
||||
|
||||
// "Tab:Added" is a special case because tab will be null if the tab was just added
|
||||
if (event.equals("Tab:Added")) {
|
||||
String url = message.isNull("uri") ? null : message.getString("uri");
|
||||
int id = message.getInt("tabID");
|
||||
Tab tab = null;
|
||||
|
||||
if (message.getBoolean("stub")) {
|
||||
if (mTabs.containsKey(id)) {
|
||||
tab = mTabs.get(id);
|
||||
tab.updateURL(url);
|
||||
} else {
|
||||
if (tab == null) {
|
||||
// Tab was already closed; abort
|
||||
return;
|
||||
}
|
||||
tab.updateURL(url);
|
||||
} else {
|
||||
tab = addTab(id, url, message.getBoolean("external"),
|
||||
message.getInt("parentId"),
|
||||
|
@ -302,66 +306,60 @@ public class Tabs implements GeckoEventListener {
|
|||
}
|
||||
|
||||
if (message.getBoolean("selected"))
|
||||
selectTab(tab.getId());
|
||||
selectTab(id);
|
||||
if (message.getBoolean("delayLoad"))
|
||||
tab.setState(Tab.STATE_DELAYED);
|
||||
if (message.getBoolean("desktopMode"))
|
||||
tab.setDesktopMode(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Tab was already closed; abort
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
if (event.startsWith("SessionHistory:")) {
|
||||
event = event.substring("SessionHistory:".length());
|
||||
tab.handleSessionHistoryMessage(event, message);
|
||||
} else if (event.equals("Tab:Close")) {
|
||||
Tab tab = getTab(message.getInt("tabID"));
|
||||
closeTab(tab);
|
||||
} else if (event.equals("Tab:Select")) {
|
||||
selectTab(message.getInt("tabID"));
|
||||
selectTab(tab.getId());
|
||||
} else if (event.equals("Content:LocationChange")) {
|
||||
Tab tab = getTab(message.getInt("tabID"));
|
||||
if (tab != null) {
|
||||
tab.handleLocationChange(message);
|
||||
tab.handleLocationChange(message);
|
||||
} else if (event.equals("Content:SecurityChange")) {
|
||||
tab.updateIdentityData(message.getJSONObject("identity"));
|
||||
notifyListeners(tab, TabEvents.SECURITY_CHANGE);
|
||||
} else if (event.equals("Content:ReaderEnabled")) {
|
||||
tab.setReaderEnabled(true);
|
||||
notifyListeners(tab, TabEvents.READER_ENABLED);
|
||||
} else if (event.equals("Content:StateChange")) {
|
||||
int state = message.getInt("state");
|
||||
if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) {
|
||||
if ((state & GeckoAppShell.WPL_STATE_START) != 0) {
|
||||
boolean showProgress = message.getBoolean("showProgress");
|
||||
tab.handleDocumentStart(showProgress, message.getString("uri"));
|
||||
notifyListeners(tab, Tabs.TabEvents.START, showProgress);
|
||||
} else if ((state & GeckoAppShell.WPL_STATE_STOP) != 0) {
|
||||
tab.handleDocumentStop(message.getBoolean("success"));
|
||||
notifyListeners(tab, Tabs.TabEvents.STOP);
|
||||
}
|
||||
}
|
||||
} else if (event.equals("Session:RestoreEnd")) {
|
||||
notifyListeners(null, TabEvents.RESTORED);
|
||||
} else if (event.equals("Reader:Added")) {
|
||||
final boolean success = message.getBoolean("success");
|
||||
final String title = message.getString("title");
|
||||
final String url = message.getString("url");
|
||||
handleReaderAdded(success, title, url);
|
||||
} else if (event.equals("Reader:Removed")) {
|
||||
final String url = message.getString("url");
|
||||
handleReaderRemoved(url);
|
||||
} else if (event.equals("Reader:Share")) {
|
||||
final String title = message.getString("title");
|
||||
final String url = message.getString("url");
|
||||
|
||||
GeckoAppShell.openUriExternal(url, "text/plain", "", "",
|
||||
Intent.ACTION_SEND, title);
|
||||
} else if (event.equals("Content:LoadError")) {
|
||||
notifyListeners(tab, Tabs.TabEvents.LOAD_ERROR);
|
||||
} else if (event.equals("Content:PageShow")) {
|
||||
notifyListeners(tab, TabEvents.PAGE_SHOW);
|
||||
} else if (event.equals("DOMTitleChanged")) {
|
||||
tab.updateTitle(message.getString("title"));
|
||||
} else if (event.equals("DOMLinkAdded")) {
|
||||
tab.updateFaviconURL(message.getString("href"), message.getInt("size"));
|
||||
notifyListeners(tab, TabEvents.LINK_ADDED);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(LOGTAG, "handleMessage threw for " + event, e);
|
||||
}
|
||||
}
|
||||
|
||||
void handleReaderAdded(boolean success, final String title, final String url) {
|
||||
if (!success) {
|
||||
mActivity.showToast(R.string.reading_list_failed, Toast.LENGTH_SHORT);
|
||||
return;
|
||||
}
|
||||
|
||||
GeckoAppShell.getHandler().post(new Runnable() {
|
||||
public void run() {
|
||||
BrowserDB.addReadingListItem(getContentResolver(), title, url);
|
||||
mActivity.showToast(R.string.reading_list_added, Toast.LENGTH_SHORT);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void handleReaderRemoved(final String url) {
|
||||
GeckoAppShell.getHandler().post(new Runnable() {
|
||||
public void run() {
|
||||
BrowserDB.removeReadingListItemWithURL(getContentResolver(), url);
|
||||
mActivity.showToast(R.string.reading_list_removed, Toast.LENGTH_SHORT);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void refreshThumbnails() {
|
||||
final ThumbnailHelper helper = ThumbnailHelper.getInstance();
|
||||
Iterator<Tab> iterator = mTabs.values().iterator();
|
||||
|
@ -409,7 +407,11 @@ public class Tabs implements GeckoEventListener {
|
|||
ADDED,
|
||||
RESTORED,
|
||||
LOCATION_CHANGE,
|
||||
MENU_UPDATED
|
||||
MENU_UPDATED,
|
||||
PAGE_SHOW,
|
||||
LINK_ADDED,
|
||||
SECURITY_CHANGE,
|
||||
READER_ENABLED
|
||||
}
|
||||
|
||||
public void notifyListeners(Tab tab, TabEvents msg) {
|
||||
|
|
|
@ -679,9 +679,7 @@ var BrowserApp = {
|
|||
if (tab) {
|
||||
let message = {
|
||||
type: "Content:LoadError",
|
||||
tabID: tab.id,
|
||||
uri: aBrowser.currentURI.spec,
|
||||
title: aBrowser.contentTitle
|
||||
tabID: tab.id
|
||||
};
|
||||
sendMessageToJava(message);
|
||||
dump("Handled load error: " + e)
|
||||
|
@ -2923,9 +2921,7 @@ Tab.prototype = {
|
|||
} catch(e) {
|
||||
let message = {
|
||||
type: "Content:LoadError",
|
||||
tabID: this.id,
|
||||
uri: this.browser.currentURI.spec,
|
||||
title: this.browser.contentTitle
|
||||
tabID: this.id
|
||||
};
|
||||
sendMessageToJava(message);
|
||||
dump("Handled load error: " + e);
|
||||
|
@ -3450,6 +3446,10 @@ Tab.prototype = {
|
|||
list.push("[" + rel + "]");
|
||||
}
|
||||
|
||||
// We only care about icon links
|
||||
if (list.indexOf("[icon]") == -1)
|
||||
return;
|
||||
|
||||
// We want to get the largest icon size possible for our UI.
|
||||
let maxSize = 0;
|
||||
|
||||
|
@ -3510,7 +3510,7 @@ Tab.prototype = {
|
|||
aEvent.preventDefault();
|
||||
|
||||
sendMessageToJava({
|
||||
type: "DOMWindowClose",
|
||||
type: "Tab:Close",
|
||||
tabID: this.id
|
||||
});
|
||||
}
|
||||
|
@ -8152,15 +8152,19 @@ var MemoryObserver = {
|
|||
};
|
||||
|
||||
var Distribution = {
|
||||
// File used to store campaign data
|
||||
_file: null,
|
||||
|
||||
// Path to distribution directory for distribution customizations
|
||||
_path: null,
|
||||
|
||||
init: function dc_init() {
|
||||
Services.obs.addObserver(this, "Distribution:Set", false);
|
||||
Services.obs.addObserver(this, "prefservice:after-app-defaults", false);
|
||||
Services.obs.addObserver(this, "Campaign:Set", false);
|
||||
|
||||
// Look for file outside the APK:
|
||||
// /data/data/org.mozilla.fennec/distribution.json
|
||||
// /data/data/org.mozilla.xxx/distribution.json
|
||||
this._file = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
|
||||
this._file.append("distribution.json");
|
||||
this.readJSON(this._file, this.update);
|
||||
|
@ -8175,6 +8179,8 @@ var Distribution = {
|
|||
observe: function dc_observe(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "Distribution:Set":
|
||||
this._path = aData;
|
||||
|
||||
// Reload the default prefs so we can observe "prefservice:after-app-defaults"
|
||||
Services.prefs.QueryInterface(Ci.nsIObserver).observe(null, "reload-default-prefs", null);
|
||||
break;
|
||||
|
@ -8215,10 +8221,16 @@ var Distribution = {
|
|||
},
|
||||
|
||||
getPrefs: function dc_getPrefs() {
|
||||
// Look for preferences file outside the APK:
|
||||
// /data/data/org.mozilla.fennec/distribution/preferences.json
|
||||
let file = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
|
||||
file.append("distribution");
|
||||
let file;
|
||||
if (this._path) {
|
||||
file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
||||
file.initWithPath(this._path);
|
||||
} else {
|
||||
// If a path isn't specified, look in the data directory:
|
||||
// /data/data/org.mozilla.xxx/distribution
|
||||
file = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
|
||||
file.append("distribution");
|
||||
}
|
||||
file.append("preferences.json");
|
||||
|
||||
this.readJSON(file, this.applyPrefs);
|
||||
|
|
|
@ -657,3 +657,5 @@ pref("media.realtime_decoder.enabled", true);
|
|||
|
||||
// Mobile manages state by autodetection
|
||||
pref("network.manage-offline-status", true);
|
||||
|
||||
pref("layout.imagevisibility.enabled", false);
|
||||
|
|
|
@ -232,7 +232,7 @@ pref("gfx.filter.nearest.force-enabled", false);
|
|||
// whether to always search all font cmaps during system font fallback
|
||||
pref("gfx.font_rendering.fallback.always_use_cmaps", false);
|
||||
|
||||
pref("gfx.font_rendering.graphite.enabled", false);
|
||||
pref("gfx.font_rendering.graphite.enabled", true);
|
||||
|
||||
// Check intl/unicharutil/util/nsUnicodeProperties.h for definitions of script bits
|
||||
// in the ShapingType enumeration
|
||||
|
@ -4110,6 +4110,12 @@ pref("dom.placeholder.show_on_focus", true);
|
|||
pref("wap.UAProf.url", "");
|
||||
pref("wap.UAProf.tagname", "x-wap-profile");
|
||||
|
||||
//Retrieval mode for MMS
|
||||
//manual: Manual retrieval mode.
|
||||
//automatic: Automatic retrieval mode.
|
||||
//never: Never retrieval mode.
|
||||
pref("dom.mms.retrieval_mode", "manual");
|
||||
|
||||
// If the user puts a finger down on an element and we think the user
|
||||
// might be executing a pan gesture, how long do we wait before
|
||||
// tentatively deciding the gesture is actually a tap and activating
|
||||
|
|
|
@ -11,42 +11,31 @@ VPATH = @srcdir@
|
|||
include $(DEPTH)/config/autoconf.mk
|
||||
CC_WRAPPER =
|
||||
CXX_WRAPPER =
|
||||
|
||||
default::
|
||||
|
||||
include $(topsrcdir)/config/config.mk
|
||||
|
||||
ifndef MOZ_NATIVE_NSS
|
||||
LOADABLE_ROOT_MODULE = $(DLL_PREFIX)nssckbi$(DLL_SUFFIX)
|
||||
endif
|
||||
NSS_DLLS = \
|
||||
nss3 \
|
||||
nssutil3 \
|
||||
smime3 \
|
||||
ssl3 \
|
||||
$(NULL)
|
||||
|
||||
NSS3_LIB = $(DLL_PREFIX)nss3$(DLL_SUFFIX)
|
||||
NSSUTIL3_LIB = $(DLL_PREFIX)nssutil3$(DLL_SUFFIX)
|
||||
SMIME3_LIB = $(DLL_PREFIX)smime3$(DLL_SUFFIX)
|
||||
SSL3_LIB = $(DLL_PREFIX)ssl3$(DLL_SUFFIX)
|
||||
SOFTOKEN3_LIB = $(DLL_PREFIX)softokn3$(DLL_SUFFIX)
|
||||
NSS_EXTRA_DLLS = \
|
||||
nssckbi \
|
||||
softokn3 \
|
||||
$(NULL)
|
||||
|
||||
ifndef NSS_DISABLE_DBM
|
||||
NSSDBM3_LIB = $(DLL_PREFIX)nssdbm3$(DLL_SUFFIX)
|
||||
else
|
||||
NSSDBM3_LIB =
|
||||
NSS_EXTRA_DLLS += nssdbm3
|
||||
endif
|
||||
|
||||
ifndef MOZ_NATIVE_NSS
|
||||
SDK_LIBS = crmf
|
||||
|
||||
ifneq (,$(filter OS2 WINNT,$(OS_ARCH)))
|
||||
SDK_LIBS = \
|
||||
$(DIST)/lib/$(LIB_PREFIX)crmf.$(LIB_SUFFIX) \
|
||||
$(DIST)/lib/$(LIB_PREFIX)smime3.$(LIB_SUFFIX) \
|
||||
$(DIST)/lib/$(LIB_PREFIX)ssl3.$(LIB_SUFFIX) \
|
||||
$(DIST)/lib/$(LIB_PREFIX)nss3.$(LIB_SUFFIX) \
|
||||
$(DIST)/lib/$(LIB_PREFIX)nssutil3.$(LIB_SUFFIX) \
|
||||
$(NULL)
|
||||
else
|
||||
SDK_LIBS = \
|
||||
$(DIST)/lib/$(LIB_PREFIX)crmf.$(LIB_SUFFIX) \
|
||||
$(DIST)/lib/$(LIB_PREFIX)smime.$(LIB_SUFFIX) \
|
||||
$(DIST)/lib/$(LIB_PREFIX)ssl.$(LIB_SUFFIX) \
|
||||
$(DIST)/lib/$(LIB_PREFIX)nss.$(LIB_SUFFIX) \
|
||||
$(DIST)/lib/$(LIB_PREFIX)nssutil.$(LIB_SUFFIX) \
|
||||
$(NULL)
|
||||
endif
|
||||
SDK_LIBS += $(NSS_DLLS)
|
||||
endif
|
||||
|
||||
# Default
|
||||
|
@ -77,18 +66,18 @@ endif
|
|||
endif
|
||||
|
||||
ifdef HAVE_FREEBL_LIBS
|
||||
FREEBL_LIB = $(DLL_PREFIX)freebl3$(DLL_SUFFIX)
|
||||
NSS_EXTRA_DLLS += freebl3
|
||||
endif
|
||||
ifdef HAVE_FREEBL_LIBS_32
|
||||
FREEBL_32INT_LIB = libfreebl_32int_3$(DLL_SUFFIX)
|
||||
FREEBL_32FPU_LIB = libfreebl_32fpu_3$(DLL_SUFFIX)
|
||||
NSS_EXTRA_DLLS += freebl_32int_3
|
||||
NSS_EXTRA_DLLS += freebl_32fpu_3
|
||||
endif
|
||||
ifdef HAVE_FREEBL_LIBS_32INT64
|
||||
FREEBL_32INT64_LIB = libfreebl_32int64_3$(DLL_SUFFIX)
|
||||
NSS_EXTRA_DLLS += freebl_32int64_3
|
||||
endif
|
||||
ifdef HAVE_FREEBL_LIBS_64
|
||||
FREEBL_64INT_LIB = libfreebl_64int_3$(DLL_SUFFIX)
|
||||
FREEBL_64FPU_LIB = libfreebl_64fpu_3$(DLL_SUFFIX)
|
||||
NSS_EXTRA_DLLS += freebl_64int_3
|
||||
NSS_EXTRA_DLLS += freebl_64fpu_3
|
||||
endif
|
||||
|
||||
ABS_DIST := $(call core_abspath,$(DIST))
|
||||
|
@ -123,8 +112,7 @@ endif
|
|||
# To get debug symbols from NSS
|
||||
export MOZ_DEBUG_SYMBOLS
|
||||
|
||||
# NSS makefiles are not safe for parallel execution.
|
||||
DEFAULT_GMAKE_FLAGS = -j1
|
||||
DEFAULT_GMAKE_FLAGS =
|
||||
DEFAULT_GMAKE_FLAGS += CC="$(CC)"
|
||||
DEFAULT_GMAKE_FLAGS += SOURCE_MD_DIR=$(ABS_DIST)
|
||||
DEFAULT_GMAKE_FLAGS += SOURCE_MDHEADERS_DIR=$(NSPR_INCLUDE_DIR)
|
||||
|
@ -135,6 +123,9 @@ DEFAULT_GMAKE_FLAGS += MOZILLA_CLIENT=1
|
|||
DEFAULT_GMAKE_FLAGS += NO_MDUPDATE=1
|
||||
DEFAULT_GMAKE_FLAGS += NSS_ENABLE_ECC=1
|
||||
DEFAULT_GMAKE_FLAGS += NSINSTALL="$(NSINSTALL)"
|
||||
ifeq ($(OS_ARCH),WINNT)
|
||||
DEFAULT_GMAKE_FLAGS += INSTALL="$(NSINSTALL) -t"
|
||||
endif
|
||||
ifndef MOZ_NATIVE_SQLITE
|
||||
DEFAULT_GMAKE_FLAGS += SQLITE_LIB_NAME=mozsqlite3
|
||||
DEFAULT_GMAKE_FLAGS += SQLITE_INCLUDE_DIR=$(ABS_DIST)/include
|
||||
|
@ -251,6 +242,18 @@ DEFAULT_GMAKE_FLAGS += XCFLAGS="$(CFLAGS)"
|
|||
DEFAULT_GMAKE_FLAGS += DARWIN_DYLIB_VERSIONS="-compatibility_version 1 -current_version 1 $(LDFLAGS)"
|
||||
endif
|
||||
|
||||
# Put NSS headers directly under $(DIST)/include
|
||||
DEFAULT_GMAKE_FLAGS += PUBLIC_EXPORT_DIR='$(ABS_DIST)/include/$$(MODULE)'
|
||||
DEFAULT_GMAKE_FLAGS += SOURCE_XPHEADERS_DIR='$$(SOURCE_XP_DIR)/include/$$(MODULE)'
|
||||
DEFAULT_GMAKE_FLAGS += MODULE_INCLUDES='$$(addprefix -I$$(SOURCE_XP_DIR)/include/,$$(REQUIRES))'
|
||||
|
||||
# Work around NSS's MAKE_OBJDIR being racy. See bug #836220
|
||||
DEFAULT_GMAKE_FLAGS += MAKE_OBJDIR='$$(INSTALL) -D $$(OBJDIR)'
|
||||
|
||||
# Work around NSS adding IMPORT_LIBRARY to TARGETS with no rule for
|
||||
# it, creating race conditions. See bug #836220
|
||||
DEFAULT_GMAKE_FLAGS += TARGETS='$$(LIBRARY) $$(SHARED_LIBRARY) $$(PROGRAM)'
|
||||
|
||||
ifdef MOZ_B2G_CERTDATA
|
||||
include $(srcdir)/b2g-certdata.mk
|
||||
endif
|
||||
|
@ -298,53 +301,59 @@ ifneq (,$(filter %--build-id,$(LDFLAGS)))
|
|||
DEFAULT_GMAKE_ENV = LDFLAGS=-Wl,--build-id
|
||||
endif
|
||||
|
||||
ifndef MOZ_NATIVE_NSS
|
||||
define build_rules
|
||||
libs::
|
||||
$$(DEFAULT_GMAKE_ENV) $$(MAKE) -C $$(NSS_SRCDIR)/security/$(1) $$(DEFAULT_GMAKE_FLAGS)
|
||||
NSS_DIST_DLL_FILES := $(addprefix $(DIST)/lib/$(DLL_PREFIX),$(addsuffix $(DLL_SUFFIX),$(NSS_DLLS) $(NSS_EXTRA_DLLS)))
|
||||
NSS_DIST_DLL_DEST := $(DIST)/bin
|
||||
INSTALL_TARGETS += NSS_DIST_DLL
|
||||
|
||||
clean clobber clobber_all realclean distclean depend::
|
||||
$$(MAKE) -C $$(NSS_SRCDIR)/security/$(1) $$(DEFAULT_GMAKE_FLAGS) clean
|
||||
endef
|
||||
$(foreach dir,$(NSS_DIRS),$(eval $(call build_rules,$(dir))))
|
||||
|
||||
NSS_LIBS = \
|
||||
$(LOADABLE_ROOT_MODULE) \
|
||||
$(SOFTOKEN3_LIB) \
|
||||
$(NSSDBM3_LIB) \
|
||||
$(NSS3_LIB) \
|
||||
$(NSSUTIL3_LIB) \
|
||||
$(SSL3_LIB) \
|
||||
$(SMIME3_LIB) \
|
||||
$(FREEBL_LIB) \
|
||||
$(FREEBL_32INT_LIB) \
|
||||
$(FREEBL_32FPU_LIB) \
|
||||
$(FREEBL_32INT64_LIB) \
|
||||
$(FREEBL_64INT_LIB) \
|
||||
$(FREEBL_64FPU_LIB) \
|
||||
$(NULL)
|
||||
|
||||
define install_rules
|
||||
libs::
|
||||
ifeq ($(OS_ARCH)_$(1), SunOS_$(SOFTOKEN3_LIB))
|
||||
ifeq ($(OS_ARCH)_$(1), SunOS_softokn3)
|
||||
# has to use copy mode on Solaris, see #665509
|
||||
$$(NSINSTALL) -t -m 755 $$(DIST)/lib/$(1) $$(DIST)/bin
|
||||
else
|
||||
$$(INSTALL) -m 755 $$(DIST)/lib/$(1) $$(DIST)/bin
|
||||
$(DIST)/bin/$(DLL_PREFIX)softokn3$(DLL_SUFFIX): INSTALL := $(INSTALL) -t
|
||||
endif
|
||||
|
||||
install::
|
||||
$$(SYSINSTALL) -m 755 $$(DIST)/lib/$(1) $$(DESTDIR)$$(mozappdir)
|
||||
endef
|
||||
$(foreach lib,$(NSS_LIBS),$(eval $(call install_rules,$(lib))))
|
||||
NSS_SDK_LIB_FILES := \
|
||||
$(addprefix $(DIST)/lib/$(LIB_PREFIX),$(addsuffix .$(LIB_SUFFIX),$(SDK_LIBS))) \
|
||||
$(addprefix $(DIST)/bin/$(DLL_PREFIX),$(addsuffix $(DLL_SUFFIX),$(NSS_DLLS))) \
|
||||
$(NULL)
|
||||
NSS_SDK_LIB_DEST := $(DIST)/sdk/lib
|
||||
INSTALL_TARGETS += NSS_SDK_LIB
|
||||
|
||||
libs::
|
||||
$(INSTALL) -m 755 $(SDK_LIBS) $(DIST)/sdk/lib
|
||||
# NSS installs headers to dist/public and we want them in dist/include
|
||||
$(NSINSTALL) -D $(DIST)/include/nss
|
||||
(cd $(DIST)/public/nss && tar $(TAR_CREATE_FLAGS) - .) | \
|
||||
(cd $(DIST)/include/nss && tar -xf -)
|
||||
|
||||
endif # MOZ_NATIVE_NSS
|
||||
$(NSS_DIST_DLL_FILES) $(NSS_SDK_LIB_FILES): libs-nss/lib
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
DEFAULT_GMAKE_FLAGS += $(EXTRA_GMAKE_FLAGS)
|
||||
|
||||
$(addprefix libs-,$(NSS_DIRS)): libs-%:
|
||||
# Work around NSS's export rule being racy when recursing for private_export
|
||||
# See bug #836220.
|
||||
$(addprefix export-,$(NSS_DIRS)): EXTRA_GMAKE_FLAGS = PRIVATE_EXPORTS=
|
||||
$(addprefix export-,$(NSS_DIRS)): export-%: private_export-%
|
||||
$(addprefix private_export-,$(NSS_DIRS)): EXTRA_GMAKE_FLAGS =
|
||||
$(addprefix private_export-,$(NSS_DIRS)): private_export-%:
|
||||
|
||||
# Work around bug #836228 in pymake
|
||||
ifdef .PYMAKE
|
||||
$(foreach p,libs export private_export,$(addprefix $(p)-,$(NSS_DIRS))): *=$(subst $(NULL) $(NULL),-,$(wordlist 2,$(words $(subst -, ,$@)),$(subst -, ,$@)))
|
||||
endif
|
||||
|
||||
$(foreach p,libs export private_export,$(addprefix $(p)-,$(NSS_DIRS))):
|
||||
$(DEFAULT_GMAKE_ENV) $(MAKE) -C $(NSS_SRCDIR)/security/$* $(@:-$*=) $(DEFAULT_GMAKE_FLAGS)
|
||||
|
||||
export:: $(addprefix export-,$(NSS_DIRS))
|
||||
|
||||
$(addprefix clean-,$(NSS_DIRS)): clean-%:
|
||||
$(MAKE) -C $(NSS_SRCDIR)/security/$* $(DEFAULT_GMAKE_FLAGS) clean
|
||||
|
||||
clean clobber clobber_all realclean distclean depend:: $(addprefix clean-,$(NSS_DIRS))
|
||||
|
||||
NSS_CMD_TARGETS := $(addprefix libs-,$(filter-out nss/cmd/lib,$(filter nss/cmd/%,$(NSS_DIRS))))
|
||||
libs:: $(NSS_CMD_TARGETS)
|
||||
|
||||
$(NSS_CMD_TARGETS): libs-nss/lib libs-nss/cmd/lib
|
||||
ifndef NSS_DISABLE_DBM
|
||||
libs-nss/lib: libs-dbm
|
||||
endif
|
||||
|
||||
# Work around NSS build system race condition creating certdata.c in
|
||||
# security/nss/lib/ckfw/builtins. See bug #836220.
|
||||
libs-nss/lib: $(call mkdir_deps,../nss/lib/ckfw/builtins)
|
||||
|
|
|
@ -30,6 +30,6 @@ hacked-certdata.txt : $(srcdir)/../nss/lib/ckfw/builtins/certdata.txt
|
|||
combined-certdata.txt : hacked-certdata.txt $(srcdir)/b2g-certdata.txt
|
||||
cat $^ > $@
|
||||
|
||||
libs:: combined-certdata.txt
|
||||
libs-nss/lib: combined-certdata.txt
|
||||
|
||||
DEFAULT_GMAKE_FLAGS += NSS_CERTDATA_TXT='$(CURDIR)/combined-certdata.txt'
|
||||
|
|
|
@ -22,6 +22,7 @@ mochikit.jar:
|
|||
content/tests/SimpleTest/SimpleTest.js (tests/SimpleTest/SimpleTest.js)
|
||||
content/tests/SimpleTest/test.css (tests/SimpleTest/test.css)
|
||||
content/tests/SimpleTest/TestRunner.js (tests/SimpleTest/TestRunner.js)
|
||||
content/tests/SimpleTest/iframe-between-tests.html (tests/SimpleTest/iframe-between-tests.html)
|
||||
content/tests/SimpleTest/WindowSnapshot.js (tests/SimpleTest/WindowSnapshot.js)
|
||||
content/tests/SimpleTest/MockObjects.js (tests/SimpleTest/MockObjects.js)
|
||||
content/tests/SimpleTest/NativeKeyCodes.js (tests/SimpleTest/NativeKeyCodes.js)
|
||||
|
|
|
@ -21,6 +21,7 @@ _SIMPLETEST_FILES = LogController.js \
|
|||
MockObjects.js \
|
||||
NativeKeyCodes.js \
|
||||
paint_listener.js \
|
||||
iframe-between-tests.html \
|
||||
$(DEPTH)/testing/specialpowers/content/MozillaLogger.js \
|
||||
$(DEPTH)/docshell/test/chrome/docshell_helpers.js \
|
||||
$(NULL)
|
||||
|
|
|
@ -468,6 +468,44 @@ SimpleTest.requestLongerTimeout = function (factor) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that the given range of assertions is to be expected. When
|
||||
* this function is not called, 0 assertions are expected. When only
|
||||
* one argument is given, that number of assertions are expected.
|
||||
*
|
||||
* A test where we expect to have assertions (which should largely be a
|
||||
* transitional mechanism to get assertion counts down from our current
|
||||
* situation) can call the SimpleTest.expectAssertions() function, with
|
||||
* either one or two arguments: one argument gives an exact number
|
||||
* expected, and two arguments give a range. For example, a test might do
|
||||
* one of the following:
|
||||
*
|
||||
* // Currently triggers two assertions (bug NNNNNN).
|
||||
* SimpleTest.expectAssertions(2);
|
||||
*
|
||||
* // Currently triggers one assertion on Mac (bug NNNNNN).
|
||||
* if (navigator.platform.indexOf("Mac") == 0) {
|
||||
* SimpleTest.expectAssertions(1);
|
||||
* }
|
||||
*
|
||||
* // Currently triggers two assertions on all platforms (bug NNNNNN),
|
||||
* // but intermittently triggers two additional assertions (bug NNNNNN)
|
||||
* // on Windows.
|
||||
* if (navigator.platform.indexOf("Win") == 0) {
|
||||
* SimpleTest.expectAssertions(2, 4);
|
||||
* } else {
|
||||
* SimpleTest.expectAssertions(2);
|
||||
* }
|
||||
*
|
||||
* // Intermittently triggers up to three assertions (bug NNNNNN).
|
||||
* SimpleTest.expectAssertions(0, 3);
|
||||
*/
|
||||
SimpleTest.expectAssertions = function(min, max) {
|
||||
if (parentRunner) {
|
||||
parentRunner.expectAssertions(min, max);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForFocus_started = false;
|
||||
SimpleTest.waitForFocus_loaded = false;
|
||||
SimpleTest.waitForFocus_focused = false;
|
||||
|
|
|
@ -72,9 +72,13 @@ function flattenArguments(lst/* ...*/) {
|
|||
var TestRunner = {};
|
||||
TestRunner.logEnabled = false;
|
||||
TestRunner._currentTest = 0;
|
||||
TestRunner._lastTestFinished = -1;
|
||||
TestRunner.currentTestURL = "";
|
||||
TestRunner.originalTestURL = "";
|
||||
TestRunner._urls = [];
|
||||
TestRunner._lastAssertionCount = 0;
|
||||
TestRunner._expectedMinAsserts = 0;
|
||||
TestRunner._expectedMaxAsserts = 0;
|
||||
|
||||
TestRunner.timeout = 5 * 60 * 1000; // 5 minutes.
|
||||
TestRunner.maxTimeouts = 4; // halt testing after too many timeouts
|
||||
|
@ -146,6 +150,18 @@ TestRunner.requestLongerTimeout = function(factor) {
|
|||
TestRunner.repeat = 0;
|
||||
TestRunner._currentLoop = 0;
|
||||
|
||||
TestRunner.expectAssertions = function(min, max) {
|
||||
if (typeof(max) == "undefined") {
|
||||
max = min;
|
||||
}
|
||||
if (typeof(min) != "number" || typeof(max) != "number" ||
|
||||
min < 0 || max < min) {
|
||||
throw "bad parameter to expectAssertions";
|
||||
}
|
||||
TestRunner._expectedMinAsserts = min;
|
||||
TestRunner._expectedMaxAsserts = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called after generating the summary.
|
||||
**/
|
||||
|
@ -338,6 +354,8 @@ TestRunner.runNextTest = function() {
|
|||
|
||||
TestRunner._currentTestStartTime = new Date().valueOf();
|
||||
TestRunner._timeoutFactor = 1;
|
||||
TestRunner._expectedMinAsserts = 0;
|
||||
TestRunner._expectedMaxAsserts = 0;
|
||||
|
||||
TestRunner.log("TEST-START | " + url); // used by automation.py
|
||||
|
||||
|
@ -401,6 +419,16 @@ TestRunner.expectChildProcessCrash = function() {
|
|||
* This stub is called by SimpleTest when a test is finished.
|
||||
**/
|
||||
TestRunner.testFinished = function(tests) {
|
||||
// Prevent a test from calling finish() multiple times before we
|
||||
// have a chance to unload it.
|
||||
if (TestRunner._currentTest == TestRunner._lastTestFinished) {
|
||||
TestRunner.error("TEST-UNEXPECTED-FAIL | " +
|
||||
TestRunner.currentTestURL +
|
||||
" | called finish() multiple times");
|
||||
return;
|
||||
}
|
||||
TestRunner._lastTestFinished = TestRunner._currentTest;
|
||||
|
||||
function cleanUpCrashDumpFiles() {
|
||||
if (!SpecialPowers.removeExpectedCrashDumpFiles(TestRunner._expectingProcessCrash)) {
|
||||
TestRunner.error("TEST-UNEXPECTED-FAIL | " +
|
||||
|
@ -440,12 +468,14 @@ TestRunner.testFinished = function(tests) {
|
|||
" | finished in " + runtime + "ms");
|
||||
|
||||
TestRunner.updateUI(tests);
|
||||
TestRunner._currentTest++;
|
||||
if (TestRunner.runSlower) {
|
||||
setTimeout(TestRunner.runNextTest, 1000);
|
||||
|
||||
var interstitialURL;
|
||||
if ($('testframe').contentWindow.location.protocol == "chrome:") {
|
||||
interstitialURL = "tests/SimpleTest/iframe-between-tests.html";
|
||||
} else {
|
||||
TestRunner.runNextTest();
|
||||
interstitialURL = "/tests/SimpleTest/iframe-between-tests.html";
|
||||
}
|
||||
TestRunner._makeIframe(interstitialURL, 0);
|
||||
}
|
||||
|
||||
SpecialPowers.executeAfterFlushingMessageQueue(function() {
|
||||
|
@ -454,6 +484,35 @@ TestRunner.testFinished = function(tests) {
|
|||
});
|
||||
};
|
||||
|
||||
TestRunner.testUnloaded = function() {
|
||||
if (SpecialPowers.isDebugBuild) {
|
||||
var newAssertionCount = SpecialPowers.assertionCount();
|
||||
var numAsserts = newAssertionCount - TestRunner._lastAssertionCount;
|
||||
TestRunner._lastAssertionCount = newAssertionCount;
|
||||
|
||||
var url = TestRunner._urls[TestRunner._currentTest];
|
||||
var max = TestRunner._expectedMaxAsserts;
|
||||
var min = TestRunner._expectedMinAsserts;
|
||||
if (numAsserts > max) {
|
||||
// WHEN ENABLING, change "log" to "error" and "DETCEPXENU"
|
||||
// to "UNEXPECTED".
|
||||
TestRunner.log("TEST-DETCEPXENU-FAIL | " + url + " | Assertion count " + numAsserts + " is greater than expected range " + min + "-" + max + " assertions.");
|
||||
} else if (numAsserts < min) {
|
||||
// WHEN ENABLING, change "log" to "error" and "DETCEPXENU"
|
||||
// to "UNEXPECTED".
|
||||
TestRunner.log("TEST-DETCEPXENU-PASS | " + url + " | Assertion count " + numAsserts + " is less than expected range " + min + "-" + max + " assertions.");
|
||||
} else if (numAsserts > 0) {
|
||||
TestRunner.log("TEST-KNOWN-FAIL | " + url + " | Assertion count " + numAsserts + " within expected range " + min + "-" + max + " assertions.");
|
||||
}
|
||||
}
|
||||
TestRunner._currentTest++;
|
||||
if (TestRunner.runSlower) {
|
||||
setTimeout(TestRunner.runNextTest, 1000);
|
||||
} else {
|
||||
TestRunner.runNextTest();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the results.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<title>iframe for between tests</title>
|
||||
<!--
|
||||
This page exists so that our accounting for assertions correctly
|
||||
counts assertions that happen while leaving a page. We load this page
|
||||
after a test finishes, check the assertion counts, and then go on to
|
||||
load the next.
|
||||
-->
|
||||
<script>
|
||||
window.addEventListener("load", function() {
|
||||
(parent.TestRunner || parent.wrappedJSObject.TestRunner).testUnloaded();
|
||||
});
|
||||
</script>
|
|
@ -1206,6 +1206,10 @@ SpecialPowersAPI.prototype = {
|
|||
var debug = Cc["@mozilla.org/xpcom/debug;1"].getService(Ci.nsIDebug2);
|
||||
return this.isDebugBuild = debug.isDebugBuild;
|
||||
},
|
||||
assertionCount: function() {
|
||||
var debugsvc = Cc['@mozilla.org/xpcom/debug;1'].getService(Ci.nsIDebug2);
|
||||
return debugsvc.assertionCount;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the message manager associated with an <iframe mozbrowser>.
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
[include:dom/network/tests/unit/xpcshell.ini]
|
||||
[include:dom/network/tests/unit_ipc/xpcshell.ini]
|
||||
[include:dom/network/tests/unit_stats/xpcshell.ini]
|
||||
[include:dom/payment/tests/unit/xpcshell.ini]
|
||||
[include:dom/permission/tests/unit/xpcshell.ini]
|
||||
[include:dom/src/json/test/unit/xpcshell.ini]
|
||||
[include:dom/system/gonk/tests/xpcshell.ini]
|
||||
|
|
|
@ -9,6 +9,8 @@ srcdir = @srcdir@
|
|||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
USE_RCS_MK := 1
|
||||
include $(topsrcdir)/config/makefiles/rcs.mk
|
||||
|
||||
MODULE = telemetry
|
||||
XPIDL_MODULE = telemetry
|
||||
|
@ -63,7 +65,7 @@ ifdef MOZILLA_OFFICIAL
|
|||
DEFINES += -DMOZILLA_OFFICIAL
|
||||
endif
|
||||
|
||||
MOZ_HISTOGRAMS_VERSION ?= $(shell hg path default)rev/$(shell hg -R $(topsrcdir) parent --template="{node|short}\n" 2>/dev/null)
|
||||
MOZ_HISTOGRAMS_VERSION ?= $(call getSourceRepo)/rev/$(firstword $(shell hg -R $(topsrcdir) parent --template="{node|short}\n" 2>/dev/null))
|
||||
ifdef MOZ_HISTOGRAMS_VERSION
|
||||
DEFINES += -DHISTOGRAMS_FILE_VERSION="$(MOZ_HISTOGRAMS_VERSION)"
|
||||
endif
|
||||
|
|
|
@ -610,7 +610,7 @@ ifdef ACCESSIBILITY
|
|||
OS_LIBS += $(call EXPAND_LIBNAME,oleacc)
|
||||
endif
|
||||
ifdef MOZ_METRO
|
||||
OS_LIBS += $(call EXPAND_LIBNAME,uiautomationcore)
|
||||
OS_LIBS += $(call EXPAND_LIBNAME,uiautomationcore runtimeobject)
|
||||
endif
|
||||
ifdef _MSC_VER
|
||||
OS_LIBS += $(call EXPAND_LIBNAME,delayimp)
|
||||
|
|
|
@ -13,6 +13,18 @@ tier_nspr_staticdirs += nsprpub
|
|||
tier_nspr_dirs += config/nspr
|
||||
endif
|
||||
|
||||
TIERS += nss
|
||||
|
||||
ifndef MOZ_NATIVE_SQLITE
|
||||
tier_nss_dirs += db/sqlite3/src
|
||||
endif
|
||||
|
||||
ifdef MOZ_PSM
|
||||
ifndef MOZ_NATIVE_NSS
|
||||
tier_nss_dirs += security/build
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/js/build.mk
|
||||
|
||||
TIERS += platform
|
||||
|
@ -31,16 +43,6 @@ endif
|
|||
|
||||
tier_platform_dirs += xpcom
|
||||
|
||||
ifndef MOZ_NATIVE_SQLITE
|
||||
tier_platform_dirs += db/sqlite3/src
|
||||
endif
|
||||
|
||||
ifdef MOZ_PSM
|
||||
tier_platform_dirs += \
|
||||
security/build \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
tier_platform_dirs += \
|
||||
modules/libpref \
|
||||
intl \
|
||||
|
|
|
@ -102,7 +102,15 @@ typedef uint16_t Modifiers;
|
|||
|
||||
// NotificationToIME is shared by nsIMEStateManager and TextComposition.
|
||||
enum NotificationToIME {
|
||||
// XXX We should replace NOTIFY_IME_OF_CURSOR_POS_CHANGED with
|
||||
// NOTIFY_IME_OF_SELECTION_CHANGE later.
|
||||
NOTIFY_IME_OF_CURSOR_POS_CHANGED,
|
||||
// An editable content is getting focus
|
||||
NOTIFY_IME_OF_FOCUS,
|
||||
// An editable content is losing focus
|
||||
NOTIFY_IME_OF_BLUR,
|
||||
// Selection in the focused editable content is changed
|
||||
NOTIFY_IME_OF_SELECTION_CHANGE,
|
||||
REQUEST_TO_COMMIT_COMPOSITION,
|
||||
REQUEST_TO_CANCEL_COMPOSITION
|
||||
};
|
||||
|
|
|
@ -52,6 +52,7 @@ CPPSRCS = \
|
|||
nsWidgetFactory.cpp \
|
||||
WinUtils.cpp \
|
||||
WinMouseScrollHandler.cpp \
|
||||
WinIMEHandler.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
|
|
|
@ -0,0 +1,389 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "WinIMEHandler.h"
|
||||
#include "nsIMM32Handler.h"
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
#include "nsTextStore.h"
|
||||
#endif // #ifdef NS_ENABLE_TSF
|
||||
|
||||
#include "nsWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace widget {
|
||||
|
||||
/******************************************************************************
|
||||
* IMEHandler
|
||||
******************************************************************************/
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
bool IMEHandler::sIsInTSFMode = false;
|
||||
bool IMEHandler::sPluginHasFocus = false;
|
||||
#endif // #ifdef NS_ENABLE_TSF
|
||||
|
||||
// static
|
||||
void
|
||||
IMEHandler::Initialize()
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
nsTextStore::Initialize();
|
||||
sIsInTSFMode = nsTextStore::IsInTSFMode();
|
||||
#endif // #ifdef NS_ENABLE_TSF
|
||||
|
||||
nsIMM32Handler::Initialize();
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
IMEHandler::Terminate()
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (sIsInTSFMode) {
|
||||
nsTextStore::Terminate();
|
||||
sIsInTSFMode = false;
|
||||
}
|
||||
#endif // #ifdef NS_ENABLE_TSF
|
||||
|
||||
nsIMM32Handler::Terminate();
|
||||
}
|
||||
|
||||
// static
|
||||
void*
|
||||
IMEHandler::GetNativeData(uint32_t aDataType)
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
void* result = nsTextStore::GetNativeData(aDataType);
|
||||
if (!result || !(*(static_cast<void**>(result)))) {
|
||||
return nullptr;
|
||||
}
|
||||
// XXX During the TSF module test, sIsInTSFMode must be true. After that,
|
||||
// the value should be restored but currently, there is no way for that.
|
||||
// When the TSF test is enabled again, we need to fix this. Perhaps,
|
||||
// sending a message can fix this.
|
||||
sIsInTSFMode = true;
|
||||
return result;
|
||||
#else // #ifdef NS_ENABLE_TSF
|
||||
return nullptr;
|
||||
#endif // #ifdef NS_ENABLE_TSF #else
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
IMEHandler::CanOptimizeKeyAndIMEMessages()
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (IsTSFAvailable()) {
|
||||
return nsTextStore::CanOptimizeKeyAndIMEMessages();
|
||||
}
|
||||
#endif // #ifdef NS_ENABLE_TSF
|
||||
|
||||
return nsIMM32Handler::CanOptimizeKeyAndIMEMessages();
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
IMEHandler::IsIMEEnabled(const InputContext& aInputContext)
|
||||
{
|
||||
return IsIMEEnabled(aInputContext.mIMEState.mEnabled);
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
IMEHandler::IsIMEEnabled(IMEState::Enabled aIMEState)
|
||||
{
|
||||
return (aIMEState == mozilla::widget::IMEState::ENABLED ||
|
||||
aIMEState == mozilla::widget::IMEState::PLUGIN);
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
IMEHandler::ProcessMessage(nsWindow* aWindow, UINT aMessage,
|
||||
WPARAM& aWParam, LPARAM& aLParam,
|
||||
LRESULT* aRetValue, bool& aEatMessage)
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (IsTSFAvailable()) {
|
||||
if (aMessage == WM_USER_TSF_TEXTCHANGE) {
|
||||
nsTextStore::OnTextChangeMsg();
|
||||
aEatMessage = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif // #ifdef NS_ENABLE_TSF
|
||||
|
||||
return nsIMM32Handler::ProcessMessage(aWindow, aMessage, aWParam, aLParam,
|
||||
aRetValue, aEatMessage);
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
IMEHandler::IsComposing()
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (IsTSFAvailable()) {
|
||||
return nsTextStore::IsComposing();
|
||||
}
|
||||
#endif // #ifdef NS_ENABLE_TSF
|
||||
|
||||
return nsIMM32Handler::IsComposing();
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
IMEHandler::IsComposingOn(nsWindow* aWindow)
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (IsTSFAvailable()) {
|
||||
return nsTextStore::IsComposingOn(aWindow);
|
||||
}
|
||||
#endif // #ifdef NS_ENABLE_TSF
|
||||
|
||||
return nsIMM32Handler::IsComposingOn(aWindow);
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
IMEHandler::NotifyIME(nsWindow* aWindow,
|
||||
NotificationToIME aNotification)
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (IsTSFAvailable()) {
|
||||
switch (aNotification) {
|
||||
case NOTIFY_IME_OF_SELECTION_CHANGE:
|
||||
return nsTextStore::OnSelectionChange();
|
||||
case NOTIFY_IME_OF_FOCUS:
|
||||
return nsTextStore::OnFocusChange(true, aWindow,
|
||||
aWindow->GetInputContext().mIMEState.mEnabled);
|
||||
case NOTIFY_IME_OF_BLUR:
|
||||
return nsTextStore::OnFocusChange(false, aWindow,
|
||||
aWindow->GetInputContext().mIMEState.mEnabled);
|
||||
case REQUEST_TO_COMMIT_COMPOSITION:
|
||||
if (nsTextStore::IsComposingOn(aWindow)) {
|
||||
nsTextStore::CommitComposition(false);
|
||||
}
|
||||
return NS_OK;
|
||||
case REQUEST_TO_CANCEL_COMPOSITION:
|
||||
if (nsTextStore::IsComposingOn(aWindow)) {
|
||||
nsTextStore::CommitComposition(true);
|
||||
}
|
||||
return NS_OK;
|
||||
default:
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
}
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
switch (aNotification) {
|
||||
case REQUEST_TO_COMMIT_COMPOSITION:
|
||||
nsIMM32Handler::CommitComposition(aWindow);
|
||||
return NS_OK;
|
||||
case REQUEST_TO_CANCEL_COMPOSITION:
|
||||
nsIMM32Handler::CancelComposition(aWindow);
|
||||
return NS_OK;
|
||||
#ifdef NS_ENABLE_TSF
|
||||
case NOTIFY_IME_OF_BLUR:
|
||||
// If a plugin gets focus while TSF has focus, we need to notify TSF of
|
||||
// the blur.
|
||||
if (nsTextStore::ThinksHavingFocus()) {
|
||||
return nsTextStore::OnFocusChange(false, aWindow,
|
||||
aWindow->GetInputContext().mIMEState.mEnabled);
|
||||
}
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
#endif //NS_ENABLE_TSF
|
||||
default:
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
IMEHandler::NotifyIMEOfTextChange(uint32_t aStart,
|
||||
uint32_t aOldEnd,
|
||||
uint32_t aNewEnd)
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (IsTSFAvailable()) {
|
||||
return nsTextStore::OnTextChange(aStart, aOldEnd, aNewEnd);
|
||||
}
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// static
|
||||
nsIMEUpdatePreference
|
||||
IMEHandler::GetUpdatePreference()
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (IsTSFAvailable()) {
|
||||
return nsTextStore::GetIMEUpdatePreference();
|
||||
}
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
return nsIMEUpdatePreference(false, false);
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
IMEHandler::GetOpenState(nsWindow* aWindow)
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (IsTSFAvailable()) {
|
||||
return nsTextStore::GetIMEOpenState();
|
||||
}
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
nsIMEContext IMEContext(aWindow->GetWindowHandle());
|
||||
return IMEContext.GetOpenState();
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
IMEHandler::OnDestroyWindow(nsWindow* aWindow)
|
||||
{
|
||||
// We need to do nothing here for TSF. Just restore the default context
|
||||
// if it's been disassociated.
|
||||
nsIMEContext IMEContext(aWindow->GetWindowHandle());
|
||||
IMEContext.AssociateDefaultContext();
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
IMEHandler::SetInputContext(nsWindow* aWindow, InputContext& aInputContext)
|
||||
{
|
||||
// FYI: If there is no composition, this call will do nothing.
|
||||
NotifyIME(aWindow, REQUEST_TO_COMMIT_COMPOSITION);
|
||||
|
||||
// Assume that SetInputContext() is called only when aWindow has focus.
|
||||
sPluginHasFocus = (aInputContext.mIMEState.mEnabled == IMEState::PLUGIN);
|
||||
|
||||
bool enable = IsIMEEnabled(aInputContext);
|
||||
bool adjustOpenState = (enable &&
|
||||
aInputContext.mIMEState.mOpen != IMEState::DONT_CHANGE_OPEN_STATE);
|
||||
bool open = (adjustOpenState &&
|
||||
aInputContext.mIMEState.mOpen == IMEState::OPEN);
|
||||
|
||||
aInputContext.mNativeIMEContext = nullptr;
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
// Note that even while a plugin has focus, we need to notify TSF of that.
|
||||
if (sIsInTSFMode) {
|
||||
nsTextStore::SetInputContext(aInputContext);
|
||||
if (IsTSFAvailable()) {
|
||||
aInputContext.mNativeIMEContext = nsTextStore::GetTextStore();
|
||||
}
|
||||
// Currently, nsTextStore doesn't set focus to keyboard disabled document.
|
||||
// Therefore, we still need to perform the following legacy code.
|
||||
}
|
||||
#endif // #ifdef NS_ENABLE_TSF
|
||||
|
||||
nsIMEContext IMEContext(aWindow->GetWindowHandle());
|
||||
if (enable) {
|
||||
IMEContext.AssociateDefaultContext();
|
||||
if (!aInputContext.mNativeIMEContext) {
|
||||
aInputContext.mNativeIMEContext = static_cast<void*>(IMEContext.get());
|
||||
}
|
||||
} else if (!aWindow->Destroyed()) {
|
||||
// Don't disassociate the context after the window is destroyed.
|
||||
IMEContext.Disassociate();
|
||||
if (!aInputContext.mNativeIMEContext) {
|
||||
// The old InputContext must store the default IMC.
|
||||
aInputContext.mNativeIMEContext =
|
||||
aWindow->GetInputContext().mNativeIMEContext;
|
||||
}
|
||||
}
|
||||
|
||||
if (adjustOpenState) {
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (IsTSFAvailable()) {
|
||||
nsTextStore::SetIMEOpenState(open);
|
||||
return;
|
||||
}
|
||||
#endif // #ifdef NS_ENABLE_TSF
|
||||
IMEContext.SetOpenState(open);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
IMEHandler::InitInputContext(nsWindow* aWindow, InputContext& aInputContext)
|
||||
{
|
||||
// For a11y, the default enabled state should be 'enabled'.
|
||||
aInputContext.mIMEState.mEnabled = IMEState::ENABLED;
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (sIsInTSFMode) {
|
||||
nsTextStore::SetInputContext(aInputContext);
|
||||
aInputContext.mNativeIMEContext = nsTextStore::GetTextStore();
|
||||
MOZ_ASSERT(aInputContext.mNativeIMEContext);
|
||||
return;
|
||||
}
|
||||
#endif // #ifdef NS_ENABLE_TSF
|
||||
|
||||
// NOTE: mNativeIMEContext may be null if IMM module isn't installed.
|
||||
nsIMEContext IMEContext(aWindow->GetWindowHandle());
|
||||
aInputContext.mNativeIMEContext = static_cast<void*>(IMEContext.get());
|
||||
MOZ_ASSERT(aInputContext.mNativeIMEContext || !CurrentKeyboardLayoutHasIME());
|
||||
// If no IME context is available, we should set the widget's pointer since
|
||||
// nullptr indicates there is only one context per process on the platform.
|
||||
if (!aInputContext.mNativeIMEContext) {
|
||||
aInputContext.mNativeIMEContext = static_cast<void*>(aWindow);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// static
|
||||
bool
|
||||
IMEHandler::CurrentKeyboardLayoutHasIME()
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (sIsInTSFMode) {
|
||||
return nsTextStore::CurrentKeyboardLayoutHasIME();
|
||||
}
|
||||
#endif // #ifdef NS_ENABLE_TSF
|
||||
|
||||
return nsIMM32Handler::IsIMEAvailable();
|
||||
}
|
||||
#endif // #ifdef DEBUG
|
||||
|
||||
// static
|
||||
bool
|
||||
IMEHandler::IsDoingKakuteiUndo(HWND aWnd)
|
||||
{
|
||||
// Following message pattern is caused by "Kakutei-Undo" of ATOK or WXG:
|
||||
// ---------------------------------------------------------------------------
|
||||
// WM_KEYDOWN * n (wParam = VK_BACK, lParam = 0x1)
|
||||
// WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC0000001) # ATOK
|
||||
// WM_IME_STARTCOMPOSITION * 1 (wParam = 0x0, lParam = 0x0)
|
||||
// WM_IME_COMPOSITION * 1 (wParam = 0x0, lParam = 0x1BF)
|
||||
// WM_CHAR * n (wParam = VK_BACK, lParam = 0x1)
|
||||
// WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC00E0001)
|
||||
// ---------------------------------------------------------------------------
|
||||
// This doesn't match usual key message pattern such as:
|
||||
// WM_KEYDOWN -> WM_CHAR -> WM_KEYDOWN -> WM_CHAR -> ... -> WM_KEYUP
|
||||
// See following bugs for the detail.
|
||||
// https://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese)
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English)
|
||||
MSG startCompositionMsg, compositionMsg, charMsg;
|
||||
return ::PeekMessageW(&startCompositionMsg, aWnd,
|
||||
WM_IME_STARTCOMPOSITION, WM_IME_STARTCOMPOSITION,
|
||||
PM_NOREMOVE | PM_NOYIELD) &&
|
||||
::PeekMessageW(&compositionMsg, aWnd, WM_IME_COMPOSITION,
|
||||
WM_IME_COMPOSITION, PM_NOREMOVE | PM_NOYIELD) &&
|
||||
::PeekMessageW(&charMsg, aWnd, WM_CHAR, WM_CHAR,
|
||||
PM_NOREMOVE | PM_NOYIELD) &&
|
||||
startCompositionMsg.wParam == 0x0 &&
|
||||
startCompositionMsg.lParam == 0x0 &&
|
||||
compositionMsg.wParam == 0x0 &&
|
||||
compositionMsg.lParam == 0x1BF &&
|
||||
charMsg.wParam == VK_BACK && charMsg.lParam == 0x1 &&
|
||||
startCompositionMsg.time <= compositionMsg.time &&
|
||||
compositionMsg.time <= charMsg.time;
|
||||
}
|
||||
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,137 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef WinIMEHandler_h_
|
||||
#define WinIMEHandler_h_
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsEvent.h"
|
||||
#include "nsIWidget.h"
|
||||
#include <windows.h>
|
||||
|
||||
#define NS_WM_IMEFIRST WM_IME_SETCONTEXT
|
||||
#define NS_WM_IMELAST WM_IME_KEYUP
|
||||
|
||||
class nsWindow;
|
||||
|
||||
namespace mozilla {
|
||||
namespace widget {
|
||||
|
||||
/**
|
||||
* IMEHandler class is a mediator class. On Windows, there are two IME API
|
||||
* sets: One is IMM which is legacy API set. The other is TSF which is modern
|
||||
* API set. By using this class, non-IME handler classes don't need to worry
|
||||
* that we're in which mode.
|
||||
*/
|
||||
class IMEHandler MOZ_FINAL
|
||||
{
|
||||
public:
|
||||
static void Initialize();
|
||||
static void Terminate();
|
||||
|
||||
/**
|
||||
* Returns TSF related native data.
|
||||
*/
|
||||
static void* GetNativeData(uint32_t aDataType);
|
||||
|
||||
/**
|
||||
* Returns true if our message loop can optimize the message order for
|
||||
* a key message or an IME message. Otherwise, false.
|
||||
*/
|
||||
static bool CanOptimizeKeyAndIMEMessages();
|
||||
|
||||
/**
|
||||
* Returns true if the context or IME state is enabled. Otherwise, false.
|
||||
*/
|
||||
static bool IsIMEEnabled(const InputContext& aInputContext);
|
||||
static bool IsIMEEnabled(IMEState::Enabled aIMEState);
|
||||
|
||||
/**
|
||||
* When the message is not needed to handle anymore by the caller, this
|
||||
* returns true. Otherwise, false.
|
||||
* Additionally, if aEatMessage is true, the caller shouldn't call next
|
||||
* wndproc anymore.
|
||||
*/
|
||||
static bool ProcessMessage(nsWindow* aWindow, UINT aMessage,
|
||||
WPARAM& aWParam, LPARAM& aLParam,
|
||||
LRESULT* aRetValue, bool& aEatMessage);
|
||||
|
||||
/**
|
||||
* When there is a composition, returns true. Otherwise, false.
|
||||
*/
|
||||
static bool IsComposing();
|
||||
|
||||
/**
|
||||
* When there is a composition and it's in the window, returns true.
|
||||
* Otherwise, false.
|
||||
*/
|
||||
static bool IsComposingOn(nsWindow* aWindow);
|
||||
|
||||
/**
|
||||
* Notifies IME of the notification (a request or an event).
|
||||
*/
|
||||
static nsresult NotifyIME(nsWindow* aWindow,
|
||||
NotificationToIME aNotification);
|
||||
|
||||
/**
|
||||
* Notifies IME of text change in the focused editable content.
|
||||
*/
|
||||
static nsresult NotifyIMEOfTextChange(uint32_t aStart,
|
||||
uint32_t aOldEnd,
|
||||
uint32_t aNewEnd);
|
||||
|
||||
/**
|
||||
* Returns update preferences.
|
||||
*/
|
||||
static nsIMEUpdatePreference GetUpdatePreference();
|
||||
|
||||
/**
|
||||
* Returns IME open state on the window.
|
||||
*/
|
||||
static bool GetOpenState(nsWindow* aWindow);
|
||||
|
||||
/**
|
||||
* Called when the window is destroying.
|
||||
*/
|
||||
static void OnDestroyWindow(nsWindow* aWindow);
|
||||
|
||||
/**
|
||||
* Called when nsIWidget::SetInputContext() is called before the window's
|
||||
* InputContext is modified actually.
|
||||
*/
|
||||
static void SetInputContext(nsWindow* aWindow, InputContext& aInputContext);
|
||||
|
||||
/**
|
||||
* Called when the window is created.
|
||||
*/
|
||||
static void InitInputContext(nsWindow* aWindow, InputContext& aInputContext);
|
||||
|
||||
/**
|
||||
* "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes
|
||||
* strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern. So, when this
|
||||
* returns true, the caller needs to be careful for processing the messages.
|
||||
*/
|
||||
static bool IsDoingKakuteiUndo(HWND aWnd);
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* Returns true when current keyboard layout has IME. Otherwise, false.
|
||||
*/
|
||||
static bool CurrentKeyboardLayoutHasIME();
|
||||
#endif // #ifdef DEBUG
|
||||
|
||||
private:
|
||||
#ifdef NS_ENABLE_TSF
|
||||
static bool sIsInTSFMode;
|
||||
static bool sPluginHasFocus;
|
||||
|
||||
static bool IsTSFAvailable() { return (sIsInTSFMode && !sPluginHasFocus); }
|
||||
#endif // #ifdef NS_ENABLE_TSF
|
||||
};
|
||||
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // #ifndef WinIMEHandler_h_
|
|
@ -11,7 +11,7 @@
|
|||
#include "WinMouseScrollHandler.h"
|
||||
#include "nsWindowDefs.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIMM32Handler.h"
|
||||
#include "WinIMEHandler.h"
|
||||
#include "mozilla/widget/AudioSession.h"
|
||||
#include "mozilla/HangMonitor.h"
|
||||
|
||||
|
@ -66,7 +66,7 @@ static bool PeekUIMessage(MSG* aMsg)
|
|||
pMsg = &imeMsg;
|
||||
}
|
||||
|
||||
if (pMsg && !nsIMM32Handler::CanOptimizeKeyAndIMEMessages(pMsg)) {
|
||||
if (pMsg && !mozilla::widget::IMEHandler::CanOptimizeKeyAndIMEMessages()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -106,41 +106,6 @@ nsIMM32Handler::IsTopLevelWindowOfComposition(nsWindow* aWindow)
|
|||
return WinUtils::GetTopLevelHWND(wnd, true) == aWindow->GetWindowHandle();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsIMM32Handler::IsDoingKakuteiUndo(HWND aWnd)
|
||||
{
|
||||
// This message pattern is "Kakutei-Undo" on ATOK and WXG.
|
||||
// In this case, the message queue has following messages:
|
||||
// ---------------------------------------------------------------------------
|
||||
// WM_KEYDOWN * n (wParam = VK_BACK, lParam = 0x1)
|
||||
// WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC0000001) # ATOK
|
||||
// WM_IME_STARTCOMPOSITION * 1 (wParam = 0x0, lParam = 0x0)
|
||||
// WM_IME_COMPOSITION * 1 (wParam = 0x0, lParam = 0x1BF)
|
||||
// WM_CHAR * n (wParam = VK_BACK, lParam = 0x1)
|
||||
// WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC00E0001)
|
||||
// ---------------------------------------------------------------------------
|
||||
// This message pattern does not match to the above case;
|
||||
// i.e.,WM_KEYDOWN -> WM_CHAR -> WM_KEYDOWN -> WM_CHAR.
|
||||
// For more information of this problem:
|
||||
// https://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese)
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English)
|
||||
MSG imeStartCompositionMsg, imeCompositionMsg, charMsg;
|
||||
return ::PeekMessageW(&imeStartCompositionMsg, aWnd,
|
||||
WM_IME_STARTCOMPOSITION, WM_IME_STARTCOMPOSITION,
|
||||
PM_NOREMOVE | PM_NOYIELD) &&
|
||||
::PeekMessageW(&imeCompositionMsg, aWnd, WM_IME_COMPOSITION,
|
||||
WM_IME_COMPOSITION, PM_NOREMOVE | PM_NOYIELD) &&
|
||||
::PeekMessageW(&charMsg, aWnd, WM_CHAR, WM_CHAR,
|
||||
PM_NOREMOVE | PM_NOYIELD) &&
|
||||
imeStartCompositionMsg.wParam == 0x0 &&
|
||||
imeStartCompositionMsg.lParam == 0x0 &&
|
||||
imeCompositionMsg.wParam == 0x0 &&
|
||||
imeCompositionMsg.lParam == 0x1BF &&
|
||||
charMsg.wParam == VK_BACK && charMsg.lParam == 0x1 &&
|
||||
imeStartCompositionMsg.time <= imeCompositionMsg.time &&
|
||||
imeCompositionMsg.time <= charMsg.time;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsIMM32Handler::ShouldDrawCompositionStringOurselves()
|
||||
{
|
||||
|
@ -172,7 +137,7 @@ nsIMM32Handler::GetKeyboardCodePage()
|
|||
}
|
||||
|
||||
/* static */ bool
|
||||
nsIMM32Handler::CanOptimizeKeyAndIMEMessages(MSG *aNextKeyOrIMEMessage)
|
||||
nsIMM32Handler::CanOptimizeKeyAndIMEMessages()
|
||||
{
|
||||
// If IME is opening right now, we shouldn't optimize the key and IME message
|
||||
// order because ATOK (Japanese IME of third party) has some problem with the
|
||||
|
@ -249,19 +214,19 @@ nsIMM32Handler::CommitComposition(nsWindow* aWindow, bool aForce)
|
|||
return;
|
||||
}
|
||||
|
||||
bool associated = aWindow->AssociateDefaultIMC(true);
|
||||
nsIMEContext IMEContext(aWindow->GetWindowHandle());
|
||||
bool associated = IMEContext.AssociateDefaultContext();
|
||||
PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
|
||||
("IMM32: CommitComposition, associated=%s\n",
|
||||
associated ? "YES" : "NO"));
|
||||
|
||||
nsIMEContext IMEContext(aWindow->GetWindowHandle());
|
||||
if (IMEContext.IsValid()) {
|
||||
::ImmNotifyIME(IMEContext.get(), NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
|
||||
::ImmNotifyIME(IMEContext.get(), NI_COMPOSITIONSTR, CPS_CANCEL, 0);
|
||||
}
|
||||
|
||||
if (associated) {
|
||||
aWindow->AssociateDefaultIMC(false);
|
||||
IMEContext.Disassociate();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,18 +245,18 @@ nsIMM32Handler::CancelComposition(nsWindow* aWindow, bool aForce)
|
|||
return;
|
||||
}
|
||||
|
||||
bool associated = aWindow->AssociateDefaultIMC(true);
|
||||
nsIMEContext IMEContext(aWindow->GetWindowHandle());
|
||||
bool associated = IMEContext.AssociateDefaultContext();
|
||||
PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
|
||||
("IMM32: CancelComposition, associated=%s\n",
|
||||
associated ? "YES" : "NO"));
|
||||
|
||||
nsIMEContext IMEContext(aWindow->GetWindowHandle());
|
||||
if (IMEContext.IsValid()) {
|
||||
::ImmNotifyIME(IMEContext.get(), NI_COMPOSITIONSTR, CPS_CANCEL, 0);
|
||||
}
|
||||
|
||||
if (associated) {
|
||||
aWindow->AssociateDefaultIMC(false);
|
||||
IMEContext.Disassociate();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,6 @@ class nsIWidget;
|
|||
class nsWindow;
|
||||
struct nsIntRect;
|
||||
|
||||
#define NS_WM_IMEFIRST WM_IME_SETCONTEXT
|
||||
#define NS_WM_IMELAST WM_IME_KEYUP
|
||||
|
||||
class nsIMEContext
|
||||
{
|
||||
public:
|
||||
|
@ -45,6 +42,48 @@ public:
|
|||
return !!mIMC;
|
||||
}
|
||||
|
||||
void SetOpenState(bool aOpen) const
|
||||
{
|
||||
if (!mIMC) {
|
||||
return;
|
||||
}
|
||||
::ImmSetOpenStatus(mIMC, aOpen);
|
||||
}
|
||||
|
||||
bool GetOpenState() const
|
||||
{
|
||||
if (!mIMC) {
|
||||
return false;
|
||||
}
|
||||
return (::ImmGetOpenStatus(mIMC) != FALSE);
|
||||
}
|
||||
|
||||
bool AssociateDefaultContext()
|
||||
{
|
||||
// We assume that there is only default IMC, no new IMC has been created.
|
||||
if (mIMC) {
|
||||
return false;
|
||||
}
|
||||
if (!::ImmAssociateContextEx(mWnd, NULL, IACE_DEFAULT)) {
|
||||
return false;
|
||||
}
|
||||
mIMC = ::ImmGetContext(mWnd);
|
||||
return (mIMC != NULL);
|
||||
}
|
||||
|
||||
bool Disassociate()
|
||||
{
|
||||
if (!mIMC) {
|
||||
return false;
|
||||
}
|
||||
if (!::ImmAssociateContextEx(mWnd, NULL, 0)) {
|
||||
return false;
|
||||
}
|
||||
::ImmReleaseContext(mWnd, mIMC);
|
||||
mIMC = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
nsIMEContext()
|
||||
{
|
||||
|
@ -83,9 +122,7 @@ public:
|
|||
return IsComposing() && IsComposingWindow(aWindow);
|
||||
}
|
||||
|
||||
static bool IsDoingKakuteiUndo(HWND aWnd);
|
||||
|
||||
static bool CanOptimizeKeyAndIMEMessages(MSG *aNextKeyOrIMEMessage);
|
||||
static bool CanOptimizeKeyAndIMEMessages();
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
|
|
|
@ -2817,9 +2817,7 @@ nsTextStore::OnFocusChange(bool aGotFocus,
|
|||
GetIMEEnabledName(aIMEEnabled), sTsfThreadMgr, sTsfTextStore));
|
||||
|
||||
// no change notifications if TSF is disabled
|
||||
if (!sTsfThreadMgr || !sTsfTextStore) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
NS_ENSURE_TRUE(sTsfThreadMgr && sTsfTextStore, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
if (aGotFocus) {
|
||||
bool bRet = sTsfTextStore->Create(aFocusedWidget, aIMEEnabled);
|
||||
|
@ -3179,3 +3177,57 @@ nsTextStore::Terminate(void)
|
|||
NS_RELEASE(sTsfThreadMgr);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// static
|
||||
bool
|
||||
nsTextStore::CurrentKeyboardLayoutHasIME()
|
||||
{
|
||||
// XXX MSDN documents that ITfInputProcessorProfiles is available only on
|
||||
// desktop apps. However, there is no known way to obtain
|
||||
// ITfInputProcessorProfileMgr instance without ITfInputProcessorProfiles
|
||||
// instance.
|
||||
nsRefPtr<ITfInputProcessorProfiles> profiles;
|
||||
HRESULT hr = ::CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_ITfInputProcessorProfiles,
|
||||
getter_AddRefs(profiles));
|
||||
if (FAILED(hr) || !profiles) {
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
|
||||
("TSF: nsTextStore::CurrentKeyboardLayoutHasIME() FAILED to create "
|
||||
"an input processor profiles instance"));
|
||||
return false;
|
||||
}
|
||||
nsRefPtr<ITfInputProcessorProfileMgr> profileMgr;
|
||||
hr = profiles->QueryInterface(IID_ITfInputProcessorProfileMgr,
|
||||
getter_AddRefs(profileMgr));
|
||||
if (FAILED(hr) || !profileMgr) {
|
||||
// On Windows Vista or later, ImmIsIME() API always returns true.
|
||||
// If we failed to obtain the profile manager, we cannot know if current
|
||||
// keyboard layout has IME.
|
||||
if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
|
||||
("TSF: nsTextStore::CurrentKeyboardLayoutHasIME() FAILED to query "
|
||||
"ITfInputProcessorProfileMgr"));
|
||||
return false;
|
||||
}
|
||||
// If the profiles instance doesn't have ITfInputProcessorProfileMgr
|
||||
// interface, that means probably we're running on WinXP or WinServer2003
|
||||
// (except WinServer2003 R2). Then, we should use ImmIsIME().
|
||||
return ::ImmIsIME(::GetKeyboardLayout(0));
|
||||
}
|
||||
|
||||
TF_INPUTPROCESSORPROFILE profile;
|
||||
hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile);
|
||||
if (hr == S_FALSE) {
|
||||
return false; // not found or not active
|
||||
}
|
||||
if (FAILED(hr)) {
|
||||
PR_LOG(sTextStoreLog, PR_LOG_ERROR,
|
||||
("TSF: nsTextStore::CurrentKeyboardLayoutHasIME() FAILED to retreive "
|
||||
"active profile"));
|
||||
return false;
|
||||
}
|
||||
return (profile.dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR);
|
||||
}
|
||||
#endif // #ifdef DEBUG
|
||||
|
|
|
@ -101,13 +101,13 @@ public:
|
|||
|
||||
static void CommitComposition(bool aDiscard)
|
||||
{
|
||||
if (!sTsfTextStore) return;
|
||||
NS_ENSURE_TRUE_VOID(sTsfTextStore);
|
||||
sTsfTextStore->CommitCompositionInternal(aDiscard);
|
||||
}
|
||||
|
||||
static void SetInputContext(const InputContext& aContext)
|
||||
{
|
||||
if (!sTsfTextStore) return;
|
||||
NS_ENSURE_TRUE_VOID(sTsfTextStore);
|
||||
sTsfTextStore->SetInputScope(aContext.mHTMLInputType);
|
||||
sTsfTextStore->SetInputContextInternal(aContext.mIMEState.mEnabled);
|
||||
}
|
||||
|
@ -119,13 +119,13 @@ public:
|
|||
uint32_t aOldEnd,
|
||||
uint32_t aNewEnd)
|
||||
{
|
||||
if (!sTsfTextStore) return NS_OK;
|
||||
NS_ENSURE_TRUE(sTsfTextStore, NS_ERROR_NOT_AVAILABLE);
|
||||
return sTsfTextStore->OnTextChangeInternal(aStart, aOldEnd, aNewEnd);
|
||||
}
|
||||
|
||||
static void OnTextChangeMsg(void)
|
||||
{
|
||||
if (!sTsfTextStore) return;
|
||||
NS_ENSURE_TRUE_VOID(sTsfTextStore);
|
||||
// Notify TSF text change
|
||||
// (see comments on WM_USER_TSF_TEXTCHANGE in nsTextStore.h)
|
||||
sTsfTextStore->OnTextChangeMsgInternal();
|
||||
|
@ -133,7 +133,7 @@ public:
|
|||
|
||||
static nsresult OnSelectionChange(void)
|
||||
{
|
||||
if (!sTsfTextStore) return NS_OK;
|
||||
NS_ENSURE_TRUE(sTsfTextStore, NS_ERROR_NOT_AVAILABLE);
|
||||
return sTsfTextStore->OnSelectionChangeInternal();
|
||||
}
|
||||
|
||||
|
@ -145,24 +145,59 @@ public:
|
|||
ts->OnCompositionTimer();
|
||||
}
|
||||
|
||||
static bool CanOptimizeKeyAndIMEMessages()
|
||||
{
|
||||
// TODO: We need to implement this for ATOK.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns the address of the pointer so that the TSF automatic test can
|
||||
// replace the system object with a custom implementation for testing.
|
||||
static void* GetThreadMgr(void)
|
||||
static void* GetNativeData(uint32_t aDataType)
|
||||
{
|
||||
Initialize(); // Apply any previous changes
|
||||
return (void*) & sTsfThreadMgr;
|
||||
switch (aDataType) {
|
||||
case NS_NATIVE_TSF_THREAD_MGR:
|
||||
Initialize(); // Apply any previous changes
|
||||
return static_cast<void*>(&sTsfThreadMgr);
|
||||
case NS_NATIVE_TSF_CATEGORY_MGR:
|
||||
return static_cast<void*>(&sCategoryMgr);
|
||||
case NS_NATIVE_TSF_DISPLAY_ATTR_MGR:
|
||||
return static_cast<void*>(&sDisplayAttrMgr);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void* GetCategoryMgr(void)
|
||||
static void* GetTextStore()
|
||||
{
|
||||
return (void*) & sCategoryMgr;
|
||||
return static_cast<void*>(sTsfTextStore);
|
||||
}
|
||||
|
||||
static void* GetDisplayAttrMgr(void)
|
||||
static bool ThinksHavingFocus()
|
||||
{
|
||||
return (void*) & sDisplayAttrMgr;
|
||||
return (sTsfTextStore && sTsfTextStore->mContext);
|
||||
}
|
||||
|
||||
static bool IsInTSFMode()
|
||||
{
|
||||
return sTsfThreadMgr != nullptr;
|
||||
}
|
||||
|
||||
static bool IsComposing()
|
||||
{
|
||||
return (sTsfTextStore && sTsfTextStore->mCompositionView != nullptr);
|
||||
}
|
||||
|
||||
static bool IsComposingOn(nsWindowBase* aWidget)
|
||||
{
|
||||
return (IsComposing() && sTsfTextStore->mWidget == aWidget);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// Returns true when keyboard layout has IME (TIP).
|
||||
static bool CurrentKeyboardLayoutHasIME();
|
||||
#endif // #ifdef DEBUG
|
||||
|
||||
protected:
|
||||
nsTextStore();
|
||||
~nsTextStore();
|
||||
|
|
|
@ -153,10 +153,6 @@
|
|||
#include "nsIWinTaskbar.h"
|
||||
#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
|
||||
|
||||
#if defined(NS_ENABLE_TSF)
|
||||
#include "nsTextStore.h"
|
||||
#endif // defined(NS_ENABLE_TSF)
|
||||
|
||||
// Windowless plugin support
|
||||
#include "npapi.h"
|
||||
|
||||
|
@ -168,7 +164,7 @@
|
|||
#include "nsIContent.h"
|
||||
|
||||
#include "mozilla/HangMonitor.h"
|
||||
#include "nsIMM32Handler.h"
|
||||
#include "WinIMEHandler.h"
|
||||
|
||||
using namespace mozilla::widget;
|
||||
using namespace mozilla::layers;
|
||||
|
@ -362,11 +358,7 @@ nsWindow::nsWindow() : nsWindowBase()
|
|||
// WinTaskbar.cpp for details.
|
||||
mozilla::widget::WinTaskbar::RegisterAppUserModelID();
|
||||
gKbdLayout.LoadLayout(::GetKeyboardLayout(0));
|
||||
// Init IME handler
|
||||
nsIMM32Handler::Initialize();
|
||||
#ifdef NS_ENABLE_TSF
|
||||
nsTextStore::Initialize();
|
||||
#endif
|
||||
IMEHandler::Initialize();
|
||||
if (SUCCEEDED(::OleInitialize(NULL))) {
|
||||
sIsOleInitialized = TRUE;
|
||||
}
|
||||
|
@ -406,17 +398,13 @@ nsWindow::~nsWindow()
|
|||
|
||||
// Global shutdown
|
||||
if (sInstanceCount == 0) {
|
||||
#ifdef NS_ENABLE_TSF
|
||||
nsTextStore::Terminate();
|
||||
#endif
|
||||
IMEHandler::Terminate();
|
||||
NS_IF_RELEASE(sCursorImgContainer);
|
||||
if (sIsOleInitialized) {
|
||||
::OleFlushClipboard();
|
||||
::OleUninitialize();
|
||||
sIsOleInitialized = FALSE;
|
||||
}
|
||||
// delete any of the IME structures that we allocated
|
||||
nsIMM32Handler::Terminate();
|
||||
}
|
||||
|
||||
NS_IF_RELEASE(mNativeDragTarget);
|
||||
|
@ -596,16 +584,7 @@ nsWindow::Create(nsIWidget *aParent,
|
|||
|
||||
SubclassWindow(TRUE);
|
||||
|
||||
// NOTE: mNativeIMEContext may be null if IMM module isn't installed.
|
||||
nsIMEContext IMEContext(mWnd);
|
||||
mInputContext.mNativeIMEContext = static_cast<void*>(IMEContext.get());
|
||||
MOZ_ASSERT(mInputContext.mNativeIMEContext ||
|
||||
!nsIMM32Handler::IsIMEAvailable());
|
||||
// If no IME context is available, we should set this widget's pointer since
|
||||
// nullptr indicates there is only one context per process on the platform.
|
||||
if (!mInputContext.mNativeIMEContext) {
|
||||
mInputContext.mNativeIMEContext = this;
|
||||
}
|
||||
IMEHandler::InitInputContext(this, mInputContext);
|
||||
|
||||
// If the internal variable set by the config.trim_on_minimize pref has not
|
||||
// been initialized, and if this is the hidden window (conveniently created
|
||||
|
@ -2867,14 +2846,10 @@ void* nsWindow::GetNativeData(uint32_t aDataType)
|
|||
return (void*)::GetDC(mWnd);
|
||||
#endif
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
case NS_NATIVE_TSF_THREAD_MGR:
|
||||
return nsTextStore::GetThreadMgr();
|
||||
case NS_NATIVE_TSF_CATEGORY_MGR:
|
||||
return nsTextStore::GetCategoryMgr();
|
||||
case NS_NATIVE_TSF_DISPLAY_ATTR_MGR:
|
||||
return nsTextStore::GetDisplayAttrMgr();
|
||||
#endif //NS_ENABLE_TSF
|
||||
return IMEHandler::GetNativeData(aDataType);
|
||||
|
||||
default:
|
||||
break;
|
||||
|
@ -4509,8 +4484,8 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam,
|
|||
#endif
|
||||
|
||||
bool eatMessage;
|
||||
if (nsIMM32Handler::ProcessMessage(this, msg, wParam, lParam, aRetValue,
|
||||
eatMessage)) {
|
||||
if (IMEHandler::ProcessMessage(this, msg, wParam, lParam, aRetValue,
|
||||
eatMessage)) {
|
||||
return mWnd ? eatMessage : true;
|
||||
}
|
||||
|
||||
|
@ -5432,11 +5407,6 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam,
|
|||
|
||||
default:
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (msg == WM_USER_TSF_TEXTCHANGE) {
|
||||
nsTextStore::OnTextChangeMsg();
|
||||
}
|
||||
#endif //NS_ENABLE_TSF
|
||||
if (msg == nsAppShell::GetTaskbarButtonCreatedMessage())
|
||||
SetHasTaskbarIconBeenCreated();
|
||||
if (msg == sOOPPPluginFocusEvent) {
|
||||
|
@ -5707,7 +5677,7 @@ LRESULT nsWindow::ProcessKeyUpMessage(const MSG &aMsg, bool *aEventDispatched)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
if (!nsIMM32Handler::IsComposingOn(this)) {
|
||||
if (!IMEHandler::IsComposingOn(this)) {
|
||||
return OnKeyUp(aMsg, modKeyState, aEventDispatched);
|
||||
}
|
||||
|
||||
|
@ -5746,7 +5716,7 @@ LRESULT nsWindow::ProcessKeyDownMessage(const MSG &aMsg,
|
|||
return FALSE;
|
||||
|
||||
LRESULT result = 0;
|
||||
if (!nsIMM32Handler::IsComposingOn(this)) {
|
||||
if (!IMEHandler::IsComposingOn(this)) {
|
||||
result = OnKeyDown(aMsg, modKeyState, aEventDispatched, nullptr);
|
||||
// OnKeyDown cleaned up the redirected message information itself, so,
|
||||
// we should do nothing.
|
||||
|
@ -6501,7 +6471,7 @@ LRESULT nsWindow::OnKeyDown(const MSG &aMsg,
|
|||
static bool sRedirectedKeyDownEventPreventedDefault = false;
|
||||
bool noDefault;
|
||||
if (aFakeCharMessage || !IsRedirectedKeyDownMessage(aMsg)) {
|
||||
nsIMEContext IMEContext(mWnd);
|
||||
bool isIMEEnabled = IMEHandler::IsIMEEnabled(mInputContext);
|
||||
nsKeyEvent keydownEvent(true, NS_KEY_DOWN, this);
|
||||
keydownEvent.keyCode = DOMKeyCode;
|
||||
InitKeyEvent(keydownEvent, nativeKey, aModKeyState);
|
||||
|
@ -6519,9 +6489,8 @@ LRESULT nsWindow::OnKeyDown(const MSG &aMsg,
|
|||
// application, we shouldn't redirect the message to it because the keydown
|
||||
// message is processed by us, so, nobody shouldn't process it.
|
||||
HWND focusedWnd = ::GetFocus();
|
||||
nsIMEContext newIMEContext(mWnd);
|
||||
if (!noDefault && !aFakeCharMessage && focusedWnd && !PluginHasFocus() &&
|
||||
!IMEContext.get() && newIMEContext.get()) {
|
||||
!isIMEEnabled && IMEHandler::IsIMEEnabled(mInputContext)) {
|
||||
RemoveNextCharMessage(focusedWnd);
|
||||
|
||||
INPUT keyinput;
|
||||
|
@ -6617,7 +6586,7 @@ LRESULT nsWindow::OnKeyDown(const MSG &aMsg,
|
|||
}
|
||||
|
||||
if (!anyCharMessagesRemoved && DOMKeyCode == NS_VK_BACK &&
|
||||
nsIMM32Handler::IsDoingKakuteiUndo(mWnd)) {
|
||||
IMEHandler::IsDoingKakuteiUndo(mWnd)) {
|
||||
NS_ASSERTION(!aFakeCharMessage,
|
||||
"We shouldn't be touching the real msg queue");
|
||||
RemoveMessageAndDispatchPluginEvent(WM_CHAR, WM_CHAR);
|
||||
|
@ -6891,7 +6860,7 @@ LRESULT nsWindow::OnChar(const MSG &aMsg,
|
|||
modKeyState.Unset(MODIFIER_ALT | MODIFIER_CONTROL);
|
||||
}
|
||||
|
||||
if (nsIMM32Handler::IsComposingOn(this)) {
|
||||
if (IMEHandler::IsComposingOn(this)) {
|
||||
ResetInputState();
|
||||
}
|
||||
|
||||
|
@ -7212,8 +7181,7 @@ void nsWindow::OnDestroy()
|
|||
CaptureRollupEvents(nullptr, false);
|
||||
}
|
||||
|
||||
// Restore the IM context.
|
||||
AssociateDefaultIMC(true);
|
||||
IMEHandler::OnDestroyWindow(this);
|
||||
|
||||
// Turn off mouse trails if enabled.
|
||||
MouseTrailer* mtrailer = nsToolkit::gMouseTrailer;
|
||||
|
@ -7396,94 +7364,33 @@ nsWindow::OnSysColorChanged()
|
|||
|
||||
NS_IMETHODIMP nsWindow::ResetInputState()
|
||||
{
|
||||
#ifdef DEBUG_KBSTATE
|
||||
PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("ResetInputState\n"));
|
||||
#endif
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
nsTextStore::CommitComposition(false);
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
nsIMM32Handler::CommitComposition(this);
|
||||
return NS_OK;
|
||||
return IMEHandler::NotifyIME(this, REQUEST_TO_COMMIT_COMPOSITION);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(void)
|
||||
nsWindow::SetInputContext(const InputContext& aContext,
|
||||
const InputContextAction& aAction)
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
nsTextStore::SetInputContext(aContext);
|
||||
#endif //NS_ENABLE_TSF
|
||||
if (nsIMM32Handler::IsComposing()) {
|
||||
ResetInputState();
|
||||
}
|
||||
void* nativeIMEContext = mInputContext.mNativeIMEContext;
|
||||
mInputContext = aContext;
|
||||
mInputContext.mNativeIMEContext = nullptr;
|
||||
bool enable = (mInputContext.mIMEState.mEnabled == IMEState::ENABLED ||
|
||||
mInputContext.mIMEState.mEnabled == IMEState::PLUGIN);
|
||||
|
||||
AssociateDefaultIMC(enable);
|
||||
|
||||
if (enable) {
|
||||
nsIMEContext IMEContext(mWnd);
|
||||
mInputContext.mNativeIMEContext = static_cast<void*>(IMEContext.get());
|
||||
}
|
||||
// Restore the latest associated context when we cannot get actual context.
|
||||
if (!mInputContext.mNativeIMEContext) {
|
||||
mInputContext.mNativeIMEContext = nativeIMEContext;
|
||||
}
|
||||
|
||||
if (enable &&
|
||||
mInputContext.mIMEState.mOpen != IMEState::DONT_CHANGE_OPEN_STATE) {
|
||||
bool open = (mInputContext.mIMEState.mOpen == IMEState::OPEN);
|
||||
#ifdef NS_ENABLE_TSF
|
||||
nsTextStore::SetIMEOpenState(open);
|
||||
#endif //NS_ENABLE_TSF
|
||||
nsIMEContext IMEContext(mWnd);
|
||||
if (IMEContext.IsValid()) {
|
||||
::ImmSetOpenStatus(IMEContext.get(), open);
|
||||
}
|
||||
}
|
||||
InputContext newInputContext = aContext;
|
||||
IMEHandler::SetInputContext(this, newInputContext);
|
||||
mInputContext = newInputContext;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(InputContext)
|
||||
nsWindow::GetInputContext()
|
||||
{
|
||||
mInputContext.mIMEState.mOpen = IMEState::CLOSED;
|
||||
switch (mInputContext.mIMEState.mEnabled) {
|
||||
case IMEState::ENABLED:
|
||||
case IMEState::PLUGIN: {
|
||||
nsIMEContext IMEContext(mWnd);
|
||||
if (IMEContext.IsValid()) {
|
||||
mInputContext.mIMEState.mOpen =
|
||||
::ImmGetOpenStatus(IMEContext.get()) ? IMEState::OPEN :
|
||||
IMEState::CLOSED;
|
||||
}
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (mInputContext.mIMEState.mOpen == IMEState::CLOSED &&
|
||||
nsTextStore::GetIMEOpenState()) {
|
||||
mInputContext.mIMEState.mOpen = IMEState::OPEN;
|
||||
}
|
||||
#endif //NS_ENABLE_TSF
|
||||
}
|
||||
if (IMEHandler::IsIMEEnabled(mInputContext) && IMEHandler::GetOpenState(this)) {
|
||||
mInputContext.mIMEState.mOpen = IMEState::OPEN;
|
||||
} else {
|
||||
mInputContext.mIMEState.mOpen = IMEState::CLOSED;
|
||||
}
|
||||
return mInputContext;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsWindow::CancelIMEComposition()
|
||||
{
|
||||
#ifdef DEBUG_KBSTATE
|
||||
PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("CancelIMEComposition\n"));
|
||||
#endif
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
nsTextStore::CommitComposition(true);
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
nsIMM32Handler::CancelComposition(this);
|
||||
return NS_OK;
|
||||
return IMEHandler::NotifyIME(this, REQUEST_TO_CANCEL_COMPOSITION);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -7497,15 +7404,11 @@ nsWindow::GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
NS_IMETHODIMP
|
||||
nsWindow::OnIMEFocusChange(bool aFocus)
|
||||
{
|
||||
nsresult rv = nsTextStore::OnFocusChange(aFocus, this,
|
||||
mInputContext.mIMEState.mEnabled);
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE)
|
||||
rv = NS_OK; // TSF is not enabled, maybe.
|
||||
return rv;
|
||||
return IMEHandler::NotifyIME(this, aFocus ? NOTIFY_IME_OF_FOCUS :
|
||||
NOTIFY_IME_OF_BLUR);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -7513,57 +7416,19 @@ nsWindow::OnIMETextChange(uint32_t aStart,
|
|||
uint32_t aOldEnd,
|
||||
uint32_t aNewEnd)
|
||||
{
|
||||
return nsTextStore::OnTextChange(aStart, aOldEnd, aNewEnd);
|
||||
return IMEHandler::NotifyIMEOfTextChange(aStart, aOldEnd, aNewEnd);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindow::OnIMESelectionChange(void)
|
||||
{
|
||||
return nsTextStore::OnSelectionChange();
|
||||
return IMEHandler::NotifyIME(this, NOTIFY_IME_OF_SELECTION_CHANGE);
|
||||
}
|
||||
|
||||
nsIMEUpdatePreference
|
||||
nsWindow::GetIMEUpdatePreference()
|
||||
{
|
||||
return nsTextStore::GetIMEUpdatePreference();
|
||||
}
|
||||
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
bool nsWindow::AssociateDefaultIMC(bool aAssociate)
|
||||
{
|
||||
nsIMEContext IMEContext(mWnd);
|
||||
|
||||
if (aAssociate) {
|
||||
BOOL ret = ::ImmAssociateContextEx(mWnd, NULL, IACE_DEFAULT);
|
||||
#ifdef DEBUG
|
||||
// Note that if IME isn't available with current keyboard layout,
|
||||
// IMM might not be installed on the system such as English Windows.
|
||||
// On such system, IMM APIs always fail.
|
||||
NS_ASSERTION(ret || !nsIMM32Handler::IsIMEAvailable(),
|
||||
"ImmAssociateContextEx failed to restore default IMC");
|
||||
if (ret) {
|
||||
nsIMEContext newIMEContext(mWnd);
|
||||
NS_ASSERTION(!IMEContext.get() || newIMEContext.get() == IMEContext.get(),
|
||||
"Unknown IMC had been associated");
|
||||
}
|
||||
#endif
|
||||
return ret && !IMEContext.get();
|
||||
}
|
||||
|
||||
if (mOnDestroyCalled) {
|
||||
// If OnDestroy() has been called, we shouldn't disassociate the default
|
||||
// IMC at destroying the window.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IMEContext.get()) {
|
||||
return false; // already disassociated
|
||||
}
|
||||
|
||||
BOOL ret = ::ImmAssociateContextEx(mWnd, NULL, 0);
|
||||
NS_ASSERTION(ret, "ImmAssociateContextEx failed to disassociate the IMC");
|
||||
return ret != FALSE;
|
||||
return IMEHandler::GetUpdatePreference();
|
||||
}
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
|
|
|
@ -176,12 +176,10 @@ public:
|
|||
virtual nsTransparencyMode GetTransparencyMode();
|
||||
virtual void UpdateOpaqueRegion(const nsIntRegion& aOpaqueRegion);
|
||||
#endif // MOZ_XUL
|
||||
#ifdef NS_ENABLE_TSF
|
||||
NS_IMETHOD OnIMEFocusChange(bool aFocus);
|
||||
NS_IMETHOD OnIMETextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd);
|
||||
NS_IMETHOD OnIMESelectionChange(void);
|
||||
virtual nsIMEUpdatePreference GetIMEUpdatePreference();
|
||||
#endif // NS_ENABLE_TSF
|
||||
NS_IMETHOD GetNonClientMargins(nsIntMargin &margins);
|
||||
NS_IMETHOD SetNonClientMargins(nsIntMargin &margins);
|
||||
void SetDrawsInTitlebar(bool aState);
|
||||
|
|
|
@ -20,12 +20,6 @@ using namespace ABI::Windows::Foundation;
|
|||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
|
||||
// XXX move
|
||||
#pragma comment(lib, "dwrite.lib")
|
||||
#pragma comment(lib, "d2d1.lib")
|
||||
#pragma comment(lib, "d3d11.lib")
|
||||
#pragma comment(lib, "runtimeobject.lib")
|
||||
|
||||
// Metro specific XRE methods we call from here on an
|
||||
// appropriate thread.
|
||||
extern nsresult XRE_metroStartup();
|
||||
|
|
|
@ -1025,11 +1025,9 @@ MetroWidget::GetNativeData(uint32_t aDataType)
|
|||
}
|
||||
break;
|
||||
case NS_NATIVE_TSF_THREAD_MGR:
|
||||
return nsTextStore::GetThreadMgr();
|
||||
case NS_NATIVE_TSF_CATEGORY_MGR:
|
||||
return nsTextStore::GetCategoryMgr();
|
||||
case NS_NATIVE_TSF_DISPLAY_ATTR_MGR:
|
||||
return nsTextStore::GetDisplayAttrMgr();
|
||||
return nsTextStore::GetNativeData(aDataType);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче