This commit is contained in:
Ryan VanderMeulen 2013-12-13 16:18:16 -05:00
Родитель d049d3bf34 218cec4598
Коммит e22b5c232b
471 изменённых файлов: 11578 добавлений и 6333 удалений

Просмотреть файл

@ -18,4 +18,9 @@
# Modifying this file will now automatically clobber the buildbot machines \o/ # Modifying this file will now automatically clobber the buildbot machines \o/
# #
Bug 887836 - webidl changes require a Windows clobber. # Are you updating CLOBBER because you think it's needed for your WebIDL
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 928195 rewrote WebIDL build system integration from the ground up. This
will hopefully be the last required clobber due to WebIDLs poorly interacting
with the build system.

Просмотреть файл

@ -548,14 +548,34 @@ HyperTextAccessible::DOMPointToHypertextOffset(nsINode* aNode,
return nullptr; return nullptr;
} }
nsresult bool
HyperTextAccessible::HypertextOffsetsToDOMRange(int32_t aStartHTOffset, HyperTextAccessible::OffsetsToDOMRange(int32_t aStartOffset, int32_t aEndOffset,
int32_t aEndHTOffset,
nsRange* aRange) nsRange* aRange)
{ {
// If the given offsets are 0 and associated editor is empty then return DOMPoint startPoint = OffsetToDOMPoint(aStartOffset);
// collapsed range with editor root element as range container. if (!startPoint.node)
if (aStartHTOffset == 0 && aEndHTOffset == 0) { return false;
aRange->SetStart(startPoint.node, startPoint.idx);
if (aStartOffset == aEndOffset) {
aRange->SetEnd(startPoint.node, startPoint.idx);
return true;
}
DOMPoint endPoint = OffsetToDOMPoint(aEndOffset);
if (!endPoint.node)
return false;
aRange->SetEnd(endPoint.node, endPoint.idx);
return true;
}
DOMPoint
HyperTextAccessible::OffsetToDOMPoint(int32_t aOffset)
{
// 0 offset is valid even if no children. In this case the associated editor
// is empty so return a DOM point for editor root element.
if (aOffset == 0) {
nsCOMPtr<nsIEditor> editor = GetEditor(); nsCOMPtr<nsIEditor> editor = GetEditor();
if (editor) { if (editor) {
bool isEmpty = false; bool isEmpty = false;
@ -565,40 +585,36 @@ HyperTextAccessible::HypertextOffsetsToDOMRange(int32_t aStartHTOffset,
editor->GetRootElement(getter_AddRefs(editorRootElm)); editor->GetRootElement(getter_AddRefs(editorRootElm));
nsCOMPtr<nsINode> editorRoot(do_QueryInterface(editorRootElm)); nsCOMPtr<nsINode> editorRoot(do_QueryInterface(editorRootElm));
if (editorRoot) { return DOMPoint(editorRoot, 0);
aRange->SetStart(editorRoot, 0);
aRange->SetEnd(editorRoot, 0);
return NS_OK;
} }
} }
} }
int32_t childIdx = GetChildIndexAtOffset(aOffset);
if (childIdx == -1)
return DOMPoint();
Accessible* child = GetChildAt(childIdx);
int32_t innerOffset = aOffset - GetChildOffset(childIdx);
// A text leaf case. The point is inside the text node.
if (child->IsTextLeaf()) {
nsIContent* content = child->GetContent();
int32_t idx = 0;
if (NS_FAILED(RenderedToContentOffset(content->GetPrimaryFrame(),
innerOffset, &idx)))
return DOMPoint();
return DOMPoint(content, idx);
} }
nsRefPtr<Accessible> startAcc, endAcc; // Case of embedded object. The point is either before or after the element.
int32_t startOffset = aStartHTOffset, endOffset = aEndHTOffset; NS_ASSERTION(innerOffset == 0 || innerOffset == 1, "A wrong inner offset!");
nsIFrame *startFrame = nullptr, *endFrame = nullptr; nsINode* node = child->GetNode();
nsINode* parentNode = node->GetParentNode();
startFrame = GetPosAndText(startOffset, endOffset, nullptr, &endFrame, return parentNode ?
getter_AddRefs(startAcc), getter_AddRefs(endAcc)); DOMPoint(parentNode, parentNode->IndexOf(node) + innerOffset) :
if (!startAcc || !endAcc) DOMPoint();
return NS_ERROR_FAILURE;
DOMPoint startPoint, endPoint;
nsresult rv = GetDOMPointByFrameOffset(startFrame, startOffset, startAcc,
&startPoint);
NS_ENSURE_SUCCESS(rv, rv);
rv = aRange->SetStart(startPoint.node, startPoint.idx);
NS_ENSURE_SUCCESS(rv, rv);
if (aStartHTOffset == aEndHTOffset)
return aRange->SetEnd(startPoint.node, startPoint.idx);
rv = GetDOMPointByFrameOffset(endFrame, endOffset, endAcc, &endPoint);
NS_ENSURE_SUCCESS(rv, rv);
return aRange->SetEnd(endPoint.node, endPoint.idx);
} }
int32_t int32_t
@ -1580,7 +1596,8 @@ HyperTextAccessible::SetSelectionBoundsAt(int32_t aSelectionNum,
if (!range) if (!range)
return false; return false;
HypertextOffsetsToDOMRange(startOffset, endOffset, range); if (!OffsetsToDOMRange(startOffset, endOffset, range))
return false;
// If new range was created then add it, otherwise notify selection listeners // If new range was created then add it, otherwise notify selection listeners
// that existing selection range was changed. // that existing selection range was changed.
@ -1610,8 +1627,7 @@ HyperTextAccessible::ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset,
uint32_t aScrollType) uint32_t aScrollType)
{ {
nsRefPtr<nsRange> range = new nsRange(mContent); nsRefPtr<nsRange> range = new nsRange(mContent);
nsresult rv = HypertextOffsetsToDOMRange(aStartOffset, aEndOffset, range); if (OffsetsToDOMRange(aStartOffset, aEndOffset, range))
if (NS_SUCCEEDED(rv))
nsCoreUtils::ScrollSubstringTo(GetFrame(), range, aScrollType); nsCoreUtils::ScrollSubstringTo(GetFrame(), range, aScrollType);
} }
@ -1629,8 +1645,7 @@ HyperTextAccessible::ScrollSubstringToPoint(int32_t aStartOffset,
this); this);
nsRefPtr<nsRange> range = new nsRange(mContent); nsRefPtr<nsRange> range = new nsRange(mContent);
nsresult rv = HypertextOffsetsToDOMRange(aStartOffset, aEndOffset, range); if (!OffsetsToDOMRange(aStartOffset, aEndOffset, range))
if (NS_FAILED(rv))
return; return;
nsPresContext* presContext = frame->PresContext(); nsPresContext* presContext = frame->PresContext();
@ -1658,7 +1673,7 @@ HyperTextAccessible::ScrollSubstringToPoint(int32_t aStartOffset,
int16_t hPercent = offsetPointX * 100 / size.width; int16_t hPercent = offsetPointX * 100 / size.width;
int16_t vPercent = offsetPointY * 100 / size.height; int16_t vPercent = offsetPointY * 100 / size.height;
rv = nsCoreUtils::ScrollSubstringTo(frame, range, vPercent, hPercent); nsresult rv = nsCoreUtils::ScrollSubstringTo(frame, range, vPercent, hPercent);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return; return;

Просмотреть файл

@ -17,6 +17,9 @@ namespace mozilla {
namespace a11y { namespace a11y {
struct DOMPoint { struct DOMPoint {
DOMPoint() : node(nullptr), idx(0) { }
DOMPoint(nsINode* aNode, int32_t aIdx) : node(aNode), idx(aIdx) { }
nsINode* node; nsINode* node;
int32_t idx; int32_t idx;
}; };
@ -128,16 +131,25 @@ public:
bool aIsEndOffset = false) const; bool aIsEndOffset = false) const;
/** /**
* Turn a start and end hypertext offsets into DOM range. * Convert start and end hypertext offsets into DOM range.
* *
* @param aStartHTOffset [in] the given start hypertext offset * @param aStartOffset [in] the given start hypertext offset
* @param aEndHTOffset [in] the given end hypertext offset * @param aEndOffset [in] the given end hypertext offset
* @param aRange [out] the range whose bounds to set * @param aRange [in, out] the range whose bounds to set
* @return true if conversion was successful
*/ */
nsresult HypertextOffsetsToDOMRange(int32_t aStartHTOffset, bool OffsetsToDOMRange(int32_t aStartOffset, int32_t aEndOffset,
int32_t aEndHTOffset,
nsRange* aRange); nsRange* aRange);
/**
* Convert the given offset into DOM point.
*
* If offset is at text leaf then DOM point is (text node, offsetInTextNode),
* if before embedded object then (parent node, indexInParent), if after then
* (parent node, indexInParent + 1).
*/
DOMPoint OffsetToDOMPoint(int32_t aOffset);
/** /**
* Return true if the used ARIA role (if any) allows the hypertext accessible * Return true if the used ARIA role (if any) allows the hypertext accessible
* to expose text interfaces. * to expose text interfaces.

Просмотреть файл

@ -118,8 +118,8 @@ this.AccessFu = {
Output.start(); Output.start();
TouchAdapter.start(); TouchAdapter.start();
Services.obs.addObserver(this, 'remote-browser-frame-shown', false); Services.obs.addObserver(this, 'remote-browser-shown', false);
Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false); Services.obs.addObserver(this, 'inprocess-browser-shown', false);
Services.obs.addObserver(this, 'Accessibility:NextObject', false); Services.obs.addObserver(this, 'Accessibility:NextObject', false);
Services.obs.addObserver(this, 'Accessibility:PreviousObject', false); Services.obs.addObserver(this, 'Accessibility:PreviousObject', false);
Services.obs.addObserver(this, 'Accessibility:Focus', false); Services.obs.addObserver(this, 'Accessibility:Focus', false);
@ -162,8 +162,8 @@ this.AccessFu = {
Utils.win.removeEventListener('TabClose', this); Utils.win.removeEventListener('TabClose', this);
Utils.win.removeEventListener('TabSelect', this); Utils.win.removeEventListener('TabSelect', this);
Services.obs.removeObserver(this, 'remote-browser-frame-shown'); Services.obs.removeObserver(this, 'remote-browser-shown');
Services.obs.removeObserver(this, 'in-process-browser-or-app-frame-shown'); Services.obs.removeObserver(this, 'inprocess-browser-shown');
Services.obs.removeObserver(this, 'Accessibility:NextObject'); Services.obs.removeObserver(this, 'Accessibility:NextObject');
Services.obs.removeObserver(this, 'Accessibility:PreviousObject'); Services.obs.removeObserver(this, 'Accessibility:PreviousObject');
Services.obs.removeObserver(this, 'Accessibility:Focus'); Services.obs.removeObserver(this, 'Accessibility:Focus');
@ -304,11 +304,15 @@ this.AccessFu = {
case 'Accessibility:MoveByGranularity': case 'Accessibility:MoveByGranularity':
this.Input.moveByGranularity(JSON.parse(aData)); this.Input.moveByGranularity(JSON.parse(aData));
break; break;
case 'remote-browser-frame-shown': case 'remote-browser-shown':
case 'in-process-browser-or-app-frame-shown': case 'inprocess-browser-shown':
{ {
let mm = aSubject.QueryInterface(Ci.nsIFrameLoader).messageManager; // Ignore notifications that aren't from a BrowserOrApp
this._handleMessageManager(mm); let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader);
if (!frameLoader.ownerIsBrowserOrAppFrame) {
return;
}
this._handleMessageManager(frameLoader.messageManager);
break; break;
} }
} }

Просмотреть файл

@ -856,3 +856,9 @@ pref("osfile.reset_worker_delay", 5000);
// The URL of the Firefox Accounts auth server backend // The URL of the Firefox Accounts auth server backend
pref("identity.fxaccounts.auth.uri", "https://api-accounts.dev.lcip.org/v1"); pref("identity.fxaccounts.auth.uri", "https://api-accounts.dev.lcip.org/v1");
// APZC preferences.
//
// Gaia relies heavily on scroll events for now, so lets fire them
// more often than the default value (100).
pref("apz.asyncscroll.throttle", 40);

Просмотреть файл

@ -0,0 +1,131 @@
/* 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/. */
/*
* This defines the look-and-feel styling of the error pages.
* (see: netError.xhtml)
*
* Original styling by William Price <bugzilla@mob.rice.edu>
* Updated for mobile by: Wes Johnston <wjohnston@mozilla.com>
*/
body {
margin: 0;
padding: 0 8px 8px;
font-family: "Nokia Sans", Tahoma, sans-serif !important;
}
h1 {
font-size: 22px;
}
h2 {
font-size: 16px;
}
ul {
margin: 0px;
padding: 0px 0px 0px 1em;
}
li {
margin: 0px;
padding: 8px 0px;
}
#errorPage {
background-color: #CEE6F4;
}
#errorPage.certerror {
background-color: #EFD400;
}
#errorPage.blockedsite {
background-color: #BF0000;
}
#errorTitle {
background: url("chrome://browser/content/images/errorpage-warning.png") left center no-repeat;
/* Scaled by .666 of their actual size */
background-size: 40px 40px;
background-origin: content-box;
min-height: 60px;
margin-left: auto;
margin-right: auto;
max-width: 500px;
margin-left: auto;
margin-right: auto;
}
#errorPage.certerror #errorTitle {
background-image: url("chrome://browser/content/images/errorpage-larry-black.png");
}
#errorPage.blockedsite #errorTitle {
background-image: url("chrome://browser/content/images/errorpage-larry-white.png");
color: white;
}
.errorTitleText {
padding: 0px 0px 0px 50px;
display: inline-block;
vertical-align: middle
}
#errorPageContainer {
background-color: white;
border: 1px solid #999999;
border-radius: 6px;
padding: 6px 20px 20px;
font-size: 14px;
max-width: 500px;
margin-left: auto;
margin-right: auto;
}
#errorShortDesc > p:empty {
display: none;
}
#errorShortDesc > p {
overflow: auto;
border-bottom: 1px solid #999999;
padding-bottom: 1em;
}
#errorPage.blockedsite #errorShortDesc > p {
font-weight: bold;
border-bottom: none;
padding-bottom: 0px;
}
#securityOverrideDiv {
padding-top: 10px;
}
div[collapsed] {
padding-left: 15px;
background-image: url("chrome://browser/skin/images/arrowright-16.png");
background-size: 11px 11px;
background-repeat: no-repeat;
background-position: left 0.3em;
}
div[collapsed="true"] {
background-image: url("chrome://browser/skin/images/arrowright-16.png");
}
div[collapsed="false"] {
background-image: url("chrome://browser/skin/images/arrowdown-16.png");
}
div[collapsed="true"] > p,
div[collapsed="true"] > div {
display: none;
}
button {
padding: 0.3em !important;
}

Просмотреть файл

@ -21,9 +21,10 @@ Cu.import('resource://gre/modules/ErrorPage.jsm');
Cu.import('resource://gre/modules/NetworkStatsService.jsm'); Cu.import('resource://gre/modules/NetworkStatsService.jsm');
#endif #endif
// identity // Identity
Cu.import('resource://gre/modules/SignInToWebsite.jsm'); Cu.import('resource://gre/modules/SignInToWebsite.jsm');
SignInToWebsiteController.init(); SignInToWebsiteController.init();
Cu.import('resource://gre/modules/FxAccountsMgmtService.jsm');
Cu.import('resource://gre/modules/DownloadsAPI.jsm'); Cu.import('resource://gre/modules/DownloadsAPI.jsm');
@ -614,6 +615,8 @@ var shell = {
this.sendEvent(window, 'ContentStart'); this.sendEvent(window, 'ContentStart');
Services.obs.notifyObservers(null, 'content-start', null);
#ifdef MOZ_WIDGET_GONK #ifdef MOZ_WIDGET_GONK
Cu.import('resource://gre/modules/OperatorApps.jsm'); Cu.import('resource://gre/modules/OperatorApps.jsm');
#endif #endif

Просмотреть файл

@ -25,9 +25,11 @@ chrome.jar:
% override chrome://global/skin/media/videocontrols.css chrome://browser/content/touchcontrols.css % override chrome://global/skin/media/videocontrols.css chrome://browser/content/touchcontrols.css
% override chrome://global/content/aboutCertError.xhtml chrome://browser/content/aboutCertError.xhtml % override chrome://global/content/aboutCertError.xhtml chrome://browser/content/aboutCertError.xhtml
% override chrome://global/skin/netError.css chrome://browser/content/netError.css
content/ErrorPage.js (content/ErrorPage.js) content/ErrorPage.js (content/ErrorPage.js)
content/aboutCertError.xhtml (content/aboutCertError.xhtml) content/aboutCertError.xhtml (content/aboutCertError.xhtml)
content/netError.css (content/netError.css)
content/images/errorpage-larry-black.png (content/images/errorpage-larry-black.png) content/images/errorpage-larry-black.png (content/images/errorpage-larry-black.png)
content/images/errorpage-larry-white.png (content/images/errorpage-larry-white.png) content/images/errorpage-larry-white.png (content/images/errorpage-larry-white.png)
content/images/errorpage-warning.png (content/images/errorpage-warning.png) content/images/errorpage-warning.png (content/images/errorpage-warning.png)

Просмотреть файл

@ -72,6 +72,10 @@ component {637b0f77-2429-49a0-915f-abf5d0db8b9a} WebappsUpdateTimer.js
contract @mozilla.org/b2g/webapps-update-timer;1 {637b0f77-2429-49a0-915f-abf5d0db8b9a} contract @mozilla.org/b2g/webapps-update-timer;1 {637b0f77-2429-49a0-915f-abf5d0db8b9a}
category update-timer WebappsUpdateTimer @mozilla.org/b2g/webapps-update-timer;1,getService,background-update-timer,webapps.update.interval,86400 category update-timer WebappsUpdateTimer @mozilla.org/b2g/webapps-update-timer;1,getService,background-update-timer,webapps.update.interval,86400
# FxAccountsUIGlue.js
component {51875c14-91d7-4b8c-b65d-3549e101228c} FxAccountsUIGlue.js
contract @mozilla.org/fxaccounts/fxaccounts-ui-glue;1 {51875c14-91d7-4b8c-b65d-3549e101228c}
# HelperAppDialog.js # HelperAppDialog.js
component {710322af-e6ae-4b0c-b2c9-1474a87b077e} HelperAppDialog.js component {710322af-e6ae-4b0c-b2c9-1474a87b077e} HelperAppDialog.js
contract @mozilla.org/helperapplauncherdialog;1 {710322af-e6ae-4b0c-b2c9-1474a87b077e} contract @mozilla.org/helperapplauncherdialog;1 {710322af-e6ae-4b0c-b2c9-1474a87b077e}

Просмотреть файл

@ -150,12 +150,16 @@ let ErrorPage = {
}, },
init: function errorPageInit() { init: function errorPageInit() {
Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false); Services.obs.addObserver(this, 'inprocess-browser-shown', false);
Services.obs.addObserver(this, 'remote-browser-frame-shown', false); Services.obs.addObserver(this, 'remote-browser-shown', false);
}, },
observe: function errorPageObserve(aSubject, aTopic, aData) { observe: function errorPageObserve(aSubject, aTopic, aData) {
let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader); let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader);
// Ignore notifications that aren't from a BrowserOrApp
if (!frameLoader.ownerIsBrowserOrAppFrame) {
return;
}
let mm = frameLoader.messageManager; let mm = frameLoader.messageManager;
// This won't happen from dom/ipc/preload.js in non-OOP builds. // This won't happen from dom/ipc/preload.js in non-OOP builds.

Просмотреть файл

@ -0,0 +1,129 @@
/* 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/. */
/**
* Some specific (certified) apps need to get access to certain Firefox Accounts
* functionality that allows them to manage accounts (this is mostly sign up,
* sign in, logout and delete) and get information about the currently existing
* ones.
*
* This service listens for requests coming from these apps, triggers the
* appropriate Fx Accounts flows and send reponses back to the UI.
*
* The communication mechanism is based in mozFxAccountsContentEvent (for
* messages coming from the UI) and mozFxAccountsChromeEvent (for messages
* sent from the chrome side) custom events.
*/
"use strict";
this.EXPORTED_SYMBOLS = ["FxAccountsMgmtService"];
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/ObjectWrapper.jsm");
Cu.import("resource://gre/modules/FxAccountsCommon.js");
XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsManager",
"resource://gre/modules/FxAccountsManager.jsm");
this.FxAccountsMgmtService = {
_sendChromeEvent: function(aMsg) {
if (!this._shell) {
return;
}
this._shell.sendCustomEvent("mozFxAccountsChromeEvent", aMsg);
},
_onFullfill: function(aMsgId, aData) {
this._sendChromeEvent({
id: aMsgId,
data: aData ? aData : null
});
},
_onReject: function(aMsgId, aReason) {
this._sendChromeEvent({
id: aMsgId,
error: aReason ? aReason : null
});
},
init: function() {
Services.obs.addObserver(this, "content-start", false);
},
observe: function(aSubject, aTopic, aData) {
this._shell = Services.wm.getMostRecentWindow("navigator:browser").shell;
let content = this._shell.contentBrowser.contentWindow;
content.addEventListener("mozFxAccountsContentEvent",
FxAccountsMgmtService);
Services.obs.removeObserver(this, "content-start");
},
handleEvent: function(aEvent) {
let msg = aEvent.detail;
log.debug("Got content msg " + JSON.stringify(msg));
let self = FxAccountsMgmtService;
if (!msg.id) {
return;
}
let data = msg.data;
if (!data) {
return;
}
switch(data.method) {
case "getAccounts":
FxAccountsManager.getAccount().then(
account => {
// We only expose the email and verification status so far.
self._onFullfill(msg.id, account);
},
reason => {
self._onReject(msg.id, reason);
}
).then(null, Components.utils.reportError);
break;
case "logout":
FxAccountsManager.signOut().then(
() => {
self._onFullfill(msg.id);
},
reason => {
self._onReject(msg.id, reason);
}
).then(null, Components.utils.reportError);
break;
case "queryAccount":
FxAccountsManager.queryAccount(data.accountId).then(
result => {
self._onFullfill(msg.id, result);
},
reason => {
self._onReject(msg.id, reason);
}
).then(null, Components.utils.reportError);
break;
case "signIn":
case "signUp":
FxAccountsManager[data.method](data.accountId, data.password).then(
user => {
self._onFullfill(msg.id, user);
},
reason => {
self._onReject(msg.id, reason);
}
).then(null, Components.utils.reportError);
break;
}
}
};
FxAccountsMgmtService.init();

Просмотреть файл

@ -0,0 +1,73 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict"
const { interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/ObjectWrapper.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/FxAccountsCommon.js");
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
function FxAccountsUIGlue() {
}
FxAccountsUIGlue.prototype = {
_browser: Services.wm.getMostRecentWindow("navigator:browser"),
signInFlow: function() {
let deferred = Promise.defer();
let content = this._browser.getContentWindow();
if (!content) {
deferred.reject("InternalErrorNoContent");
return;
}
let id = uuidgen.generateUUID().toString();
content.addEventListener("mozFxAccountsRPContentEvent",
function onContentEvent(result) {
let msg = result.detail;
if (!msg || !msg.id || msg.id != id) {
deferred.reject("InternalErrorWrongContentEvent");
content.removeEventListener("mozFxAccountsRPContentEvent",
onContentEvent);
return;
}
log.debug("Got content event " + JSON.stringify(msg));
if (msg.error) {
deferred.reject(msg);
} else {
deferred.resolve(msg.result);
}
content.removeEventListener("mozFxAccountsRPContentEvent",
onContentEvent);
});
let detail = {
method: "openFlow",
id: id
};
log.debug("Send chrome event " + JSON.stringify(detail));
this._browser.shell.sendCustomEvent("mozFxAccountsRPChromeEvent", detail);
return deferred.promise;
},
classID: Components.ID("{51875c14-91d7-4b8c-b65d-3549e101228c}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFxAccountsUIGlue])
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FxAccountsUIGlue]);

Просмотреть файл

@ -13,6 +13,7 @@ EXTRA_COMPONENTS += [
'ContentHandler.js', 'ContentHandler.js',
'ContentPermissionPrompt.js', 'ContentPermissionPrompt.js',
'FilePicker.js', 'FilePicker.js',
'FxAccountsUIGlue.js',
'HelperAppDialog.js', 'HelperAppDialog.js',
'MailtoProtocolHandler.js', 'MailtoProtocolHandler.js',
'PaymentGlue.js', 'PaymentGlue.js',
@ -36,6 +37,7 @@ if CONFIG['MOZ_UPDATER']:
EXTRA_JS_MODULES += [ EXTRA_JS_MODULES += [
'ErrorPage.jsm', 'ErrorPage.jsm',
'FxAccountsMgmtService.jsm',
'SignInToWebsite.jsm', 'SignInToWebsite.jsm',
'TelURIParser.jsm', 'TelURIParser.jsm',
'WebappsUpdater.jsm', 'WebappsUpdater.jsm',

Просмотреть файл

@ -1,4 +1,4 @@
{ {
"revision": "9271d94f35a54995e4442da711e51cff0244741d", "revision": "62c0ad5b88f15d5da1cc2496b9534087dbc5d015",
"repo_path": "/integration/gaia-central" "repo_path": "/integration/gaia-central"
} }

Просмотреть файл

@ -789,6 +789,7 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
@BINPATH@/components/TelProtocolHandler.js @BINPATH@/components/TelProtocolHandler.js
@BINPATH@/components/B2GAboutRedirector.js @BINPATH@/components/B2GAboutRedirector.js
@BINPATH@/components/FilePicker.js @BINPATH@/components/FilePicker.js
@BINPATH@/components/FxAccountsUIGlue.js
@BINPATH@/components/HelperAppDialog.js @BINPATH@/components/HelperAppDialog.js
@BINPATH@/components/DownloadsUI.js @BINPATH@/components/DownloadsUI.js
@ -796,6 +797,8 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
@BINPATH@/components/DataStoreService.js @BINPATH@/components/DataStoreService.js
@BINPATH@/components/dom_datastore.xpt @BINPATH@/components/dom_datastore.xpt
@BINPATH@/components/services_fxaccounts.xpt
#ifdef MOZ_WEBSPEECH #ifdef MOZ_WEBSPEECH
@BINPATH@/components/dom_webspeechsynth.xpt @BINPATH@/components/dom_webspeechsynth.xpt
#endif #endif

Просмотреть файл

@ -752,10 +752,9 @@ WriteBitmap(nsIFile* aFile, imgIContainer* aImage)
{ {
nsresult rv; nsresult rv;
nsRefPtr<gfxASurface> surface; nsRefPtr<gfxASurface> surface =
aImage->GetFrame(imgIContainer::FRAME_FIRST, aImage->GetFrame(imgIContainer::FRAME_FIRST,
imgIContainer::FLAG_SYNC_DECODE, imgIContainer::FLAG_SYNC_DECODE);
getter_AddRefs(surface));
NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE); NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
nsRefPtr<gfxImageSurface> image(surface->GetAsReadableARGB32ImageSurface()); nsRefPtr<gfxImageSurface> image(surface->GetAsReadableARGB32ImageSurface());

Просмотреть файл

@ -14,13 +14,13 @@ function test() {
let events = win.EVENTS; let events = win.EVENTS;
let variables = win.DebuggerView.Variables; let variables = win.DebuggerView.Variables;
// Allow this generator function to yield first.
executeSoon(() => debuggee.test());
yield waitForSourceAndCaretAndScopes(panel, ".html", 23);
// Wait for the hierarchy to be committed by the VariablesViewController. // Wait for the hierarchy to be committed by the VariablesViewController.
let committed = promise.defer(); let committed = promise.defer();
variables.oncommit = committed.resolve; variables.oncommit = committed.resolve;
// Allow this generator function to yield first.
executeSoon(() => debuggee.test());
yield waitForSourceAndCaretAndScopes(panel, ".html", 23);
yield committed.promise; yield committed.promise;
let firstScope = variables.getScopeAtIndex(0); let firstScope = variables.getScopeAtIndex(0);

Просмотреть файл

@ -13,6 +13,8 @@ mock.pth:python/mock-1.0.0
mozilla.pth:build mozilla.pth:build
mozilla.pth:config mozilla.pth:config
mozilla.pth:xpcom/typelib/xpt/tools mozilla.pth:xpcom/typelib/xpt/tools
mozilla.pth:dom/bindings
mozilla.pth:dom/bindings/parser
moztreedocs.pth:tools/docs moztreedocs.pth:tools/docs
copy:build/buildconfig.py copy:build/buildconfig.py
packages.txt:testing/mozbase/packages.txt packages.txt:testing/mozbase/packages.txt

Просмотреть файл

@ -111,7 +111,7 @@ interface nsIContentViewManager : nsISupports
readonly attribute nsIContentView rootContentView; readonly attribute nsIContentView rootContentView;
}; };
[scriptable, builtinclass, uuid(5b9949dc-56f1-47b6-b6d2-3785bb90ed6d)] [scriptable, builtinclass, uuid(a723673b-a26e-4cc6-ae23-ec70df9d97c9)]
interface nsIFrameLoader : nsISupports interface nsIFrameLoader : nsISupports
{ {
/** /**
@ -272,6 +272,11 @@ interface nsIFrameLoader : nsISupports
* have a notion of visibility in the parent process when frames are OOP. * have a notion of visibility in the parent process when frames are OOP.
*/ */
[infallible] attribute boolean visible; [infallible] attribute boolean visible;
/**
* Find out whether the owner content really is a browser or app frame
*/
readonly attribute boolean ownerIsBrowserOrAppFrame;
}; };
%{C++ %{C++

Просмотреть файл

@ -948,9 +948,9 @@ nsFrameLoader::ShowRemoteFrame(const nsIntSize& size,
EnsureMessageManager(); EnsureMessageManager();
nsCOMPtr<nsIObserverService> os = services::GetObserverService(); nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (OwnerIsBrowserOrAppFrame() && os && !mRemoteBrowserInitialized) { if (os && !mRemoteBrowserInitialized) {
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
"remote-browser-frame-shown", nullptr); "remote-browser-shown", nullptr);
mRemoteBrowserInitialized = true; mRemoteBrowserInitialized = true;
} }
} else { } else {
@ -1409,6 +1409,14 @@ nsFrameLoader::OwnerIsBrowserOrAppFrame()
return browserFrame ? browserFrame->GetReallyIsBrowserOrApp() : false; return browserFrame ? browserFrame->GetReallyIsBrowserOrApp() : false;
} }
// The xpcom getter version
NS_IMETHODIMP
nsFrameLoader::GetOwnerIsBrowserOrAppFrame(bool* aResult)
{
*aResult = OwnerIsBrowserOrAppFrame();
return NS_OK;
}
bool bool
nsFrameLoader::OwnerIsAppFrame() nsFrameLoader::OwnerIsAppFrame()
{ {
@ -1677,19 +1685,17 @@ nsFrameLoader::MaybeCreateDocShell()
mDocShell->SetIsBrowserInsideApp(containingAppId); mDocShell->SetIsBrowserInsideApp(containingAppId);
} }
if (OwnerIsBrowserOrAppFrame()) {
nsCOMPtr<nsIObserverService> os = services::GetObserverService(); nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) { if (os) {
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
"in-process-browser-or-app-frame-shown", nullptr); "inprocess-browser-shown", nullptr);
} }
if (mMessageManager) { if (OwnerIsBrowserOrAppFrame() && mMessageManager) {
mMessageManager->LoadFrameScript( mMessageManager->LoadFrameScript(
NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"), NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"),
/* allowDelayedLoad = */ true); /* allowDelayedLoad = */ true);
} }
}
return NS_OK; return NS_OK;
} }

Просмотреть файл

@ -1471,13 +1471,6 @@ GK_ATOM(restart, "restart")
GK_ATOM(to, "to") GK_ATOM(to, "to")
GK_ATOM(XML, "XML") GK_ATOM(XML, "XML")
// internal MathML attributes: different from columnalign_, columnlines_,
// rowalign_ and rowlines_
GK_ATOM(_moz_math_columnalign_, "_moz-math-columnalign")
GK_ATOM(_moz_math_columnline_, "_moz-math-columnline")
GK_ATOM(_moz_math_rowalign_, "_moz-math-rowalign")
GK_ATOM(_moz_math_rowline_, "_moz-math-rowline")
GK_ATOM(abs_, "abs") GK_ATOM(abs_, "abs")
GK_ATOM(accent_, "accent") GK_ATOM(accent_, "accent")
GK_ATOM(accentunder_, "accentunder") GK_ATOM(accentunder_, "accentunder")

Просмотреть файл

@ -789,8 +789,11 @@ nsScriptLoader::AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest)
if (!context) { if (!context) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
AutoPushJSContext cx(context->GetNativeContext());
JS::Rooted<JSObject*> global(cx, unrootedGlobal); JSContext* unpushedCx = context->GetNativeContext();
JSAutoRequest ar(unpushedCx);
JS::Rooted<JSObject*> global(unpushedCx, unrootedGlobal);
AutoPushJSContext cx(unpushedCx);
JS::CompileOptions options(cx); JS::CompileOptions options(cx);
FillCompileOptionsForRequest(aRequest, global, &options); FillCompileOptionsForRequest(aRequest, global, &options);
@ -1002,8 +1005,11 @@ nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest,
if (!context) { if (!context) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
AutoPushJSContext cx(context->GetNativeContext());
JS::Rooted<JSObject*> global(cx, unrootedGlobal); JSContext* unpushedCx = context->GetNativeContext();
JSAutoRequest ar(unpushedCx);
JS::Rooted<JSObject*> global(unpushedCx, unrootedGlobal);
AutoPushJSContext cx(unpushedCx);
bool oldProcessingScriptTag = context->GetProcessingScriptTag(); bool oldProcessingScriptTag = context->GetProcessingScriptTag();
context->SetProcessingScriptTag(true); context->SetProcessingScriptTag(true);

Просмотреть файл

@ -1890,7 +1890,7 @@ CanvasRenderingContext2D::Arc(double x, double y, double r,
EnsureWritablePath(); EnsureWritablePath();
ArcToBezier(this, Point(x, y), r, startAngle, endAngle, anticlockwise); ArcToBezier(this, Point(x, y), Size(r, r), startAngle, endAngle, anticlockwise);
} }
void void

Просмотреть файл

@ -1347,7 +1347,9 @@ nsEventListenerManager::GetScriptGlobalAndDocument(nsIDocument** aDoc)
// XXX sXBL/XBL2 issue -- do we really want the owner here? What // XXX sXBL/XBL2 issue -- do we really want the owner here? What
// if that's the XBL document? // if that's the XBL document?
doc = node->OwnerDoc(); doc = node->OwnerDoc();
MOZ_ASSERT(!doc->IsLoadedAsData(), "Should not get in here at all"); if (doc->IsLoadedAsData()) {
return nullptr;
}
// We want to allow compiling an event handler even in an unloaded // We want to allow compiling an event handler even in an unloaded
// document, so use GetScopeObject here, not GetScriptHandlingObject. // document, so use GetScopeObject here, not GetScriptHandlingObject.

Просмотреть файл

@ -84,6 +84,7 @@ skip-if = true # Disabled due to timeouts.
[test_bug855741.html] [test_bug855741.html]
[test_bug864040.html] [test_bug864040.html]
[test_bug930374-content.html] [test_bug930374-content.html]
[test_bug944847.html]
skip-if = toolkit == "gonk" skip-if = toolkit == "gonk"
[test_clickevent_on_input.html] [test_clickevent_on_input.html]
[test_continuous_wheel_events.html] [test_continuous_wheel_events.html]

Просмотреть файл

@ -0,0 +1,42 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=944847
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 944847</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Bug 944847 **/
var e1 = document.createElement("div");
is(e1.onclick, null);
e1.setAttribute("onclick", "");
isnot(e1.onclick, null);
var e2 = document.implementation.createHTMLDocument(null, null).createElement("div");
is(e2.onclick, null);
e2.setAttribute("onclick", "");
is(e2.onclick, null);
var e3 = document.createElement("div");
is(e3.onclick, null);
e3.setAttribute("onclick", "");
e2.ownerDocument.adoptNode(e3);
is(e3.onclick, null);
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=944847">Mozilla Bug 944847</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

Просмотреть файл

@ -40,7 +40,7 @@ void VideoFrameContainer::SetCurrentFrame(const gfxIntSize& aIntrinsicSize,
mIntrinsicSizeChanged = true; mIntrinsicSizeChanged = true;
} }
gfxIntSize oldFrameSize = mImageContainer->GetCurrentSize(); gfx::IntSize oldFrameSize = mImageContainer->GetCurrentSize();
TimeStamp lastPaintTime = mImageContainer->GetPaintTime(); TimeStamp lastPaintTime = mImageContainer->GetPaintTime();
if (!lastPaintTime.IsNull() && !mPaintTarget.IsNull()) { if (!lastPaintTime.IsNull() && !mPaintTarget.IsNull()) {
mPaintDelay = lastPaintTime - mPaintTarget; mPaintDelay = lastPaintTime - mPaintTarget;
@ -57,7 +57,7 @@ void VideoFrameContainer::SetCurrentFrame(const gfxIntSize& aIntrinsicSize,
mImageContainer->UnlockCurrentImage(); mImageContainer->UnlockCurrentImage();
mImageContainer->SetCurrentImage(aImage); mImageContainer->SetCurrentImage(aImage);
gfxIntSize newFrameSize = mImageContainer->GetCurrentSize(); gfx::IntSize newFrameSize = mImageContainer->GetCurrentSize();
if (oldFrameSize != newFrameSize) { if (oldFrameSize != newFrameSize) {
mImageSizeChanged = true; mImageSizeChanged = true;
} }

Просмотреть файл

@ -147,7 +147,7 @@ VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
while (!iter.IsEnded()) { while (!iter.IsEnded()) {
VideoChunk chunk = *iter; VideoChunk chunk = *iter;
if (!chunk.IsNull()) { if (!chunk.IsNull()) {
gfxIntSize imgsize = chunk.mFrame.GetImage()->GetSize(); gfx::IntSize imgsize = chunk.mFrame.GetImage()->GetSize();
nsresult rv = Init(imgsize.width, imgsize.height, aTrackRate); nsresult rv = Init(imgsize.width, imgsize.height, aTrackRate);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
LOG("[VideoTrackEncoder]: Fail to initialize the encoder!"); LOG("[VideoTrackEncoder]: Fail to initialize the encoder!");

Просмотреть файл

@ -6,6 +6,7 @@
#include "MediaPluginReader.h" #include "MediaPluginReader.h"
#include "mozilla/TimeStamp.h" #include "mozilla/TimeStamp.h"
#include "mozilla/dom/TimeRanges.h" #include "mozilla/dom/TimeRanges.h"
#include "mozilla/gfx/Point.h"
#include "MediaResource.h" #include "MediaResource.h"
#include "VideoUtils.h" #include "VideoUtils.h"
#include "MediaPluginDecoder.h" #include "MediaPluginDecoder.h"
@ -172,7 +173,7 @@ bool MediaPluginReader::DecodeVideoFrame(bool &aKeyframeSkip,
nsAutoPtr<VideoData> v; nsAutoPtr<VideoData> v;
if (currentImage) { if (currentImage) {
gfxIntSize frameSize = currentImage->GetSize(); gfx::IntSize frameSize = currentImage->GetSize();
if (frameSize.width != mInitialFrame.width || if (frameSize.width != mInitialFrame.width ||
frameSize.height != mInitialFrame.height) { frameSize.height != mInitialFrame.height) {
// Frame size is different from what the container reports. This is legal, // Frame size is different from what the container reports. This is legal,

Просмотреть файл

@ -174,8 +174,8 @@ AudioContext::CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels,
uint32_t aLength, float aSampleRate, uint32_t aLength, float aSampleRate,
ErrorResult& aRv) ErrorResult& aRv)
{ {
if (aSampleRate < 8000 || aSampleRate > 96000 || !aLength) { if (aSampleRate < 8000 || aSampleRate > 192000 || !aLength || !aNumberOfChannels) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return nullptr; return nullptr;
} }

Просмотреть файл

@ -81,15 +81,21 @@ addLoadEvent(function() {
expectException(function() { expectException(function() {
context.createBuffer(2, 2048, 7999); context.createBuffer(2, 2048, 7999);
}, DOMException.NOT_SUPPORTED_ERR); }, DOMException.INDEX_SIZE_ERR);
expectException(function() { expectException(function() {
context.createBuffer(2, 2048, 96001); context.createBuffer(2, 2048, 192001);
}, DOMException.NOT_SUPPORTED_ERR); }, DOMException.INDEX_SIZE_ERR);
context.createBuffer(2, 2048, 8000); // no exception context.createBuffer(2, 2048, 8000); // no exception
context.createBuffer(2, 2048, 96000); // no exception context.createBuffer(2, 2048, 192000); // no exception
context.createBuffer(32, 2048, 48000); // no exception
// Null length
expectException(function() { expectException(function() {
context.createBuffer(2, 0, 48000); context.createBuffer(2, 0, 48000);
}, DOMException.NOT_SUPPORTED_ERR); }, DOMException.INDEX_SIZE_ERR);
// Null number of channels
expectException(function() {
context.createBuffer(0, 2048, 48000);
}, DOMException.INDEX_SIZE_ERR);
SimpleTest.finish(); SimpleTest.finish();
}); });

Просмотреть файл

@ -21,8 +21,6 @@ var gTest = {
var source = context.createBufferSource(); var source = context.createBufferSource();
source.buffer = buffer; source.buffer = buffer;
var sp = context.createScriptProcessor(2048 * 4, 1);
source.start(0); source.start(0);
source.loop = true; source.loop = true;
source.loopStart = buffer.duration * 0.25; source.loopStart = buffer.duration * 0.25;

Просмотреть файл

@ -9,9 +9,12 @@
<pre id="test"> <pre id="test">
<script class="testbody" type="text/javascript"> <script class="testbody" type="text/javascript">
try {
var ctx = new AudioContext(); var ctx = new AudioContext();
ctx.createBuffer(0, 1, ctx.sampleRate); ctx.createBuffer(0, 1, ctx.sampleRate);
} catch (e) {
ok(true, "The test should not crash during CC"); ok(true, "The test should not crash during CC");
}
</script> </script>
</pre> </pre>

Просмотреть файл

@ -6,6 +6,7 @@
#include "nsPresContext.h" #include "nsPresContext.h"
#include "gfxImageSurface.h" #include "gfxImageSurface.h"
#include "gfxContext.h" #include "gfxContext.h"
#include "gfx2DGlue.h"
#include "ImageContainer.h" #include "ImageContainer.h"
#include "Layers.h" #include "Layers.h"
#include "nsIInterfaceRequestorUtils.h" #include "nsIInterfaceRequestorUtils.h"
@ -154,8 +155,8 @@ NotifyPull(MediaStreamGraph*, SourceMediaStream* aSource, mozilla::TrackID aID,
if (delta > 0) { if (delta > 0) {
// nullptr images are allowed // nullptr images are allowed
if (image) { if (image) {
gfxIntSize size = image->GetSize(); gfx::IntSize size = image->GetSize();
segment.AppendFrame(image.forget(), delta, size); segment.AppendFrame(image.forget(), delta, gfx::ThebesIntSize(size));
} else { } else {
segment.AppendFrame(nullptr, delta, gfxIntSize(0,0)); segment.AppendFrame(nullptr, delta, gfxIntSize(0,0));
} }

Просмотреть файл

@ -164,6 +164,15 @@ public:
return (mIsActive || mIsFrozen); return (mIsActive || mIsFrozen);
} }
/**
* Indicates if the animation is active.
*
* @return true if the animation is active, false otherwise.
*/
bool IsActive() const {
return mIsActive;
}
/** /**
* Indicates if this animation will replace the passed in result rather than * Indicates if this animation will replace the passed in result rather than
* adding to it. Animations that replace the underlying value may be called * adding to it. Animations that replace the underlying value may be called

Просмотреть файл

@ -27,7 +27,11 @@ public:
explicit nsSMILRepeatCount(double aCount) explicit nsSMILRepeatCount(double aCount)
: mCount(kNotSet) { SetCount(aCount); } : mCount(kNotSet) { SetCount(aCount); }
operator double() const { return mCount; } operator double() const {
MOZ_ASSERT(IsDefinite(),
"Converting indefinite or unset repeat count to double");
return mCount;
}
bool IsDefinite() const { bool IsDefinite() const {
return mCount != kNotSet && mCount != kIndefinite; return mCount != kNotSet && mCount != kIndefinite;
} }

Просмотреть файл

@ -112,8 +112,9 @@ namespace
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Helper class: AutoIntervalUpdateBatcher // Helper class: AutoIntervalUpdateBatcher
// RAII helper to set the mDeferIntervalUpdates flag on an nsSMILTimedElement // Stack-based helper class to set the mDeferIntervalUpdates flag on an
// and perform the UpdateCurrentInterval when the object is destroyed. // nsSMILTimedElement and perform the UpdateCurrentInterval when the object is
// destroyed.
// //
// If several of these objects are allocated on the stack, the update will not // If several of these objects are allocated on the stack, the update will not
// be performed until the last object for a given nsSMILTimedElement is // be performed until the last object for a given nsSMILTimedElement is
@ -146,6 +147,31 @@ private:
bool mDidSetFlag; bool mDidSetFlag;
}; };
//----------------------------------------------------------------------
// Helper class: AutoIntervalUpdater
// Stack-based helper class to call UpdateCurrentInterval when it is destroyed
// which helps avoid bugs where we forget to call UpdateCurrentInterval in the
// case of early returns (e.g. due to parse errors).
//
// This can be safely used in conjunction with AutoIntervalUpdateBatcher; any
// calls to UpdateCurrentInterval made by this class will simply be deferred if
// there is an AutoIntervalUpdateBatcher on the stack.
class MOZ_STACK_CLASS nsSMILTimedElement::AutoIntervalUpdater
{
public:
AutoIntervalUpdater(nsSMILTimedElement& aTimedElement)
: mTimedElement(aTimedElement) { }
~AutoIntervalUpdater()
{
mTimedElement.UpdateCurrentInterval();
}
private:
nsSMILTimedElement& mTimedElement;
};
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Templated helper functions // Templated helper functions
@ -667,14 +693,26 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, bool aEndOnly)
NS_ASSERTION(aContainerTime >= beginTime, NS_ASSERTION(aContainerTime >= beginTime,
"Sample time should not precede current interval"); "Sample time should not precede current interval");
nsSMILTime activeTime = aContainerTime - beginTime; nsSMILTime activeTime = aContainerTime - beginTime;
// The 'min' attribute can cause the active interval to be longer than
// the 'repeating interval'.
// In that extended period we apply the fill mode.
if (GetRepeatDuration() <= nsSMILTimeValue(activeTime)) {
if (mClient && mClient->IsActive()) {
mClient->Inactivate(mFillMode == FILL_FREEZE);
}
SampleFillValue();
} else {
SampleSimpleTime(activeTime); SampleSimpleTime(activeTime);
// We register our repeat times as milestones (except when we're // We register our repeat times as milestones (except when we're
// seeking) so we should get a sample at exactly the time we repeat. // seeking) so we should get a sample at exactly the time we repeat.
// (And even when we are seeking we want to update // (And even when we are seeking we want to update
// mCurrentRepeatIteration so we do that first before testing the seek // mCurrentRepeatIteration so we do that first before testing the
// state.) // seek state.)
uint32_t prevRepeatIteration = mCurrentRepeatIteration; uint32_t prevRepeatIteration = mCurrentRepeatIteration;
if (ActiveTimeToSimpleTime(activeTime, mCurrentRepeatIteration)==0 && if (
ActiveTimeToSimpleTime(activeTime, mCurrentRepeatIteration)==0 &&
mCurrentRepeatIteration != prevRepeatIteration && mCurrentRepeatIteration != prevRepeatIteration &&
mCurrentRepeatIteration && mCurrentRepeatIteration &&
mSeekState == SEEK_NOT_SEEKING) { mSeekState == SEEK_NOT_SEEKING) {
@ -683,6 +721,7 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, bool aEndOnly)
} }
} }
} }
}
break; break;
case STATE_POSTACTIVE: case STATE_POSTACTIVE:
@ -905,8 +944,10 @@ nsSMILTimedElement::UnsetEndSpec(RemovalTestFunction aRemove)
nsresult nsresult
nsSMILTimedElement::SetSimpleDuration(const nsAString& aDurSpec) nsSMILTimedElement::SetSimpleDuration(const nsAString& aDurSpec)
{ {
nsSMILTimeValue duration; // Update the current interval before returning
AutoIntervalUpdater updater(*this);
nsSMILTimeValue duration;
const nsAString& dur = nsSMILParserUtils::TrimWhitespace(aDurSpec); const nsAString& dur = nsSMILParserUtils::TrimWhitespace(aDurSpec);
// SVG-specific: "For SVG's animation elements, if "media" is specified, the // SVG-specific: "For SVG's animation elements, if "media" is specified, the
@ -926,7 +967,6 @@ nsSMILTimedElement::SetSimpleDuration(const nsAString& aDurSpec)
"Setting unresolved simple duration"); "Setting unresolved simple duration");
mSimpleDur = duration; mSimpleDur = duration;
UpdateCurrentInterval();
return NS_OK; return NS_OK;
} }
@ -941,8 +981,10 @@ nsSMILTimedElement::UnsetSimpleDuration()
nsresult nsresult
nsSMILTimedElement::SetMin(const nsAString& aMinSpec) nsSMILTimedElement::SetMin(const nsAString& aMinSpec)
{ {
nsSMILTimeValue duration; // Update the current interval before returning
AutoIntervalUpdater updater(*this);
nsSMILTimeValue duration;
const nsAString& min = nsSMILParserUtils::TrimWhitespace(aMinSpec); const nsAString& min = nsSMILParserUtils::TrimWhitespace(aMinSpec);
if (min.EqualsLiteral("media")) { if (min.EqualsLiteral("media")) {
@ -957,7 +999,6 @@ nsSMILTimedElement::SetMin(const nsAString& aMinSpec)
NS_ABORT_IF_FALSE(duration.GetMillis() >= 0L, "Invalid duration"); NS_ABORT_IF_FALSE(duration.GetMillis() >= 0L, "Invalid duration");
mMin = duration; mMin = duration;
UpdateCurrentInterval();
return NS_OK; return NS_OK;
} }
@ -972,8 +1013,10 @@ nsSMILTimedElement::UnsetMin()
nsresult nsresult
nsSMILTimedElement::SetMax(const nsAString& aMaxSpec) nsSMILTimedElement::SetMax(const nsAString& aMaxSpec)
{ {
nsSMILTimeValue duration; // Update the current interval before returning
AutoIntervalUpdater updater(*this);
nsSMILTimeValue duration;
const nsAString& max = nsSMILParserUtils::TrimWhitespace(aMaxSpec); const nsAString& max = nsSMILParserUtils::TrimWhitespace(aMaxSpec);
if (max.EqualsLiteral("media") || max.EqualsLiteral("indefinite")) { if (max.EqualsLiteral("media") || max.EqualsLiteral("indefinite")) {
@ -988,7 +1031,6 @@ nsSMILTimedElement::SetMax(const nsAString& aMaxSpec)
} }
mMax = duration; mMax = duration;
UpdateCurrentInterval();
return NS_OK; return NS_OK;
} }
@ -1023,15 +1065,16 @@ nsSMILTimedElement::UnsetRestart()
nsresult nsresult
nsSMILTimedElement::SetRepeatCount(const nsAString& aRepeatCountSpec) nsSMILTimedElement::SetRepeatCount(const nsAString& aRepeatCountSpec)
{ {
// Update the current interval before returning
AutoIntervalUpdater updater(*this);
nsSMILRepeatCount newRepeatCount; nsSMILRepeatCount newRepeatCount;
if (nsSMILParserUtils::ParseRepeatCount(aRepeatCountSpec, newRepeatCount)) { if (nsSMILParserUtils::ParseRepeatCount(aRepeatCountSpec, newRepeatCount)) {
mRepeatCount = newRepeatCount; mRepeatCount = newRepeatCount;
UpdateCurrentInterval();
return NS_OK; return NS_OK;
} }
mRepeatCount.Unset(); mRepeatCount.Unset();
UpdateCurrentInterval();
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -1045,6 +1088,9 @@ nsSMILTimedElement::UnsetRepeatCount()
nsresult nsresult
nsSMILTimedElement::SetRepeatDur(const nsAString& aRepeatDurSpec) nsSMILTimedElement::SetRepeatDur(const nsAString& aRepeatDurSpec)
{ {
// Update the current interval before returning
AutoIntervalUpdater updater(*this);
nsSMILTimeValue duration; nsSMILTimeValue duration;
const nsAString& repeatDur = const nsAString& repeatDur =
@ -1060,7 +1106,6 @@ nsSMILTimedElement::SetRepeatDur(const nsAString& aRepeatDurSpec)
} }
mRepeatDur = duration; mRepeatDur = duration;
UpdateCurrentInterval();
return NS_OK; return NS_OK;
} }
@ -1084,12 +1129,8 @@ nsSMILTimedElement::SetFillMode(const nsAString& aFillModeSpec)
? nsSMILFillMode(temp.GetEnumValue()) ? nsSMILFillMode(temp.GetEnumValue())
: FILL_REMOVE; : FILL_REMOVE;
// Check if we're in a fill-able state: i.e. we've played at least one // Update fill mode of client
// interval and are now between intervals or at the end of all intervals if (mFillMode != previousFillMode && HasClientInFillRange()) {
bool isFillable = HasPlayed() &&
(mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE);
if (mClient && mFillMode != previousFillMode && isFillable) {
mClient->Inactivate(mFillMode == FILL_FREEZE); mClient->Inactivate(mFillMode == FILL_FREEZE);
SampleFillValue(); SampleFillValue();
} }
@ -1102,10 +1143,10 @@ nsSMILTimedElement::UnsetFillMode()
{ {
uint16_t previousFillMode = mFillMode; uint16_t previousFillMode = mFillMode;
mFillMode = FILL_REMOVE; mFillMode = FILL_REMOVE;
if ((mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE) && if (previousFillMode == FILL_FREEZE && HasClientInFillRange()) {
previousFillMode == FILL_FREEZE && mClient && HasPlayed())
mClient->Inactivate(false); mClient->Inactivate(false);
} }
}
void void
nsSMILTimedElement::AddDependent(nsSMILTimeValueSpec& aDependent) nsSMILTimedElement::AddDependent(nsSMILTimeValueSpec& aDependent)
@ -1803,11 +1844,7 @@ nsSMILTimedElement::CalcActiveEnd(const nsSMILTimeValue& aBegin,
NS_ABORT_IF_FALSE(aBegin.IsDefinite(), NS_ABORT_IF_FALSE(aBegin.IsDefinite(),
"Indefinite or unresolved begin time in CalcActiveEnd"); "Indefinite or unresolved begin time in CalcActiveEnd");
if (mRepeatDur.IsIndefinite()) {
result.SetIndefinite();
} else {
result = GetRepeatDuration(); result = GetRepeatDuration();
}
if (aEnd.IsDefinite()) { if (aEnd.IsDefinite()) {
nsSMILTime activeDur = aEnd.GetMillis() - aBegin.GetMillis(); nsSMILTime activeDur = aEnd.GetMillis() - aBegin.GetMillis();
@ -1832,29 +1869,25 @@ nsSMILTimedElement::CalcActiveEnd(const nsSMILTimeValue& aBegin,
nsSMILTimeValue nsSMILTimeValue
nsSMILTimedElement::GetRepeatDuration() const nsSMILTimedElement::GetRepeatDuration() const
{ {
nsSMILTimeValue result; nsSMILTimeValue multipliedDuration;
if (mRepeatCount.IsDefinite() && mSimpleDur.IsDefinite()) {
if (mRepeatCount.IsDefinite() && mRepeatDur.IsDefinite()) { multipliedDuration.SetMillis(
if (mSimpleDur.IsDefinite()) { nsSMILTime(mRepeatCount * double(mSimpleDur.GetMillis())));
nsSMILTime activeDur =
nsSMILTime(mRepeatCount * double(mSimpleDur.GetMillis()));
result.SetMillis(std::min(activeDur, mRepeatDur.GetMillis()));
} else { } else {
result = mRepeatDur; multipliedDuration.SetIndefinite();
}
} else if (mRepeatCount.IsDefinite() && mSimpleDur.IsDefinite()) {
nsSMILTime activeDur =
nsSMILTime(mRepeatCount * double(mSimpleDur.GetMillis()));
result.SetMillis(activeDur);
} else if (mRepeatDur.IsDefinite()) {
result = mRepeatDur;
} else if (mRepeatCount.IsIndefinite()) {
result.SetIndefinite();
} else {
result = mSimpleDur;
} }
return result; nsSMILTimeValue repeatDuration;
if (mRepeatDur.IsResolved()) {
repeatDuration = std::min(multipliedDuration, mRepeatDur);
} else if (mRepeatCount.IsSet()) {
repeatDuration = multipliedDuration;
} else {
repeatDuration = mSimpleDur;
}
return repeatDuration;
} }
nsSMILTimeValue nsSMILTimeValue
@ -1873,8 +1906,7 @@ nsSMILTimedElement::ApplyMinAndMax(const nsSMILTimeValue& aDuration) const
if (aDuration > mMax) { if (aDuration > mMax) {
result = mMax; result = mMax;
} else if (aDuration < mMin) { } else if (aDuration < mMin) {
nsSMILTimeValue repeatDur = GetRepeatDuration(); result = mMin;
result = mMin > repeatDur ? repeatDur : mMin;
} else { } else {
result = aDuration; result = aDuration;
} }
@ -2068,6 +2100,9 @@ nsSMILTimedElement::SampleFillValue()
if (mFillMode != FILL_FREEZE || !mClient) if (mFillMode != FILL_FREEZE || !mClient)
return; return;
nsSMILTime activeTime;
if (mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE) {
const nsSMILInterval* prevInterval = GetPreviousInterval(); const nsSMILInterval* prevInterval = GetPreviousInterval();
NS_ABORT_IF_FALSE(prevInterval, NS_ABORT_IF_FALSE(prevInterval,
"Attempting to sample fill value but there is no previous interval"); "Attempting to sample fill value but there is no previous interval");
@ -2076,9 +2111,25 @@ nsSMILTimedElement::SampleFillValue()
"Attempting to sample fill value but the endpoint of the previous " "Attempting to sample fill value but the endpoint of the previous "
"interval is not resolved and fixed"); "interval is not resolved and fixed");
nsSMILTime activeTime = prevInterval->End()->Time().GetMillis() - activeTime = prevInterval->End()->Time().GetMillis() -
prevInterval->Begin()->Time().GetMillis(); prevInterval->Begin()->Time().GetMillis();
// If the interval's repeat duration was shorter than its active duration,
// use the end of the repeat duration to determine the frozen animation's
// state.
nsSMILTimeValue repeatDuration = GetRepeatDuration();
if (repeatDuration.IsDefinite()) {
activeTime = std::min(repeatDuration.GetMillis(), activeTime);
}
} else if (mElementState == STATE_ACTIVE) {
// If we are being asked to sample the fill value while active we *must*
// have a repeat duration shorter than the active duration so use that.
MOZ_ASSERT(GetRepeatDuration().IsDefinite(),
"Attempting to sample fill value of an active animation with "
"an indefinite repeat duration");
activeTime = GetRepeatDuration().GetMillis();
}
uint32_t repeatIteration; uint32_t repeatIteration;
nsSMILTime simpleTime = nsSMILTime simpleTime =
ActiveTimeToSimpleTime(activeTime, repeatIteration); ActiveTimeToSimpleTime(activeTime, repeatIteration);
@ -2173,8 +2224,13 @@ nsSMILTimedElement::GetNextMilestone(nsSMILMilestone& aNextMilestone) const
// Work out what comes next: the interval end or the next repeat iteration // Work out what comes next: the interval end or the next repeat iteration
nsSMILTimeValue nextRepeat; nsSMILTimeValue nextRepeat;
if (mSeekState == SEEK_NOT_SEEKING && mSimpleDur.IsDefinite()) { if (mSeekState == SEEK_NOT_SEEKING && mSimpleDur.IsDefinite()) {
nsSMILTime nextRepeatActiveTime =
(mCurrentRepeatIteration + 1) * mSimpleDur.GetMillis();
// Check that the repeat fits within the repeat duration
if (nsSMILTimeValue(nextRepeatActiveTime) < GetRepeatDuration()) {
nextRepeat.SetMillis(mCurrentInterval->Begin()->Time().GetMillis() + nextRepeat.SetMillis(mCurrentInterval->Begin()->Time().GetMillis() +
(mCurrentRepeatIteration + 1) * mSimpleDur.GetMillis()); nextRepeatActiveTime);
}
} }
nsSMILTimeValue nextMilestone = nsSMILTimeValue nextMilestone =
std::min(mCurrentInterval->End()->Time(), nextRepeat); std::min(mCurrentInterval->End()->Time(), nextRepeat);
@ -2283,6 +2339,15 @@ nsSMILTimedElement::GetPreviousInterval() const
: mOldIntervals[mOldIntervals.Length()-1].get(); : mOldIntervals[mOldIntervals.Length()-1].get();
} }
bool
nsSMILTimedElement::HasClientInFillRange() const
{
// Returns true if we have a client that is in the range where it will fill
return mClient &&
((mElementState != STATE_ACTIVE && HasPlayed()) ||
(mElementState == STATE_ACTIVE && !mClient->IsActive()));
}
bool bool
nsSMILTimedElement::EndHasEventConditions() const nsSMILTimedElement::EndHasEventConditions() const
{ {

Просмотреть файл

@ -512,6 +512,7 @@ protected:
const nsSMILInstanceTime* GetEffectiveBeginInstance() const; const nsSMILInstanceTime* GetEffectiveBeginInstance() const;
const nsSMILInterval* GetPreviousInterval() const; const nsSMILInterval* GetPreviousInterval() const;
bool HasPlayed() const { return !mOldIntervals.IsEmpty(); } bool HasPlayed() const { return !mOldIntervals.IsEmpty(); }
bool HasClientInFillRange() const;
bool EndHasEventConditions() const; bool EndHasEventConditions() const;
bool AreEndTimesDependentOn( bool AreEndTimesDependentOn(
const nsSMILInstanceTime* aBase) const; const nsSMILInstanceTime* aBase) const;
@ -615,6 +616,9 @@ protected:
bool mDoDeferredUpdate; // Set if an update to the current interval was bool mDoDeferredUpdate; // Set if an update to the current interval was
// requested while mDeferIntervalUpdates was set // requested while mDeferIntervalUpdates was set
// Stack-based helper class to call UpdateCurrentInterval when it is destroyed
class AutoIntervalUpdater;
// Recursion depth checking // Recursion depth checking
uint8_t mDeleteCount; uint8_t mDeleteCount;
uint8_t mUpdateIntervalRecursionDepth; uint8_t mUpdateIntervalRecursionDepth;

Просмотреть файл

@ -32,12 +32,15 @@ support-files =
[test_smilGetSimpleDuration.xhtml] [test_smilGetSimpleDuration.xhtml]
[test_smilGetStartTime.xhtml] [test_smilGetStartTime.xhtml]
[test_smilHyperlinking.xhtml] [test_smilHyperlinking.xhtml]
[test_smilInvalidValues.html]
[test_smilKeySplines.xhtml] [test_smilKeySplines.xhtml]
[test_smilKeyTimes.xhtml] [test_smilKeyTimes.xhtml]
[test_smilKeyTimesPacedMode.xhtml] [test_smilKeyTimesPacedMode.xhtml]
[test_smilMappedAttrFromBy.xhtml] [test_smilMappedAttrFromBy.xhtml]
[test_smilMappedAttrFromTo.xhtml] [test_smilMappedAttrFromTo.xhtml]
[test_smilMappedAttrPaced.xhtml] [test_smilMappedAttrPaced.xhtml]
[test_smilMinTiming.html]
[test_smilRepeatDuration.html]
[test_smilRepeatTiming.xhtml] [test_smilRepeatTiming.xhtml]
[test_smilReset.xhtml] [test_smilReset.xhtml]
[test_smilRestart.xhtml] [test_smilRestart.xhtml]

Просмотреть файл

@ -0,0 +1,113 @@
<!doctype html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=941315
-->
<head>
<meta charset="utf-8">
<title>Test invalid values cause the model to be updated (bug 941315)</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=941315">Mozilla Bug 941315</a>
<p id="display"></p>
<div id="content" style="display: none">
<svg width="100%" height="1" onload="this.pauseAnimations()">
<rect>
<animate id="a" dur="100s"/>
<animate id="b" dur="5s" begin="a.end"/>
</rect>
<circle cx="-100" cy="20" r="15" fill="blue" id="circle"/>
</svg>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
var a = $('a'),
b = $('b');
// Animation doesn't start until onload
SimpleTest.waitForExplicitFinish();
window.addEventListener("load", runTests, false);
// Make testing getStartTime easier
SVGAnimationElement.prototype.safeGetStartTime = function() {
try {
return this.getStartTime();
} catch(e) {
if (e.name == "InvalidStateError" &&
e.code == DOMException.INVALID_STATE_ERR) {
return 'none';
} else {
ok(false, "Unexpected exception: " + e);
return null;
}
}
};
function runTests() {
[testSimpleDuration, testMin, testMax, testRepeatDur, testRepeatCount]
.forEach(function(test) {
ise(b.getStartTime(), 100, "initial state before running " + test.name);
test();
ise(b.getStartTime(), 100, "final state after running " + test.name);
});
SimpleTest.finish();
}
function testSimpleDuration() {
// Verify a valid value updates as expected
a.setAttribute("dur", "50s");
ise(b.safeGetStartTime(), 50, "valid simple duration");
// Check an invalid value also causes the model to be updated
a.setAttribute("dur", "abc"); // -> indefinite
ise(b.safeGetStartTime(), "none", "invalid simple duration");
// Restore state
a.setAttribute("dur", "100s");
}
function testMin() {
a.setAttribute("min", "200s");
ise(b.safeGetStartTime(), 200, "valid min duration");
a.setAttribute("min", "abc"); // -> indefinite
ise(b.safeGetStartTime(), 100, "invalid min duration");
a.removeAttribute("min");
}
function testMax() {
a.setAttribute("max", "50s");
ise(b.safeGetStartTime(), 50, "valid max duration");
a.setAttribute("max", "abc"); // -> indefinite
ise(b.safeGetStartTime(), 100, "invalid max duration");
a.removeAttribute("max");
}
function testRepeatDur() {
a.setAttribute("repeatDur", "200s");
ise(b.safeGetStartTime(), 200, "valid repeatDur duration");
a.setAttribute("repeatDur", "abc"); // -> indefinite
ise(b.safeGetStartTime(), 100, "invalid repeatDur duration");
a.removeAttribute("repeatDur");
}
function testRepeatCount() {
a.setAttribute("repeatCount", "2");
ise(b.safeGetStartTime(), 200, "valid repeatCount duration");
a.setAttribute("repeatCount", "abc"); // -> indefinite
ise(b.safeGetStartTime(), 100, "invalid repeatCount duration");
a.removeAttribute("repeatCount");
}
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,93 @@
<!doctype html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=948245
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 948245</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=948245">Mozilla Bug 948245</a>
<p id="display"></p>
<div id="content" style="display: none">
<svg id="svg" onload="this.pauseAnimations()">
<rect fill="red" id="rect" x="0">
<animate attributeName="x" to="100" id="animation" dur="100s" min="200s"/>
</rect>
</svg>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
// The 'min' attribute introduces a kind of additional state into the SMIL
// model. If the 'min' attribute extends the active duration, the additional
// time between the amount of time the animation normally runs for (called the
// 'repeat duration') and the extended active duration is filled using the
// fill mode.
//
// Below we refer to this period of time between the end of the repeat
// duration and the end of the active duration as the 'extended period'.
//
// This test verifies that as we jump in and out of these states we produce
// the correct values.
//
// The test animation above produces an active interval that is longer than
// the 'repeating duration' of the animation.
var rect = $('rect'),
animation = $('animation');
// Animation doesn't start until onload
SimpleTest.waitForExplicitFinish();
window.addEventListener("load", runTests, false);
function runTests() {
ok($('svg').animationsPaused(), "should be paused by <svg> load handler");
// In the extended period (t=150s) we should not be animating or filling
// since the default fill mode is "none".
animation.ownerSVGElement.setCurrentTime(150);
ise(rect.x.animVal.value, 0,
"Shouldn't fill in extended period with fill='none'");
// If we set the fill mode we should start filling.
animation.setAttribute("fill", "freeze");
ise(rect.x.animVal.value, 100,
"Should fill in extended period with fill='freeze'");
// If we unset the fill attribute we should stop filling.
animation.removeAttribute("fill");
ise(rect.x.animVal.value, 0, "Shouldn't fill after unsetting fill");
// If we jump back into the repeated interval (at t=50s) we should be
// animating.
animation.ownerSVGElement.setCurrentTime(50);
ise(rect.x.animVal.value, 50, "Should be active in repeating interval");
// If we jump to the boundary at the start of the extended period we should
// not be filling (since we removed the fill attribute above).
animation.ownerSVGElement.setCurrentTime(100);
ise(rect.x.animVal.value, 0,
"Shouldn't fill after seeking to boundary of extended period");
// If we apply a fill mode at this boundary point we should do regular fill
// behavior of using the last value in the interpolation range.
animation.setAttribute("fill", "freeze");
ise(rect.x.animVal.value, 100,
"Should fill at boundary to extended period");
// Check that if we seek past the interval we fill with the value at the end
// of the _repeat_duration_ not the value at the end of the
// _active_duration_.
animation.setAttribute("repeatCount", "1.5");
animation.ownerSVGElement.setCurrentTime(225);
ise(rect.x.animVal.value, 50,
"Should fill with the end of the repeat duration value");
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,139 @@
<!doctype html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=948245
-->
<head>
<meta charset="utf-8">
<title>Test for repeat duration calculation (Bug 948245)</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=948245">Mozilla Bug 948245</a>
<p id="display"></p>
<div id="content" style="display: none">
<svg id="svg" onload="this.pauseAnimations()">
<rect>
<animate id="a"/>
<animate id="b" begin="a.end"/>
</rect>
</svg>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
// Tests the calculation of the repeat duration which is one of the steps
// towards determining the active duration.
//
// The repeat duration is determined by the following three attributes:
//
// dur: may be definite (e.g. '2s') or 'indefinite' (the default)
// repeatCount: may be definite (e.g. '2.5'), 'indefinite', or not set
// repeatDur: may be definite (e.g. '5s'), 'indefinite', or not set
//
// That leaves 18 combinations to test.
var testCases =
[
// 1. repeatDur: definite, repeatCount: definite, dur: definite
// (Two test cases here to ensure we get the minimum)
{ repeatDur: 15, repeatCount: 2, dur: 10, result: 15 },
{ repeatDur: 25, repeatCount: 2, dur: 10, result: 20 },
// 2. repeatDur: indefinite, repeatCount: definite, dur: definite
{ repeatDur: 'indefinite', repeatCount: 2, dur: 10, result: 20 },
// 3. repeatDur: not set, repeatCount: definite, dur: definite
{ repeatCount: 2, dur: 10, result: 20 },
// 4. repeatDur: definite, repeatCount: indefinite, dur: definite
{ repeatDur: 15, repeatCount: 'indefinite', dur: 10, result: 15 },
// 5. repeatDur: indefinite, repeatCount: indefinite, dur: definite
{ repeatDur: 'indefinite', repeatCount: 'indefinite', dur: 10,
result: 'indefinite' },
// 6. repeatDur: not set, repeatCount: indefinite, dur: definite
{ repeatCount: 'indefinite', dur: 10, result: 'indefinite' },
// 7. repeatDur: definite, repeatCount: not set, dur: definite
{ repeatDur: 15, dur: 10, result: 15 },
// 8. repeatDur: indefinite, repeatCount: not set, dur: definite
{ repeatDur: 'indefinite', dur: 10, result: 'indefinite' },
// 9. repeatDur: not set, repeatCount: not set, dur: definite
{ dur: 10, result: 10 },
// 10. repeatDur: definite, repeatCount: definite, dur: indefinite
{ repeatDur: 15, repeatCount: 2, dur: 'indefinite', result: 15 },
// 11. repeatDur: indefinite, repeatCount: definite, dur: indefinite
{ repeatDur: 'indefinite', repeatCount: 2, dur: 'indefinite',
result: 'indefinite' },
// 12. repeatDur: not set, repeatCount: definite, dur: indefinite
{ repeatCount: 2, dur: 'indefinite', result: 'indefinite' },
// 13. repeatDur: definite, repeatCount: indefinite, dur: indefinite
{ repeatDur: 15, repeatCount: 'indefinite', dur: 'indefinite',
result: 15 },
// 14. repeatDur: indefinite, repeatCount: indefinite, dur: indefinite
{ repeatDur: 'indefinite', repeatCount: 'indefinite', dur: 'indefinite',
result: 'indefinite' },
// 15. repeatDur: not set, repeatCount: indefinite, dur: indefinite
{ repeatCount: 'indefinite', dur: 'indefinite', result: 'indefinite' },
// 16. repeatDur: definite, repeatCount: not set, dur: indefinite
{ repeatDur: 15, dur: 'indefinite', result: 15 },
// 17. repeatDur: indefinite, repeatCount: not set, dur: indefinite
{ repeatDur: 'indefinite', dur: 'indefinite', result: 'indefinite' },
// 18. repeatDur: not set, repeatCount: not set, dur: indefinite
{ dur: 'indefinite', result: 'indefinite' }
];
// We can test the repeat duration by setting these attributes on animation
// 'a' and checking the start time of 'b' which is defined to start when 'a'
// finishes.
//
// Since 'a' has no end/min/max attributes the end of its active interval
// should coincide with the end of its repeat duration.
//
// Sometimes the repeat duration is defined to be 'indefinite'. In this case
// calling getStartTime on b will throw an exception so we need to catch that
// exception and translate it to 'indefinite' as follows:
function getRepeatDuration() {
try {
return $('b').getStartTime();
} catch(e) {
if (e.name == "InvalidStateError" &&
e.code == DOMException.INVALID_STATE_ERR) {
return 'indefinite';
} else {
ok(false, "Unexpected exception: " + e);
return null;
}
}
}
// Animation doesn't start until onload
SimpleTest.waitForExplicitFinish();
window.addEventListener("load", runTests, false);
// Run through each of the test cases
function runTests() {
ok($('svg').animationsPaused(), "should be paused by <svg> load handler");
testCases.forEach(function(test) {
var a = $('a');
// Set the attributes
var msgPieces = [];
[ 'repeatDur', 'repeatCount', 'dur' ].forEach(function(attr) {
if (typeof test[attr] != "undefined") {
a.setAttribute(attr, test[attr].toString());
msgPieces.push(attr + ': ' + test[attr].toString());
} else {
a.removeAttribute(attr);
msgPieces.push(attr + ': <not set>');
}
});
var msg = msgPieces.join(', ');
// Check the result
ise(getRepeatDuration(), test.result, msg);
});
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -95,6 +95,15 @@ SVGEllipseElement::GetLengthInfo()
void void
SVGEllipseElement::ConstructPath(gfxContext *aCtx) SVGEllipseElement::ConstructPath(gfxContext *aCtx)
{ {
if (!aCtx->IsCairo()) {
RefPtr<Path> path = BuildPath();
if (path) {
gfxPath gfxpath(path);
aCtx->SetPath(&gfxpath);
}
return;
}
float x, y, rx, ry; float x, y, rx, ry;
GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr); GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr);
@ -116,7 +125,7 @@ SVGEllipseElement::BuildPath()
RefPtr<PathBuilder> pathBuilder = CreatePathBuilder(); RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
AppendEllipseToPath(pathBuilder, Point(x, y), Size(2.0*rx, 2.0*ry)); ArcToBezier(pathBuilder.get(), Point(x, y), Size(rx, ry), 0, Float(2*M_PI), false);
return pathBuilder->Finish(); return pathBuilder->Finish();
} }

Просмотреть файл

@ -211,9 +211,9 @@ SVGFEImageElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance,
nsRefPtr<gfxASurface> currentFrame; nsRefPtr<gfxASurface> currentFrame;
if (imageContainer) { if (imageContainer) {
currentFrame =
imageContainer->GetFrame(imgIContainer::FRAME_CURRENT, imageContainer->GetFrame(imgIContainer::FRAME_CURRENT,
imgIContainer::FLAG_SYNC_DECODE, imgIContainer::FLAG_SYNC_DECODE);
getter_AddRefs(currentFrame));
} }
if (!currentFrame) { if (!currentFrame) {

Просмотреть файл

@ -1082,28 +1082,7 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, JS::Handle<JSObject*> global,
bool bool
nsXBLBinding::AllowScripts() nsXBLBinding::AllowScripts()
{ {
if (!mPrototypeBinding->GetAllowScripts()) return mPrototypeBinding->GetAllowScripts();
return false;
// Nasty hack. Use the JSContext of the bound node, since the
// security manager API expects to get the docshell type from
// that. But use the nsIPrincipal of our document.
nsIScriptSecurityManager* mgr = nsContentUtils::GetSecurityManager();
if (!mgr) {
return false;
}
nsIDocument* doc = mBoundElement ? mBoundElement->OwnerDoc() : nullptr;
if (!doc) {
return false;
}
nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(doc->GetInnerWindow());
if (!global || !global->GetGlobalJSObject()) {
return false;
}
return mgr->ScriptAllowed(global->GetGlobalJSObject());
} }
nsXBLBinding* nsXBLBinding*

Просмотреть файл

@ -141,7 +141,7 @@ public:
JS::MutableHandle<JSObject*> aClassObject, JS::MutableHandle<JSObject*> aClassObject,
bool* aNew); bool* aNew);
bool AllowScripts(); // XXX make const bool AllowScripts();
mozilla::dom::XBLChildrenElement* FindInsertionPointFor(nsIContent* aChild); mozilla::dom::XBLChildrenElement* FindInsertionPointFor(nsIContent* aChild);

Просмотреть файл

@ -403,6 +403,24 @@ nsXBLDocumentInfo::nsXBLDocumentInfo(nsIDocument* aDocument)
mScriptAccess = allow; mScriptAccess = allow;
} }
mIsChrome = true; mIsChrome = true;
} else {
// If this binding isn't running with system principal, then it's running
// from a remote-XUL whitelisted domain. This is already a not-really-
// supported configuration (among other things, we don't use XBL scopes in
// that configuration for compatibility reasons). But we should still at
// least make an effort to prevent binding code from running if content
// script is disabled or if the source domain is blacklisted (since the
// source domain for remote XBL must always be the same as the source domain
// of the bound content).
//
// If we just ask the binding document if script is enabled, it will
// discover that it has no inner window, and return false. So instead, we
// short-circuit the normal compartment-managed script-disabling machinery,
// and query the policy for the URI directly.
bool allow;
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
nsresult rv = ssm->PolicyAllowsScript(uri, &allow);
mScriptAccess = NS_SUCCEEDED(rv) && allow;
} }
} }

Просмотреть файл

@ -27,7 +27,7 @@ public:
already_AddRefed<nsIDocument> GetDocument() already_AddRefed<nsIDocument> GetDocument()
{ nsCOMPtr<nsIDocument> copy = mDocument; return copy.forget(); } { nsCOMPtr<nsIDocument> copy = mDocument; return copy.forget(); }
bool GetScriptAccess() { return mScriptAccess; } bool GetScriptAccess() const { return mScriptAccess; }
nsIURI* DocumentURI() { return mDocument->GetDocumentURI(); } nsIURI* DocumentURI() { return mDocument->GetDocumentURI(); }

Просмотреть файл

@ -214,7 +214,7 @@ nsXBLPrototypeBinding::SetBindingElement(nsIContent* aElement)
} }
bool bool
nsXBLPrototypeBinding::GetAllowScripts() nsXBLPrototypeBinding::GetAllowScripts() const
{ {
return mXBLDocInfoWeak->GetScriptAccess(); return mXBLDocInfoWeak->GetScriptAccess();
} }

Просмотреть файл

@ -48,7 +48,7 @@ public:
// binding URIs. // binding URIs.
bool CompareBindingURI(nsIURI* aURI) const; bool CompareBindingURI(nsIURI* aURI) const;
bool GetAllowScripts(); bool GetAllowScripts() const;
nsresult BindingAttached(nsIContent* aBoundElement); nsresult BindingAttached(nsIContent* aBoundElement);
nsresult BindingDetached(nsIContent* aBoundElement); nsresult BindingDetached(nsIContent* aBoundElement);

Просмотреть файл

@ -1,4 +1,6 @@
[DEFAULT] [DEFAULT]
support-files =
file_bug944407.xml
[test_bug378518.xul] [test_bug378518.xul]
[test_bug398135.xul] [test_bug398135.xul]
@ -6,3 +8,4 @@
[test_bug721452.xul] [test_bug721452.xul]
[test_bug723676.xul] [test_bug723676.xul]
[test_bug772966.xul] [test_bug772966.xul]
[test_bug944407.xul]

Просмотреть файл

@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<body>
<div id="deny" style="-moz-binding: url(file_bug944407.xml#testAllowScript)"></div>
<div id="allow" style="-moz-binding: url(chrome://mochitests/content/chrome/content/xbl/test/file_bug944407.xml#testAllowScript)"</div>
<script>/* Flush layout with a script tab - see bug 944407 comment 37. */</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,78 @@
<?xml version="1.0"?>
<bindings id="testBindings" xmlns="http://www.mozilla.org/xbl"
xmlns:html="http://www.w3.org/1999/xhtml">
<binding id="testAllowScript">
<implementation>
<property name="someProp" onget="return 2;" readonly="true"></property>
<method name="someMethod"><body> return 3; </body></method>
<method name="startTest">
<body>
<![CDATA[
// Make sure we only get constructed when we're loaded from a domain
// with script enabled.
is(this.id, 'allow', "XBL should only be bound when the origin of the binding allows scripts");
var t = this;
doFinish = function() {
// Take a moment to make sure that other constructors don't run when they shouldn't.
if (t.id == 'allow')
setTimeout(SpecialPowers.wrap(window.parent).finish, 100);
}
onTestEvent = function(target) {
ok(true, 'called event handler');
// First, dispatch an event to the anonymous content. The event
// handlers on the AC should run, but they won't until bug 948000
// is fixed. So the check here is a todo().
var e = new MouseEvent('click');
document.getAnonymousNodes(target)[1].dispatchEvent(e);
// Now, dispatch a key event to test key handlers and move the test along.
var k = document.createEvent('KeyboardEvent');
k.initEvent('keyup', true, true);
target.dispatchEvent(k);
}
// Check the implementation.
is(this.someProp, 2, "Properties work");
is(this.someMethod(), 3, "Methods work");
// Kick over to the event handlers. This tests XBL event handlers,
// XBL key handlers, and event handlers on anonymous content.
this.dispatchEvent(new CustomEvent('testEvent'));
]]>
</body>
</method>
<constructor>
<![CDATA[
win = XPCNativeWrapper.unwrap(window);
SpecialPowers = win.SpecialPowers;
ok = win.ok = SpecialPowers.wrap(window.parent).ok;
todo = win.todo = SpecialPowers.wrap(window.parent).todo;
is = win.is = SpecialPowers.wrap(window.parent).is;
info = win.info = SpecialPowers.wrap(window.parent).info;
info("Invoked constructor for " + this.id);
var t = this;
window.addEventListener('load', function loadListener() {
window.removeEventListener('load', loadListener);
// Wait two refresh-driver ticks to make sure that the constructor runs
// for both |allow| and |deny| if it's ever going to.
//
// See bug 944407 comment 37.
info("Invoked load listener for " + t.id);
window.requestAnimationFrame(function() { window.requestAnimationFrame(t.startTest.bind(t)); });
});
]]>
</constructor>
</implementation>
<handlers>
<handler event="testEvent" action="onTestEvent(this)" allowuntrusted="true"/>
<handler event="keyup" action="ok(true, 'called key handler'); doFinish();" allowuntrusted="true"/>
</handlers>
<content>Anonymous Content<html:div onclick="todo(true, 'called event handler on ac, this needs bug 948000');"></html:div><html:b style="display:none"><children/></html:b></content>
</binding>
</bindings>

Просмотреть файл

@ -12,6 +12,8 @@ support-files =
file_bug591198_xbl.xml file_bug591198_xbl.xml
file_bug821850.xhtml file_bug821850.xhtml
file_bug844783.xhtml file_bug844783.xhtml
file_bug944407.html
file_bug944407.xml
[test_bug310107.html] [test_bug310107.html]
[test_bug366770.html] [test_bug366770.html]

Просмотреть файл

@ -0,0 +1,44 @@
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=944407
-->
<window title="Mozilla Bug 944407"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=944407"
target="_blank">Mozilla Bug 944407</a>
</body>
<!-- test code goes here -->
<script type="application/javascript">
<![CDATA[
/** Test for XBL bindings with script disabled. **/
SimpleTest.waitForExplicitFinish();
const Cu = Components.utils;
Cu.import('resource://gre/modules/Services.jsm');
function go() {
// Disable javascript, and load the frame.
function loadFrame() {
ok(!Services.prefs.getBoolPref('javascript.enabled'), "Javascript should be disabled");
$('ifr').setAttribute('src', 'http://mochi.test:8888/tests/content/xbl/test/file_bug944407.html');
}
SpecialPowers.pushPrefEnv({ set: [['javascript.enabled', false]] }, loadFrame);
}
function finish() {
SimpleTest.finish();
}
addLoadEvent(go);
]]>
</script>
<iframe id='ifr' />
</window>

Просмотреть файл

@ -1703,14 +1703,16 @@ Navigator::HasMobileMessageSupport(JSContext* /* unused */, JSObject* aGlobal)
/* static */ /* static */
bool bool
Navigator::HasTelephonySupport(JSContext* /* unused */, JSObject* aGlobal) Navigator::HasTelephonySupport(JSContext* cx, JSObject* aGlobal)
{ {
JS::Rooted<JSObject*> global(cx, aGlobal);
// First of all, the general pref has to be turned on. // First of all, the general pref has to be turned on.
bool enabled = false; bool enabled = false;
Preferences::GetBool("dom.telephony.enabled", &enabled); Preferences::GetBool("dom.telephony.enabled", &enabled);
NS_ENSURE_TRUE(enabled, false); NS_ENSURE_TRUE(enabled, false);
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal); nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(global);
return win && CheckPermission(win, "telephony"); return win && CheckPermission(win, "telephony");
} }
@ -1854,8 +1856,10 @@ bool Navigator::HasInputMethodSupport(JSContext* /* unused */,
/* static */ /* static */
bool bool
Navigator::HasDataStoreSupport(JSContext* /* unused */, JSObject* aGlobal) Navigator::HasDataStoreSupport(JSContext* cx, JSObject* aGlobal)
{ {
JS::Rooted<JSObject*> global(cx, aGlobal);
// First of all, the general pref has to be turned on. // First of all, the general pref has to be turned on.
bool enabled = false; bool enabled = false;
Preferences::GetBool("dom.datastore.enabled", &enabled); Preferences::GetBool("dom.datastore.enabled", &enabled);
@ -1866,7 +1870,7 @@ Navigator::HasDataStoreSupport(JSContext* /* unused */, JSObject* aGlobal)
return true; return true;
} }
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal); nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(global);
if (!win) { if (!win) {
return false; return false;
} }

Просмотреть файл

@ -250,7 +250,7 @@ public:
} }
static bool HasMobileMessageSupport(JSContext* /* unused */, static bool HasMobileMessageSupport(JSContext* /* unused */,
JSObject* aGlobal); JSObject* aGlobal);
static bool HasTelephonySupport(JSContext* /* unused */, static bool HasTelephonySupport(JSContext* cx,
JSObject* aGlobal); JSObject* aGlobal);
static bool HasCameraSupport(JSContext* /* unused */, static bool HasCameraSupport(JSContext* /* unused */,
JSObject* aGlobal); JSObject* aGlobal);
@ -286,7 +286,7 @@ public:
static bool HasInputMethodSupport(JSContext* /* unused */, JSObject* aGlobal); static bool HasInputMethodSupport(JSContext* /* unused */, JSObject* aGlobal);
static bool HasDataStoreSupport(JSContext* /* unused */, JSObject* aGlobal); static bool HasDataStoreSupport(JSContext* cx, JSObject* aGlobal);
nsPIDOMWindow* GetParentObject() const nsPIDOMWindow* GetParentObject() const
{ {

Просмотреть файл

@ -1,98 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import cPickle
from Configuration import Configuration
from Codegen import CGBindingRoot, replaceFileIfChanged, CGEventRoot
from mozbuild.makeutil import Makefile
from mozbuild.pythonutil import iter_modules_in_path
from buildconfig import topsrcdir
def generate_binding_files(config, outputprefix, srcprefix, webidlfile,
generatedEventsWebIDLFiles):
"""
|config| Is the configuration object.
|outputprefix| is a prefix to use for the header guards and filename.
"""
depsname = ".deps/" + outputprefix + ".pp"
root = CGBindingRoot(config, outputprefix, webidlfile)
replaceFileIfChanged(outputprefix + ".h", root.declare())
replaceFileIfChanged(outputprefix + ".cpp", root.define())
if webidlfile in generatedEventsWebIDLFiles:
eventName = webidlfile[:-len(".webidl")]
generatedEvent = CGEventRoot(config, eventName)
replaceFileIfChanged(eventName + ".h", generatedEvent.declare())
replaceFileIfChanged(eventName + ".cpp", generatedEvent.define())
mk = Makefile()
# NOTE: it's VERY important that we output dependencies for the FooBinding
# file here, not for the header or generated cpp file. These dependencies
# are used later to properly determine changedDeps and prevent rebuilding
# too much. See the comment explaining $(binding_dependency_trackers) in
# Makefile.in.
rule = mk.create_rule([outputprefix])
rule.add_dependencies(os.path.join(srcprefix, x) for x in sorted(root.deps()))
rule.add_dependencies(iter_modules_in_path(topsrcdir))
with open(depsname, 'w') as f:
mk.dump(f)
def main():
# Parse arguments.
from optparse import OptionParser
usagestring = "usage: %prog [header|cpp] configFile outputPrefix srcPrefix webIDLFile"
o = OptionParser(usage=usagestring)
o.add_option("--verbose-errors", action='store_true', default=False,
help="When an error happens, display the Python traceback.")
(options, args) = o.parse_args()
configFile = os.path.normpath(args[0])
srcPrefix = os.path.normpath(args[1])
# Load the configuration
f = open('ParserResults.pkl', 'rb')
config = cPickle.load(f)
f.close()
def readFile(f):
file = open(f, 'rb')
try:
contents = file.read()
finally:
file.close()
return contents
allWebIDLFiles = readFile(args[2]).split()
generatedEventsWebIDLFiles = readFile(args[3]).split()
changedDeps = readFile(args[4]).split()
if all(f.endswith("Binding") or f == "ParserResults.pkl" for f in changedDeps):
toRegenerate = filter(lambda f: f.endswith("Binding"), changedDeps)
if len(toRegenerate) == 0 and len(changedDeps) == 1:
# Work around build system bug 874923: if we get here that means
# that changedDeps contained only one entry and it was
# "ParserResults.pkl". That should never happen: if the
# ParserResults.pkl changes then either one of the globalgen files
# changed (in which case we wouldn't be in this "only
# ParserResults.pkl and *Binding changed" code) or some .webidl
# files changed (and then the corresponding *Binding files should
# show up in changedDeps). Since clearly the build system is
# confused, just regenerate everything to be safe.
toRegenerate = allWebIDLFiles
else:
toRegenerate = map(lambda f: f[:-len("Binding")] + ".webidl",
toRegenerate)
else:
toRegenerate = allWebIDLFiles
for webIDLFile in toRegenerate:
assert webIDLFile.endswith(".webidl")
outputPrefix = webIDLFile[:-len(".webidl")] + "Binding"
generate_binding_files(config, outputPrefix, srcPrefix, webIDLFile,
generatedEventsWebIDLFiles);
if __name__ == '__main__':
main()

Просмотреть файл

@ -29,27 +29,6 @@ INSTANCE_RESERVED_SLOTS = 3
def memberReservedSlot(member): def memberReservedSlot(member):
return "(DOM_INSTANCE_RESERVED_SLOTS + %d)" % member.slotIndex return "(DOM_INSTANCE_RESERVED_SLOTS + %d)" % member.slotIndex
def replaceFileIfChanged(filename, newContents):
"""
Read a copy of the old file, so that we don't touch it if it hasn't changed.
Returns True if the file was updated, false otherwise.
"""
oldFileContents = ""
try:
oldFile = open(filename, 'rb')
oldFileContents = ''.join(oldFile.readlines())
oldFile.close()
except:
pass
if newContents == oldFileContents:
return False
f = open(filename, 'wb')
f.write(newContents)
f.close()
return True
def toStringBool(arg): def toStringBool(arg):
return str(not not arg).lower() return str(not not arg).lower()

Просмотреть файл

@ -1,46 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import cPickle
from Configuration import Configuration
from Codegen import CGExampleRoot, replaceFileIfChanged
def generate_interface_example(config, interfaceName):
"""
|config| Is the configuration object.
|interfaceName| is the name of the interface we're generating an example for.
"""
root = CGExampleRoot(config, interfaceName)
exampleHeader = interfaceName + "-example.h"
exampleImpl = interfaceName + "-example.cpp"
replaceFileIfChanged(exampleHeader, root.declare())
replaceFileIfChanged(exampleImpl, root.define())
def main():
# Parse arguments.
from optparse import OptionParser
usagestring = "usage: %prog configFile interfaceName"
o = OptionParser(usage=usagestring)
o.add_option("--verbose-errors", action='store_true', default=False,
help="When an error happens, display the Python traceback.")
(options, args) = o.parse_args()
if len(args) != 2:
o.error(usagestring)
configFile = os.path.normpath(args[0])
interfaceName = args[1]
# Load the configuration
f = open('ParserResults.pkl', 'rb')
config = cPickle.load(f)
f.close()
# Generate the example class.
generate_interface_example(config, interfaceName)
if __name__ == '__main__':
main()

Просмотреть файл

@ -1,81 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
# We do one global pass over all the WebIDL to generate our prototype enum
# and generate information for subsequent phases.
import os
import WebIDL
import cPickle
from Configuration import Configuration
from Codegen import GlobalGenRoots, replaceFileIfChanged
def generate_file(config, name, action):
root = getattr(GlobalGenRoots, name)(config)
if action is 'declare':
filename = name + '.h'
code = root.declare()
else:
assert action is 'define'
filename = name + '.cpp'
code = root.define()
if replaceFileIfChanged(filename, code):
print "Generating %s" % (filename)
else:
print "%s hasn't changed - not touching it" % (filename)
def main():
# Parse arguments.
from optparse import OptionParser
usageString = "usage: %prog [options] webidldir [files]"
o = OptionParser(usage=usageString)
o.add_option("--cachedir", dest='cachedir', default=None,
help="Directory in which to cache lex/parse tables.")
o.add_option("--verbose-errors", action='store_true', default=False,
help="When an error happens, display the Python traceback.")
(options, args) = o.parse_args()
if len(args) < 2:
o.error(usageString)
configFile = args[0]
baseDir = args[1]
fileList = args[2:]
# Parse the WebIDL.
parser = WebIDL.Parser(options.cachedir)
for filename in fileList:
fullPath = os.path.normpath(os.path.join(baseDir, filename))
f = open(fullPath, 'rb')
lines = f.readlines()
f.close()
parser.parse(''.join(lines), fullPath)
parserResults = parser.finish()
# Load the configuration.
config = Configuration(configFile, parserResults)
# Write the configuration out to a pickle.
resultsFile = open('ParserResults.pkl', 'wb')
cPickle.dump(config, resultsFile, -1)
resultsFile.close()
# Generate the atom list.
generate_file(config, 'GeneratedAtomList', 'declare')
# Generate the prototype list.
generate_file(config, 'PrototypeList', 'declare')
# Generate the common code.
generate_file(config, 'RegisterBindings', 'declare')
generate_file(config, 'RegisterBindings', 'define')
generate_file(config, 'UnionTypes', 'declare')
generate_file(config, 'UnionTypes', 'define')
generate_file(config, 'UnionConversions', 'declare')
if __name__ == '__main__':
main()

Просмотреть файл

@ -1,243 +1,85 @@
# This Source Code Form is subject to the terms of the Mozilla Public # 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, # License, v. 2.0. If a copy of the MPL was not distributed with this
# You can obtain one at http://mozilla.org/MPL/2.0/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
abs_dist := $(abspath $(DIST))
webidl_base := $(topsrcdir)/dom/webidl
webidl_base = $(topsrcdir)/dom/webidl
# Generated by moz.build # Generated by moz.build
include webidlsrcs.mk include webidlsrcs.mk
binding_include_path := mozilla/dom ifdef GNU_CC
webidl_files += $(generated_events_webidl_files) OS_CXXFLAGS += -Wno-uninitialized
all_webidl_files = $(webidl_files) $(generated_webidl_files) $(preprocessed_webidl_files)
# Set exported_binding_headers before adding the test IDL to the mix
exported_binding_headers := $(subst .webidl,Binding.h,$(all_webidl_files))
exported_generated_events_headers := $(subst .webidl,.h,$(generated_events_webidl_files))
# Set linked_binding_cpp_files before adding the test IDL to the mix
linked_binding_cpp_files := $(subst .webidl,Binding.cpp,$(all_webidl_files))
linked_generated_events_cpp_files := $(subst .webidl,.cpp,$(generated_events_webidl_files))
all_webidl_files += $(test_webidl_files) $(preprocessed_test_webidl_files)
generated_header_files := $(subst .webidl,Binding.h,$(all_webidl_files)) $(exported_generated_events_headers)
generated_cpp_files := $(subst .webidl,Binding.cpp,$(all_webidl_files)) $(linked_generated_events_cpp_files)
# We want to be able to only regenerate the .cpp and .h files that really need
# to change when a .webidl file changes. We do this by making the
# binding_dependency_trackers targets have dependencies on the right .webidl
# files via generated .pp files, having a .BindingGen target that depends on the
# binding_dependency_trackers and which has all the generated binding .h/.cpp
# depending on it, and then in the make commands for that target being able to
# check which exact binding_dependency_trackers changed.
binding_dependency_trackers := $(subst .webidl,Binding,$(all_webidl_files))
globalgen_targets := \
GeneratedAtomList.h \
PrototypeList.h \
RegisterBindings.h \
RegisterBindings.cpp \
UnionTypes.h \
UnionTypes.cpp \
UnionConversions.h \
$(NULL)
# Nasty hack: when the test/Makefile.in invokes us to do codegen, it
# uses a target of
# "export TestExampleInterface-example TestExampleProxyInterface-example".
# We don't actually need to load our .o.pp files in that case, so just
# pretend like we have no CPPSRCS if that's the target. It makes the
# test cycle much faster, which is why we're doing it.
#
# XXXbz We could try to cheat even more and only include our CPPSRCS
# when $(MAKECMDGOALS) contains libs, so that we can skip loading all
# those .o.pp when trying to make a single .cpp file too, but that
# would break |make FooBinding.o(bj)|. Ah, well.
ifneq (export TestExampleInterface-example TestExampleProxyInterface-example,$(MAKECMDGOALS))
CPPSRCS = \
$(unified_binding_cpp_files) \
$(linked_generated_events_cpp_files) \
$(filter %.cpp, $(globalgen_targets)) \
$(NULL)
endif endif
ABS_DIST := $(abspath $(DIST)) # These come from webidlsrcs.mk.
# TODO Write directly into backend.mk.
CPPSRCS += $(globalgen_sources) $(unified_binding_cpp_files)
EXTRA_EXPORT_MDDEPEND_FILES := $(addsuffix .pp,$(binding_dependency_trackers)) # Generated bindings reference *Binding.h, not mozilla/dom/*Binding.h. And,
# since we generate exported bindings directly to $(DIST)/include, we need
# to add that path to the search list.
#
# Ideally, binding generation uses the prefixed header file names.
# Bug 932092 tracks.
LOCAL_INCLUDES += -I$(DIST)/include/mozilla/dom
EXPORTS_GENERATED_FILES := $(exported_binding_headers) $(exported_generated_events_headers) PYTHON_UNIT_TESTS += $(srcdir)/mozwebidlcodegen/test/test_mozwebidlcodegen.py
EXPORTS_GENERATED_DEST := $(ABS_DIST)/include/$(binding_include_path)
EXPORTS_GENERATED_TARGET := export
INSTALL_TARGETS += EXPORTS_GENERATED
# Install auto-generated GlobalGen files. The rules for the install must
# be in the same target/subtier as GlobalGen.py, otherwise the files will not
# get installed into the appropriate location as they are generated.
globalgen_headers_FILES := \
GeneratedAtomList.h \
PrototypeList.h \
RegisterBindings.h \
UnionConversions.h \
UnionTypes.h \
$(NULL)
globalgen_headers_DEST = $(ABS_DIST)/include/mozilla/dom
globalgen_headers_TARGET := export
INSTALL_TARGETS += globalgen_headers
include $(topsrcdir)/config/rules.mk include $(topsrcdir)/config/rules.mk
ifdef GNU_CC # TODO This list should be emitted to a .pp file via
CXXFLAGS += -Wno-uninitialized # GenerateCSS2PropertiesWebIDL.py.
endif css2properties_dependencies = \
$(topsrcdir)/layout/style/nsCSSPropList.h \
# If you change bindinggen_dependencies here, change it in
# dom/bindings/test/Makefile.in too.
bindinggen_dependencies := \
BindingGen.py \
Bindings.conf \
Configuration.py \
Codegen.py \
ParserResults.pkl \
parser/WebIDL.py \
$(GLOBAL_DEPS) \
$(NULL)
CSS2Properties.webidl: $(topsrcdir)/layout/style/nsCSSPropList.h \
$(topsrcdir)/layout/style/nsCSSPropAliasList.h \ $(topsrcdir)/layout/style/nsCSSPropAliasList.h \
$(webidl_base)/CSS2Properties.webidl.in \ $(webidl_base)/CSS2Properties.webidl.in \
$(webidl_base)/CSS2PropertiesProps.h \ $(webidl_base)/CSS2PropertiesProps.h \
$(srcdir)/GenerateCSS2PropertiesWebIDL.py \ $(srcdir)/GenerateCSS2PropertiesWebIDL.py \
$(GLOBAL_DEPS)
$(CPP) $(DEFINES) $(ACDEFINES) -I$(topsrcdir)/layout/style $(webidl_base)/CSS2PropertiesProps.h | \
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) \
$(srcdir)/GenerateCSS2PropertiesWebIDL.py $(webidl_base)/CSS2Properties.webidl.in > CSS2Properties.webidl
$(webidl_files): %: $(webidl_base)/%
$(INSTALL) $(IFLAGS1) $(webidl_base)/$* .
$(test_webidl_files): %: $(srcdir)/test/%
$(INSTALL) $(IFLAGS1) $(srcdir)/test/$* .
# We can't easily use PP_TARGETS here because it insists on outputting targets
# that look like "$(CURDIR)/foo" whereas we want our target to just be "foo".
# Make sure to include $(GLOBAL_DEPS) so we pick up changes to what symbols are
# defined. Also make sure to remove $@ before writing to it, because otherwise
# if a file goes from non-preprocessed to preprocessed we can end up writing to
# a symlink, which will clobber files in the srcdir, which is bad.
$(preprocessed_webidl_files): %: $(webidl_base)/% $(GLOBAL_DEPS)
$(RM) $@
$(call py_action,preprocessor, \
$(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $(webidl_base)/$* -o $@)
# See the comment about PP_TARGETS for $(preprocessed_webidl_files)
$(preprocessed_test_webidl_files): %: $(srcdir)/test/% $(GLOBAL_DEPS)
$(RM) $@
$(call py_action,preprocessor, \
$(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $(srcdir)/test/$* -o $@)
# Make is dumb and can get confused between "foo" and "$(CURDIR)/foo". Make
# sure that the latter depends on the former, since the latter gets used in .pp
# files.
all_webidl_files_absolute = $(addprefix $(CURDIR)/,$(all_webidl_files))
$(all_webidl_files_absolute): $(CURDIR)/%: %
$(generated_header_files): .BindingGen
$(generated_cpp_files): .BindingGen
# $(binding_dependency_trackers) pick up additional dependencies via .pp files
# The rule: just brings the tracker up to date, if it's out of date, so that
# we'll know that we have to redo binding generation and flag this prerequisite
# there as being newer than the bindinggen target.
$(binding_dependency_trackers):
@$(TOUCH) $@
$(globalgen_targets): ParserResults.pkl
%-example: .BindingGen
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
$(PLY_INCLUDE) -I$(srcdir)/parser \
$(srcdir)/ExampleGen.py \
$(srcdir)/Bindings.conf $*
CACHE_DIR = _cache
globalgen_dependencies := \
GlobalGen.py \
Bindings.conf \
Configuration.py \
Codegen.py \
parser/WebIDL.py \
webidlsrcs.mk \
$(all_webidl_files) \
$(CACHE_DIR)/.done \
$(GLOBAL_DEPS) \ $(GLOBAL_DEPS) \
$(NULL) $(NULL)
$(CACHE_DIR)/.done: CSS2Properties.webidl: $(css2properties_dependencies)
$(MKDIR) -p $(CACHE_DIR) $(CPP) $(DEFINES) $(ACDEFINES) -I$(topsrcdir)/layout/style \
$(webidl_base)/CSS2PropertiesProps.h | \
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) \
$(srcdir)/GenerateCSS2PropertiesWebIDL.py \
$(webidl_base)/CSS2Properties.webidl.in > $@
# Most of the logic for dependencies lives inside Python so it can be
# used by multiple build backends. We simply have rules to generate
# and include the .pp file.
#
# The generated .pp file contains all the important dependencies such as
# changes to .webidl or .py files should result in code generation being
# performed.
codegen_dependencies := \
$(nonstatic_webidl_files) \
$(GLOBAL_DEPS) \
$(NULL)
$(call include_deps,codegen.pp)
codegen.pp: $(codegen_dependencies)
$(call py_action,webidl,$(srcdir))
@$(TOUCH) $@ @$(TOUCH) $@
# Running GlobalGen.py updates ParserResults.pkl as a side-effect .PHONY: compiletests
ParserResults.pkl: $(globalgen_dependencies) compiletests:
$(info Generating global WebIDL files) $(call SUBMAKE,libs,test)
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
$(PLY_INCLUDE) -I$(srcdir)/parser \
$(srcdir)/GlobalGen.py $(srcdir)/Bindings.conf . \
--cachedir=$(CACHE_DIR) \
$(all_webidl_files)
$(globalgen_headers_FILES): ParserResults.pkl
# Make sure .deps actually exists, since we'll try to write to it from
# BindingGen.py but we're typically running in the export phase, which is
# before anyone has bothered creating .deps.
# Then, pass our long lists through files to try to avoid blowing out the
# command line.
# Next, BindingGen.py will examine the changed dependency list to figure out
# what it really needs to regenerate.
# Finally, touch the .BindingGen file so that we don't have to keep redoing
# all that until something else actually changes.
.BindingGen: $(bindinggen_dependencies) $(binding_dependency_trackers)
$(info Generating WebIDL bindings)
$(MKDIR) -p .deps
echo $(all_webidl_files) > .all-webidl-file-list
echo $(generated_events_webidl_files) > .generated-events-webidl-files
echo $? > .changed-dependency-list
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
$(PLY_INCLUDE) -I$(srcdir)/parser \
$(srcdir)/BindingGen.py \
$(srcdir)/Bindings.conf \
$(CURDIR) \
.all-webidl-file-list \
.generated-events-webidl-files \
.changed-dependency-list
@$(TOUCH) $@
GARBAGE += \ GARBAGE += \
webidlyacc.py \ codegen.pp \
codegen.json \
parser.out \ parser.out \
$(wildcard *-example.h) \ WebIDLGrammar.pkl \
$(wildcard *-example.cpp) \ $(wildcard *.h) \
.BindingGen \ $(wildcard *Binding.cpp) \
.all-webidl-file-list \ $(wildcard *Event.cpp) \
.generated-events-webidl-files \ $(wildcard *-event.cpp) \
.changed-dependency-list \ $(wildcard *.webidl) \
$(binding_dependency_trackers) \
$(NULL) $(NULL)
# Make sure all binding header files are created during the export stage, so we DIST_GARBAGE += \
# don't have issues with .cpp files being compiled before we've generated the file-lists.json \
# headers they depend on. This is really only needed for the test files, since
# the non-test headers are all exported above anyway. Note that this means that
# we do all of our codegen during export.
export:: $(generated_header_files)
distclean::
-$(RM) \
$(generated_header_files) \
$(generated_cpp_files) \
$(all_webidl_files) \
$(globalgen_targets) \
ParserResults.pkl \
$(NULL) $(NULL)

122
dom/bindings/docs/index.rst Normal file
Просмотреть файл

@ -0,0 +1,122 @@
.. _webidl:
======
WebIDL
======
WebIDL describes interfaces web browsers are supposed to implement.
The interaction between WebIDL and the build system is somewhat complex.
This document will attempt to explain how it all works.
Overview
========
``.webidl`` files throughout the tree define interfaces the browser
implements. Since Gecko/Firefox is implemented in C++, there is a
mechanism to convert these interfaces and associated metadata to
C++ code. That's where the build system comes into play.
All the code for interacting with ``.webidl`` files lives under
``dom/bindings``. There is code in the build system to deal with
WebIDLs explicitly.
WebIDL source file flavors
==========================
Not all ``.webidl`` files are created equal! There are several flavors,
each represented by a separate symbol from :ref:`mozbuild_symbols`.
WEBIDL_FILES
Refers to regular/static ``.webidl`` files. Most WebIDL interfaces
are defined this way.
GENERATED_EVENTS_WEBIDL_FILES
In addition to generating a binding, these ``.webidl`` files also
generate a source file implementing the event object in C++
PREPROCESSED_WEBIDL_FILES
The ``.webidl`` files are generated by preprocessing an input file.
They otherwise behave like *WEBIDL_FILES*.
TEST_WEBIDL_FILES
Like *WEBIDL_FILES* but the interfaces are for testing only and
aren't shipped with the browser.
PREPROCESSED_TEST_WEBIDL_FILES
Like *TEST_WEBIDL_FILES* except the ``.webidl`` is obtained via
preprocessing, much like *PREPROCESSED_WEBIDL_FILES*.
GENERATED_WEBIDL_FILES
The ``.webidl`` for these is obtained through an *external*
mechanism. Typically there are custom build rules for producing these
files.
Producing C++ code
==================
The most complicated part about WebIDLs is the process by which
``.webidl`` files are converted into C++.
This process is handled by code in the :py:mod:`mozwebidlcodegen`
package. :py:class:`mozwebidlcodegen.WebIDLCodegenManager` is
specifically where you want to look for how code generation is
performed. This includes complex dependency management.
Requirements
============
This section aims to document the build and developer workflow requirements
for WebIDL.
Parser unit tests
There are parser tests provided by ``dom/bindings/parser/runtests.py``
that should run as part of ``make check``. There must be a mechanism
to run the tests in *human* mode so they output friendly error
messages.
The current mechanism for this is ``mach webidl-parser-test``.
Mochitests
There are various mochitests under ``dom/bindings/test``. They should
be runnable through the standard mechanisms.
Working with test interfaces
``TestExampleGenBinding.cpp`` calls into methods from the
``TestExampleInterface`` and ``TestExampleProxyInterface`` interfaces.
These interfaces need to be generated as part of the build. These
interfaces should not be exported or packaged.
There is a ``compiletests`` make target in ``dom/bindings`` that
isn't part of the build that facilitates turnkey code generation
and test file compilation.
Minimal rebuilds
Reprocessing every output for every change is expensive. So we don't
inconvenience people changing ``.webidl`` files, the build system
should only perform a minimal rebuild when sources change.
This logic is mostly all handled in
:py:class:`mozwebidlcodegen.WebIDLCodegenManager`. The unit tests for
that Python code should adequately test typical rebuild scenarios.
Bug 940469 tracks making the existing implementation better.
Explicit method for performing codegen
There needs to be an explicit method for invoking code generation.
It needs to cover regular and test files.
This is implemented via ``make export`` in ``dom/bindings``.
No-op binding generation should be fast
So developers touching ``.webidl`` files are not inconvenienced,
no-op binding generation should be fast. Watch out for the build system
processing large dependency files it doesn't need in order to perform
code generation.
Ability to generate example files
*Any* interface can have example ``.h``/``.cpp`` files generated.
There must be a mechanism to facilitate this.
This is currently facilitated through ``mach webidl-example``. e.g.
``mach webidl-example HTMLStyleElement``.

Просмотреть файл

@ -18,6 +18,17 @@ from mozbuild.base import MachCommandBase
@CommandProvider @CommandProvider
class WebIDLProvider(MachCommandBase): class WebIDLProvider(MachCommandBase):
@Command('webidl-example', category='misc',
description='Generate example files for a WebIDL interface.')
@CommandArgument('interface', nargs='+',
help='Interface(s) whose examples to generate.')
def webidl_example(self, interface):
from mozwebidlcodegen import BuildSystemWebIDL
manager = self._spawn(BuildSystemWebIDL).manager
for i in interface:
manager.generate_example_files(i)
@Command('webidl-parser-test', category='testing', @Command('webidl-parser-test', category='testing',
description='Run WebIDL tests.') description='Run WebIDL tests.')
@CommandArgument('--verbose', '-v', action='store_true', @CommandArgument('--verbose', '-v', action='store_true',

Просмотреть файл

@ -4,6 +4,8 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # 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/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
TEST_DIRS += ['test']
EXPORTS.mozilla += [ EXPORTS.mozilla += [
'ErrorResult.h', 'ErrorResult.h',
] ]
@ -85,3 +87,6 @@ if CONFIG['MOZ_AUDIO_CHANNEL_MANAGER']:
] ]
FINAL_LIBRARY = 'xul' FINAL_LIBRARY = 'xul'
SPHINX_TREES['webidl'] = 'docs'
SPHINX_PYTHON_PACKAGE_DIRS += ['mozwebidlcodegen']

Просмотреть файл

@ -0,0 +1,565 @@
# 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/.
# This module contains code for managing WebIDL files and bindings for
# the build system.
from __future__ import unicode_literals
import errno
import hashlib
import json
import logging
import os
from copy import deepcopy
from mach.mixin.logging import LoggingMixin
from mozbuild.base import MozbuildObject
from mozbuild.makeutil import Makefile
from mozbuild.pythonutil import iter_modules_in_path
from mozbuild.util import FileAvoidWrite
import mozpack.path as mozpath
import WebIDL
from Codegen import (
CGBindingRoot,
CGEventRoot,
CGExampleRoot,
GlobalGenRoots,
)
from Configuration import Configuration
class BuildResult(object):
"""Represents the result of processing WebIDL files.
This holds a summary of output file generation during code generation.
"""
def __init__(self):
# The .webidl files that had their outputs regenerated.
self.inputs = set()
# The output files that were created.
self.created = set()
# The output files that changed.
self.updated = set()
# The output files that didn't change.
self.unchanged = set()
class WebIDLCodegenManagerState(dict):
"""Holds state for the WebIDL code generation manager.
State is currently just an extended dict. The internal implementation of
state should be considered a black box to everyone except
WebIDLCodegenManager. But we'll still document it.
Fields:
version
The integer version of the format. This is to detect incompatible
changes between state. It should be bumped whenever the format
changes or semantics change.
webidls
A dictionary holding information about every known WebIDL input.
Keys are the basenames of input WebIDL files. Values are dicts of
metadata. Keys in those dicts are:
* filename - The full path to the input filename.
* inputs - A set of full paths to other webidl files this webidl
depends on.
* outputs - Set of full output paths that are created/derived from
this file.
* sha1 - The hexidecimal SHA-1 of the input filename from the last
processing time.
global_inputs
A dictionary defining files that influence all processing. Keys
are full filenames. Values are hexidecimal SHA-1 from the last
processing time.
"""
VERSION = 1
def __init__(self, fh=None):
self['version'] = self.VERSION
self['webidls'] = {}
self['global_depends'] = {}
if not fh:
return
state = json.load(fh)
if state['version'] != self.VERSION:
raise Exception('Unknown state version: %s' % state['version'])
self['version'] = state['version']
self['global_depends'] = state['global_depends']
for k, v in state['webidls'].items():
self['webidls'][k] = v
# Sets are converted to lists for serialization because JSON
# doesn't support sets.
self['webidls'][k]['inputs'] = set(v['inputs'])
self['webidls'][k]['outputs'] = set(v['outputs'])
def dump(self, fh):
"""Dump serialized state to a file handle."""
normalized = deepcopy(self)
for k, v in self['webidls'].items():
# Convert sets to lists because JSON doesn't support sets.
normalized['webidls'][k]['outputs'] = sorted(v['outputs'])
normalized['webidls'][k]['inputs'] = sorted(v['inputs'])
json.dump(normalized, fh, sort_keys=True)
class WebIDLCodegenManager(LoggingMixin):
"""Manages all code generation around WebIDL.
To facilitate testing, this object is meant to be generic and reusable.
Paths, etc should be parameters and not hardcoded.
"""
# Global parser derived declaration files.
GLOBAL_DECLARE_FILES = {
'GeneratedAtomList.h',
'PrototypeList.h',
'RegisterBindings.h',
'UnionConversions.h',
'UnionTypes.h',
}
# Global parser derived definition files.
GLOBAL_DEFINE_FILES = {
'RegisterBindings.cpp',
'UnionTypes.cpp',
}
# Example interfaces to build along with the tree. Other example
# interfaces will need to be generated manually.
BUILD_EXAMPLE_INTERFACES = {
'TestExampleInterface',
'TestExampleProxyInterface',
}
def __init__(self, config_path, inputs, exported_header_dir,
codegen_dir, state_path, cache_dir=None, make_deps_path=None,
make_deps_target=None):
"""Create an instance that manages WebIDLs in the build system.
config_path refers to a WebIDL config file (e.g. Bindings.conf).
inputs is a 3-tuple describing the input .webidl files and how to
process them. Members are:
(set(.webidl files), set(basenames of exported files),
set(basenames of generated events files))
exported_header_dir and codegen_dir are directories where generated
files will be written to.
state_path is the path to a file that will receive JSON state from our
actions.
make_deps_path is the path to a make dependency file that we can
optionally write.
make_deps_target is the target that receives the make dependencies. It
must be defined if using make_deps_path.
"""
self.populate_logger()
input_paths, exported_stems, generated_events_stems = inputs
self._config_path = config_path
self._input_paths = set(input_paths)
self._exported_stems = set(exported_stems)
self._generated_events_stems = set(generated_events_stems)
self._exported_header_dir = exported_header_dir
self._codegen_dir = codegen_dir
self._state_path = state_path
self._cache_dir = cache_dir
self._make_deps_path = make_deps_path
self._make_deps_target = make_deps_target
if (make_deps_path and not make_deps_target) or (not make_deps_path and
make_deps_target):
raise Exception('Must define both make_deps_path and make_deps_target '
'if one is defined.')
self._parser_results = None
self._config = None
self._state = WebIDLCodegenManagerState()
if os.path.exists(state_path):
with open(state_path, 'rb') as fh:
try:
self._state = WebIDLCodegenManagerState(fh=fh)
except Exception as e:
self.log(logging.WARN, 'webidl_bad_state', {'msg': str(e)},
'Bad WebIDL state: {msg}')
@property
def config(self):
if not self._config:
self._parse_webidl()
return self._config
def generate_build_files(self):
"""Generate files required for the build.
This function is in charge of generating all the .h/.cpp files derived
from input .webidl files. Please note that there are build actions
required to produce .webidl files and these build actions are
explicitly not captured here: this function assumes all .webidl files
are present and up to date.
This routine is called as part of the build to ensure files that need
to exist are present and up to date. This routine may not be called if
the build dependencies (generated as a result of calling this the first
time) say everything is up to date.
Because reprocessing outputs for every .webidl on every invocation
is expensive, we only regenerate the minimal set of files on every
invocation. The rules for deciding what needs done are roughly as
follows:
1. If any .webidl changes, reparse all .webidl files and regenerate
the global derived files. Only regenerate output files (.h/.cpp)
impacted by the modified .webidl files.
2. If an non-.webidl dependency (Python files, config file) changes,
assume everything is out of date and regenerate the world. This
is because changes in those could globally impact every output
file.
3. If an output file is missing, ensure it is present by performing
necessary regeneration.
"""
# Despite #1 above, we assume the build system is smart enough to not
# invoke us if nothing has changed. Therefore, any invocation means
# something has changed. And, if anything has changed, we need to
# parse the WebIDL.
self._parse_webidl()
result = BuildResult()
# If we parse, we always update globals - they are cheap and it is
# easier that way.
created, updated, unchanged = self._write_global_derived()
result.created |= created
result.updated |= updated
result.unchanged |= unchanged
# If any of the extra dependencies changed, regenerate the world.
global_changed, global_hashes = self._global_dependencies_changed()
if global_changed:
# Make a copy because we may modify.
changed_inputs = set(self._input_paths)
else:
changed_inputs = self._compute_changed_inputs()
self._state['global_depends'] = global_hashes
# Generate bindings from .webidl files.
for filename in sorted(changed_inputs):
basename = mozpath.basename(filename)
result.inputs.add(filename)
written, deps = self._generate_build_files_for_webidl(filename)
result.created |= written[0]
result.updated |= written[1]
result.unchanged |= written[2]
self._state['webidls'][basename] = dict(
filename=filename,
outputs=written[0] | written[1] | written[2],
inputs=set(deps),
sha1=self._input_hashes[filename],
)
# Process some special interfaces required for testing.
for interface in self.BUILD_EXAMPLE_INTERFACES:
written = self.generate_example_files(interface)
result.created |= written[0]
result.updated |= written[1]
result.unchanged |= written[2]
# Generate a make dependency file.
if self._make_deps_path:
mk = Makefile()
codegen_rule = mk.create_rule([self._make_deps_target])
codegen_rule.add_dependencies(global_hashes.keys())
codegen_rule.add_dependencies(self._input_paths)
with FileAvoidWrite(self._make_deps_path) as fh:
mk.dump(fh)
self._save_state()
return result
def generate_example_files(self, interface):
"""Generates example files for a given interface."""
root = CGExampleRoot(self.config, interface)
return self._maybe_write_codegen(root, *self._example_paths(interface))
def _parse_webidl(self):
self.log(logging.INFO, 'webidl_parse',
{'count': len(self._input_paths)},
'Parsing {count} WebIDL files.')
hashes = {}
parser = WebIDL.Parser(self._cache_dir)
for path in sorted(self._input_paths):
with open(path, 'rb') as fh:
data = fh.read()
hashes[path] = hashlib.sha1(data).hexdigest()
parser.parse(data, path)
self._parser_results = parser.finish()
self._config = Configuration(self._config_path, self._parser_results)
self._input_hashes = hashes
def _write_global_derived(self):
things = [('declare', f) for f in self.GLOBAL_DECLARE_FILES]
things.extend(('define', f) for f in self.GLOBAL_DEFINE_FILES)
result = (set(), set(), set())
for what, filename in things:
stem = mozpath.splitext(filename)[0]
root = getattr(GlobalGenRoots, stem)(self._config)
if what == 'declare':
code = root.declare()
output_root = self._exported_header_dir
elif what == 'define':
code = root.define()
output_root = self._codegen_dir
else:
raise Exception('Unknown global gen type: %s' % what)
output_path = mozpath.join(output_root, filename)
self._maybe_write_file(output_path, code, result)
return result
def _compute_changed_inputs(self):
"""Compute the set of input files that need to be regenerated."""
changed_inputs = set()
expected_outputs = self.expected_build_output_files()
# Look for missing output files.
if any(not os.path.exists(f) for f in expected_outputs):
# FUTURE Bug 940469 Only regenerate minimum set.
changed_inputs |= self._input_paths
# That's it for examining output files. We /could/ examine SHA-1's of
# output files from a previous run to detect modifications. But that's
# a lot of extra work and most build systems don't do that anyway.
# Now we move on to the input files.
old_hashes = {v['filename']: v['sha1']
for v in self._state['webidls'].values()}
old_filenames = set(old_hashes.keys())
new_filenames = self._input_paths
# If an old file has disappeared or a new file has arrived, mark
# it.
changed_inputs |= old_filenames ^ new_filenames
# For the files in common between runs, compare content. If the file
# has changed, mark it. We don't need to perform mtime comparisons
# because content is a stronger validator.
for filename in old_filenames & new_filenames:
if old_hashes[filename] != self._input_hashes[filename]:
changed_inputs.add(filename)
# We've now populated the base set of inputs that have changed.
# Inherit dependencies from previous run. The full set of dependencies
# is associated with each record, so we don't need to perform any fancy
# graph traversal.
for v in self._state['webidls'].values():
if any(dep for dep in v['inputs'] if dep in changed_inputs):
changed_inputs.add(v['filename'])
# Ensure all changed inputs actually exist (some changed inputs could
# have been from deleted files).
return set(f for f in changed_inputs if os.path.exists(f))
def _binding_info(self, p):
"""Compute binding metadata for an input path.
Returns a tuple of:
(stem, binding_stem, is_event, output_files)
output_files is itself a tuple. The first two items are the binding
header and C++ paths, respectively. The 2nd pair are the event header
and C++ paths or None if this isn't an event binding.
"""
basename = mozpath.basename(p)
stem = mozpath.splitext(basename)[0]
binding_stem = '%sBinding' % stem
if stem in self._exported_stems:
header_dir = self._exported_header_dir
else:
header_dir = self._codegen_dir
is_event = stem in self._generated_events_stems
files = (
mozpath.join(header_dir, '%s.h' % binding_stem),
mozpath.join(self._codegen_dir, '%s.cpp' % binding_stem),
mozpath.join(header_dir, '%s.h' % stem) if is_event else None,
mozpath.join(self._codegen_dir, '%s.cpp' % stem) if is_event else None,
)
return stem, binding_stem, is_event, header_dir, files
def _example_paths(self, interface):
return (
mozpath.join(self._codegen_dir, '%s-example.h' % interface),
mozpath.join(self._codegen_dir, '%s-example.cpp' % interface))
def expected_build_output_files(self):
"""Obtain the set of files generate_build_files() should write."""
paths = set()
# Account for global generation.
for p in self.GLOBAL_DECLARE_FILES:
paths.add(mozpath.join(self._exported_header_dir, p))
for p in self.GLOBAL_DEFINE_FILES:
paths.add(mozpath.join(self._codegen_dir, p))
for p in self._input_paths:
stem, binding_stem, is_event, header_dir, files = self._binding_info(p)
paths |= {f for f in files if f}
for interface in self.BUILD_EXAMPLE_INTERFACES:
for p in self._example_paths(interface):
paths.add(p)
return paths
def _generate_build_files_for_webidl(self, filename):
self.log(logging.INFO, 'webidl_generate_build_for_input',
{'filename': filename},
'Generating WebIDL files derived from {filename}')
stem, binding_stem, is_event, header_dir, files = self._binding_info(filename)
root = CGBindingRoot(self._config, binding_stem, filename)
result = self._maybe_write_codegen(root, files[0], files[1])
if is_event:
generated_event = CGEventRoot(self._config, stem)
result = self._maybe_write_codegen(generated_event, files[2],
files[3], result)
return result, root.deps()
def _global_dependencies_changed(self):
"""Determine whether the global dependencies have changed."""
current_files = set(iter_modules_in_path(mozpath.dirname(__file__)))
# We need to catch other .py files from /dom/bindings. We assume these
# are in the same directory as the config file.
current_files |= set(iter_modules_in_path(mozpath.dirname(self._config_path)))
current_files.add(self._config_path)
current_hashes = {}
for f in current_files:
# This will fail if the file doesn't exist. If a current global
# dependency doesn't exist, something else is wrong.
with open(f, 'rb') as fh:
current_hashes[f] = hashlib.sha1(fh.read()).hexdigest()
# The set of files has changed.
if current_files ^ set(self._state['global_depends'].keys()):
return True, current_hashes
# Compare hashes.
for f, sha1 in current_hashes.items():
if sha1 != self._state['global_depends'][f]:
return True, current_hashes
return False, current_hashes
def _save_state(self):
with open(self._state_path, 'wb') as fh:
self._state.dump(fh)
def _maybe_write_codegen(self, obj, declare_path, define_path, result=None):
assert declare_path and define_path
if not result:
result = (set(), set(), set())
self._maybe_write_file(declare_path, obj.declare(), result)
self._maybe_write_file(define_path, obj.define(), result)
return result
def _maybe_write_file(self, path, content, result):
fh = FileAvoidWrite(path)
fh.write(content)
existed, updated = fh.close()
if not existed:
result[0].add(path)
elif updated:
result[1].add(path)
else:
result[2].add(path)
def create_build_system_manager(topsrcdir, topobjdir, dist_dir):
"""Create a WebIDLCodegenManager for use by the build system."""
src_dir = os.path.join(topsrcdir, 'dom', 'bindings')
obj_dir = os.path.join(topobjdir, 'dom', 'bindings')
with open(os.path.join(obj_dir, 'file-lists.json'), 'rb') as fh:
files = json.load(fh)
inputs = (files['webidls'], files['exported_stems'],
files['generated_events_stems'])
cache_dir = os.path.join(obj_dir, '_cache')
try:
os.makedirs(cache_dir)
except OSError as e:
if e.errno != errno.EEXIST:
raise
return WebIDLCodegenManager(
os.path.join(src_dir, 'Bindings.conf'),
inputs,
os.path.join(dist_dir, 'include', 'mozilla', 'dom'),
obj_dir,
os.path.join(obj_dir, 'codegen.json'),
cache_dir=cache_dir,
# The make rules include a codegen.pp file containing dependencies.
make_deps_path=os.path.join(obj_dir, 'codegen.pp'),
make_deps_target='codegen.pp',
)
class BuildSystemWebIDL(MozbuildObject):
@property
def manager(self):
if not hasattr(self, '_webidl_manager'):
self._webidl_manager = create_build_system_manager(
self.topsrcdir, self.topobjdir, self.distdir)
return self._webidl_manager

Просмотреть файл

@ -0,0 +1,3 @@
interface Child : Parent {
void ChildBaz();
};

Просмотреть файл

@ -0,0 +1,2 @@
interface DummyInterface {};
interface DummyInterfaceWorkers {};

Просмотреть файл

@ -0,0 +1,3 @@
/* These interfaces are hard-coded and need to be defined. */
interface TestExampleInterface {};
interface TestExampleProxyInterface {};

Просмотреть файл

@ -0,0 +1,3 @@
interface Parent {
void MethodFoo();
};

Просмотреть файл

@ -0,0 +1,13 @@
interface EventTarget {
void addEventListener();
};
interface Event {};
callback EventHandlerNonNull = any (Event event);
typedef EventHandlerNonNull? EventHandler;
[NoInterfaceObject]
interface TestEvent : EventTarget {
attribute EventHandler onfoo;
};

Просмотреть файл

@ -0,0 +1,278 @@
# 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/.
from __future__ import unicode_literals
import imp
import json
import os
import shutil
import sys
import tempfile
import unittest
import mozpack.path as mozpath
from mozwebidlcodegen import (
WebIDLCodegenManager,
WebIDLCodegenManagerState,
)
from mozfile import NamedTemporaryFile
from mozunit import (
MockedOpen,
main,
)
OUR_DIR = mozpath.abspath(mozpath.dirname(__file__))
TOPSRCDIR = mozpath.normpath(mozpath.join(OUR_DIR, '..', '..', '..', '..'))
class TestWebIDLCodegenManager(unittest.TestCase):
TEST_STEMS = {
'Child',
'Parent',
'ExampleBinding',
'TestEvent',
}
@property
def _static_input_paths(self):
s = {mozpath.join(OUR_DIR, p) for p in os.listdir(OUR_DIR)
if p.endswith('.webidl')}
return s
@property
def _config_path(self):
config = mozpath.join(TOPSRCDIR, 'dom', 'bindings', 'Bindings.conf')
self.assertTrue(os.path.exists(config))
return config
def _get_manager_args(self):
tmp = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, tmp)
cache_dir = mozpath.join(tmp, 'cache')
os.mkdir(cache_dir)
ip = self._static_input_paths
inputs = (
ip,
{mozpath.splitext(mozpath.basename(p))[0] for p in ip},
set()
)
return dict(
config_path=self._config_path,
inputs=inputs,
exported_header_dir=mozpath.join(tmp, 'exports'),
codegen_dir=mozpath.join(tmp, 'codegen'),
state_path=mozpath.join(tmp, 'state.json'),
make_deps_path=mozpath.join(tmp, 'codegen.pp'),
make_deps_target='codegen.pp',
cache_dir=cache_dir,
)
def _get_manager(self):
return WebIDLCodegenManager(**self._get_manager_args())
def test_unknown_state_version(self):
"""Loading a state file with a too new version resets state."""
args = self._get_manager_args()
p = args['state_path']
with open(p, 'wb') as fh:
json.dump({
'version': WebIDLCodegenManagerState.VERSION + 1,
'foobar': '1',
}, fh)
manager = WebIDLCodegenManager(**args)
self.assertEqual(manager._state['version'],
WebIDLCodegenManagerState.VERSION)
self.assertNotIn('foobar', manager._state)
def test_generate_build_files(self):
"""generate_build_files() does the right thing from empty."""
manager = self._get_manager()
result = manager.generate_build_files()
self.assertEqual(len(result.inputs), 5)
output = manager.expected_build_output_files()
self.assertEqual(result.created, output)
self.assertEqual(len(result.updated), 0)
self.assertEqual(len(result.unchanged), 0)
for f in output:
self.assertTrue(os.path.isfile(f))
for f in manager.GLOBAL_DECLARE_FILES:
self.assertIn(mozpath.join(manager._exported_header_dir, f), output)
for f in manager.GLOBAL_DEFINE_FILES:
self.assertIn(mozpath.join(manager._codegen_dir, f), output)
for s in self.TEST_STEMS:
self.assertTrue(os.path.isfile(mozpath.join(
manager._exported_header_dir, '%sBinding.h' % s)))
self.assertTrue(os.path.isfile(mozpath.join(
manager._codegen_dir, '%sBinding.cpp' % s)))
self.assertTrue(os.path.isfile(manager._state_path))
with open(manager._state_path, 'rb') as fh:
state = json.load(fh)
self.assertEqual(state['version'], 1)
self.assertIn('webidls', state)
child = state['webidls']['Child.webidl']
self.assertEqual(len(child['inputs']), 2)
self.assertEqual(len(child['outputs']), 2)
self.assertEqual(child['sha1'], 'c41527cad3bc161fa6e7909e48fa11f9eca0468b')
def test_generate_build_files_load_state(self):
"""State should be equivalent when instantiating a new instance."""
args = self._get_manager_args()
m1 = WebIDLCodegenManager(**args)
self.assertEqual(len(m1._state['webidls']), 0)
m1.generate_build_files()
m2 = WebIDLCodegenManager(**args)
self.assertGreater(len(m2._state['webidls']), 2)
self.assertEqual(m1._state, m2._state)
def test_no_change_no_writes(self):
"""If nothing changes, no files should be updated."""
args = self._get_manager_args()
m1 = WebIDLCodegenManager(**args)
m1.generate_build_files()
m2 = WebIDLCodegenManager(**args)
result = m2.generate_build_files()
self.assertEqual(len(result.inputs), 0)
self.assertEqual(len(result.created), 0)
self.assertEqual(len(result.updated), 0)
def test_output_file_regenerated(self):
"""If an output file disappears, it is regenerated."""
args = self._get_manager_args()
m1 = WebIDLCodegenManager(**args)
m1.generate_build_files()
rm_count = 0
for p in m1._state['webidls']['Child.webidl']['outputs']:
rm_count += 1
os.unlink(p)
for p in m1.GLOBAL_DECLARE_FILES:
rm_count += 1
os.unlink(mozpath.join(m1._exported_header_dir, p))
m2 = WebIDLCodegenManager(**args)
result = m2.generate_build_files()
self.assertEqual(len(result.created), rm_count)
def test_only_rebuild_self(self):
"""If an input file changes, only rebuild that one file."""
args = self._get_manager_args()
m1 = WebIDLCodegenManager(**args)
m1.generate_build_files()
child_path = None
for p in m1._input_paths:
if p.endswith('Child.webidl'):
child_path = p
break
self.assertIsNotNone(child_path)
child_content = open(child_path, 'rb').read()
with MockedOpen({child_path: child_content + '\n/* */'}):
m2 = WebIDLCodegenManager(**args)
result = m2.generate_build_files()
self.assertEqual(result.inputs, set([child_path]))
self.assertEqual(len(result.updated), 0)
self.assertEqual(len(result.created), 0)
def test_rebuild_dependencies(self):
"""Ensure an input file used by others results in others rebuilding."""
args = self._get_manager_args()
m1 = WebIDLCodegenManager(**args)
m1.generate_build_files()
parent_path = None
child_path = None
for p in m1._input_paths:
if p.endswith('Parent.webidl'):
parent_path = p
elif p.endswith('Child.webidl'):
child_path = p
self.assertIsNotNone(parent_path)
parent_content = open(parent_path, 'rb').read()
with MockedOpen({parent_path: parent_content + '\n/* */'}):
m2 = WebIDLCodegenManager(**args)
result = m2.generate_build_files()
self.assertEqual(result.inputs, {child_path, parent_path})
self.assertEqual(len(result.updated), 0)
self.assertEqual(len(result.created), 0)
def test_python_change_regenerate_everything(self):
"""If a Python file changes, we should attempt to rebuild everything."""
# We don't want to mutate files in the source directory because we want
# to be able to build from a read-only filesystem. So, we install a
# dummy module and rewrite the metadata to say it comes from the source
# directory.
#
# Hacking imp to accept a MockedFile doesn't appear possible. So for
# the first iteration we read from a temp file. The second iteration
# doesn't need to import, so we are fine with a mocked file.
fake_path = mozpath.join(OUR_DIR, 'fakemodule.py')
with NamedTemporaryFile('wt') as fh:
fh.write('# Original content')
fh.flush()
mod = imp.load_source('mozwebidlcodegen.fakemodule', fh.name)
mod.__file__ = fake_path
args = self._get_manager_args()
m1 = WebIDLCodegenManager(**args)
with MockedOpen({fake_path: '# Original content'}):
old_exists = os.path.exists
try:
def exists(p):
if p == fake_path:
return True
return old_exists(p)
os.path.exists = exists
result = m1.generate_build_files()
l = len(result.inputs)
with open(fake_path, 'wt') as fh:
fh.write('# Modified content')
m2 = WebIDLCodegenManager(**args)
result = m2.generate_build_files()
self.assertEqual(len(result.inputs), l)
result = m2.generate_build_files()
self.assertEqual(len(result.inputs), 0)
finally:
os.path.exists = old_exists
del sys.modules['mozwebidlcodegen.fakemodule']
if __name__ == '__main__':
main()

Просмотреть файл

@ -2,89 +2,23 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this file, # 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/. # You can obtain one at http://mozilla.org/MPL/2.0/.
# Do NOT export this library. We don't actually want our test code
# being added to libxul or anything.
# pymake can't handle descending into dom/bindings several times simultaneously
ifdef .PYMAKE
.NOTPARALLEL:
endif
# Need this for $(test_webidl_files)
include ../webidlsrcs.mk include ../webidlsrcs.mk
# But the webidl actually lives in our parent dir # $(test_sources) comes from webidlsrcs.mk.
test_webidl_files := $(addprefix ../,$(test_webidl_files)) # TODO Update this variable in backend.mk.
# Store the actual locations of our source preprocessed files, so we CPPSRCS += $(addprefix ../,$(test_sources))
# can depend on them sanely.
source_preprocessed_test_webidl_files := $(addprefix $(srcdir)/,$(preprocessed_test_webidl_files))
preprocessed_test_webidl_files := $(addprefix ../,$(preprocessed_test_webidl_files))
CPPSRCS += \
$(subst .webidl,Binding.cpp,$(test_webidl_files)) \
$(subst .webidl,Binding.cpp,$(preprocessed_test_webidl_files)) \
$(NULL)
# If you change bindinggen_dependencies here, change it in
# dom/bindings/Makefile.in too. But note that we include ../Makefile
# here manually, since $(GLOBAL_DEPS) won't cover it.
bindinggen_dependencies := \
../BindingGen.py \
../Bindings.conf \
../Configuration.py \
../Codegen.py \
../ParserResults.pkl \
../parser/WebIDL.py \
../Makefile \
$(GLOBAL_DEPS) \
$(NULL)
ifdef GNU_CC ifdef GNU_CC
CXXFLAGS += -Wno-uninitialized OS_CXXFLAGS += -Wno-uninitialized
endif endif
# Bug 932082 tracks having bindings use namespaced includes.
LOCAL_INCLUDES += -I$(DIST)/include/mozilla/dom -I..
# Include rules.mk before any of our targets so our first target is coming from # Include rules.mk before any of our targets so our first target is coming from
# rules.mk and running make with no target in this dir does the right thing. # rules.mk and running make with no target in this dir does the right thing.
include $(topsrcdir)/config/rules.mk include $(topsrcdir)/config/rules.mk
$(CPPSRCS): .BindingGen
.BindingGen: $(bindinggen_dependencies) \
$(test_webidl_files) \
$(source_preprocessed_test_webidl_files) \
$(NULL)
# The export phase in dom/bindings is what actually looks at
# dependencies and regenerates things as needed, so just go ahead and
# make that phase here. Also make our example interface files. If the
# target used here ever changes, change the conditional around
# $(CPPSRCS) in dom/bindings/Makefile.in.
$(MAKE) -C .. export TestExampleInterface-example TestExampleProxyInterface-example
@$(TOUCH) $@
check:: check::
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \ PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
$(PLY_INCLUDE) $(srcdir)/../parser/runtests.py $(PLY_INCLUDE) $(srcdir)/../parser/runtests.py
# Since we define MOCHITEST_FILES, config/makefiles/mochitest.mk goes ahead and
# sets up a rule with libs:: in itm which makes our .DEFAULT_TARGET be "libs".
# Then ruls.mk does |.DEFAULT_TARGET ?= default| which leaves it as "libs". So
# if we make without an explicit target in this directory, we try to make
# "libs", but with a $(MAKECMDGOALS) of empty string. And then rules.mk
# helpfully does not include our *.o.pp files, since it includes them only if
# filtering some stuff out from $(MAKECMDGOALS) leaves it nonempty. The upshot
# is that if some headers change and we run make in this dir without an explicit
# target things don't get rebuilt.
#
# On the other hand, if we set .DEFAULT_TARGET to "default" explicitly here,
# then rules.mk will reinvoke make with "export" and "libs" but this time hey
# will be passed as explicit targets, show up in $(MAKECMDGOALS), and things
# will work. Do this at the end of our Makefile so the rest of the build system
# does not get a chance to muck with it after we set it.
.DEFAULT_GOAL := default
# Make sure to add .BindingGen to GARBAGE so we'll rebuild our example
# files if someone goes through and deletes GARBAGE all over, which
# will delete example files from our parent dir.
GARBAGE += \
.BindingGen \
$(NULL)

Просмотреть файл

@ -14,9 +14,20 @@ MOCHITEST_MANIFESTS += ['mochitest.ini']
MOCHITEST_CHROME_MANIFESTS += ['chrome.ini'] MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
TEST_WEBIDL_FILES += [
'TestDictionary.webidl',
'TestJSImplInheritanceGen.webidl',
'TestTypedef.webidl',
]
PREPROCESSED_TEST_WEBIDL_FILES += [
'TestCodeGen.webidl',
'TestExampleGen.webidl',
'TestJSImplGen.webidl',
]
LOCAL_INCLUDES += [ LOCAL_INCLUDES += [
'/dom/bindings', '/dom/bindings',
'/js/xpconnect/src', '/js/xpconnect/src',
'/js/xpconnect/wrappers', '/js/xpconnect/wrappers',
] ]

Просмотреть файл

@ -0,0 +1,280 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* 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/. */
let Promise =
SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise;
let bluetoothManager;
/* Get mozSettings value specified by @aKey.
*
* Resolve if that mozSettings value is retrieved successfully, reject
* otherwise.
*
* Forfill params:
* The corresponding mozSettings value of the key.
* Reject params: (none)
*
* @param aKey
* A string.
*
* @return A deferred promise.
*/
function getSettings(aKey) {
let deferred = Promise.defer();
let request = navigator.mozSettings.createLock().get(aKey);
request.addEventListener("success", function(aEvent) {
ok(true, "getSettings(" + aKey + ")");
deferred.resolve(aEvent.target.result[aKey]);
});
request.addEventListener("error", function() {
ok(false, "getSettings(" + aKey + ")");
deferred.reject();
});
return deferred.promise;
}
/* Set mozSettings values.
*
* Resolve if that mozSettings value is set successfully, reject otherwise.
*
* Forfill params: (none)
* Reject params: (none)
*
* @param aSettings
* An object of format |{key1: value1, key2: value2, ...}|.
*
* @return A deferred promise.
*/
function setSettings(aSettings) {
let deferred = Promise.defer();
let request = navigator.mozSettings.createLock().set(aSettings);
request.addEventListener("success", function() {
ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
deferred.resolve();
});
request.addEventListener("error", function() {
ok(false, "setSettings(" + JSON.stringify(aSettings) + ")");
deferred.reject();
});
return deferred.promise;
}
/* Get mozSettings value of 'bluetooth.enabled'.
*
* Resolve if that mozSettings value is retrieved successfully, reject
* otherwise.
*
* Forfill params:
* A boolean value.
* Reject params: (none)
*
* @return A deferred promise.
*/
function getBluetoothEnabled() {
return getSettings("bluetooth.enabled");
}
/* Set mozSettings value of 'bluetooth.enabled'.
*
* Resolve if that mozSettings value is set successfully, reject otherwise.
*
* Forfill params: (none)
* Reject params: (none)
*
* @param aEnabled
* A boolean value.
*
* @return A deferred promise.
*/
function setBluetoothEnabled(aEnabled) {
let obj = {};
obj["bluetooth.enabled"] = aEnabled;
return setSettings(obj);
}
/* Push required permissions and test if |navigator.mozBluetooth| exists.
* Resolve if it does, reject otherwise.
*
* Forfill params:
* bluetoothManager -- an reference to navigator.mozBluetooth.
* Reject params: (none)
*
* @param aPermissions
* Additional permissions to push before any test cases. Could be either
* a string or an array of strings.
*
* @return A deferred promise.
*/
function ensureBluetoothManager(aPermissions) {
let deferred = Promise.defer();
let permissions = ["bluetooth"];
if (aPermissions) {
if (Array.isArray(aPermissions)) {
permissions = permissions.concat(aPermissions);
} else if (typeof aPermissions == "string") {
permissions.push(aPermissions);
}
}
let obj = [];
for (let perm of permissions) {
obj.push({
"type": perm,
"allow": 1,
"context": document,
});
}
SpecialPowers.pushPermissions(obj, function() {
ok(true, "permissions pushed: " + JSON.stringify(permissions));
bluetoothManager = window.navigator.mozBluetooth;
log("navigator.mozBluetooth is " +
(bluetoothManager ? "available" : "unavailable"));
if (bluetoothManager instanceof BluetoothManager) {
deferred.resolve(bluetoothManager);
} else {
deferred.reject();
}
});
return deferred.promise;
}
/* Wait for one named BluetoothManager event.
*
* Resolve if that named event occurs. Never reject.
*
* Forfill params: the DOMEvent passed.
*
* @return A deferred promise.
*/
function waitForManagerEvent(aEventName) {
let deferred = Promise.defer();
bluetoothManager.addEventListener(aEventName, function onevent(aEvent) {
bluetoothManager.removeEventListener(aEventName, onevent);
ok(true, "BluetoothManager event '" + aEventName + "' got.");
deferred.resolve(aEvent);
});
return deferred.promise;
}
/* Convenient function for setBluetoothEnabled and waitForManagerEvent
* combined.
*
* Resolve if that named event occurs. Reject if we can't set settings.
*
* Forfill params: the DOMEvent passed.
* Reject params: (none)
*
* @return A deferred promise.
*/
function setBluetoothEnabledAndWait(aEnabled) {
return setBluetoothEnabled(aEnabled)
.then(waitForManagerEvent.bind(null, aEnabled ? "enabled" : "disabled"));
}
/* Get default adapter.
*
* Resolve if that default adapter is got, reject otherwise.
*
* Forfill params: a BluetoothAdapter instance.
* Reject params: a DOMError, or null if if there is no adapter ready yet.
*
* @return A deferred promise.
*/
function getDefaultAdapter() {
let deferred = Promise.defer();
let request = bluetoothManager.getDefaultAdapter();
request.onsuccess = function(aEvent) {
let adapter = aEvent.target.result;
if (!(adapter instanceof BluetoothAdapter)) {
ok(false, "no BluetoothAdapter ready yet.");
deferred.reject(null);
return;
}
ok(true, "BluetoothAdapter got.");
// TODO: We have an adapter instance now, but some of its attributes may
// still remain unassigned/out-dated. Here we waste a few seconds to
// wait for the property changed events.
//
// See https://bugzilla.mozilla.org/show_bug.cgi?id=932914
window.setTimeout(function() {
deferred.resolve(adapter);
}, 3000);
};
request.onerror = function(aEvent) {
ok(false, "Failed to get default adapter.");
deferred.reject(aEvent.target.error);
};
return deferred.promise;
}
/* Flush permission settings and call |finish()|.
*/
function cleanUp() {
SpecialPowers.flushPermissions(function() {
// Use ok here so that we have at least one test run.
ok(true, "permissions flushed");
finish();
});
}
function startBluetoothTestBase(aPermissions, aTestCaseMain) {
ensureBluetoothManager(aPermissions)
.then(aTestCaseMain)
.then(cleanUp, function() {
ok(false, "Unhandled rejected promise.");
cleanUp();
});
}
function startBluetoothTest(aReenable, aTestCaseMain) {
startBluetoothTestBase(["settings-read", "settings-write"], function() {
let origEnabled, needEnable;
return getBluetoothEnabled()
.then(function(aEnabled) {
origEnabled = aEnabled;
needEnable = !aEnabled;
log("Original 'bluetooth.enabled' is " + origEnabled);
if (aEnabled && aReenable) {
log(" Disable 'bluetooth.enabled' ...");
needEnable = true;
return setBluetoothEnabledAndWait(false);
}
})
.then(function() {
if (needEnable) {
log(" Enable 'bluetooth.enabled' ...");
return setBluetoothEnabledAndWait(true)
.then(waitForManagerEvent.bind(null, "adapteradded"));
}
})
.then(getDefaultAdapter)
.then(aTestCaseMain)
.then(function() {
if (!origEnabled) {
return setBluetoothEnabledAndWait(false);
}
});
});
}

Просмотреть файл

@ -0,0 +1,7 @@
[DEFAULT]
b2g = true
browser = false
qemu = true
[test_dom_BluetoothManager_enabled.js]
[test_dom_BluetoothManager_adapteradded.js]

Просмотреть файл

@ -0,0 +1,44 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* 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/. */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
// https://github.com/mozilla-b2g/platform_external_qemu/blob/master/vl-android.c#L765
// static int bt_hci_parse(const char *str) {
// ...
// bdaddr.b[0] = 0x52;
// bdaddr.b[1] = 0x54;
// bdaddr.b[2] = 0x00;
// bdaddr.b[3] = 0x12;
// bdaddr.b[4] = 0x34;
// bdaddr.b[5] = 0x56 + nb_hcis;
const EMULATOR_ADDRESS = "56:34:12:00:54:52";
// $ adb shell hciconfig /dev/ttyS2 name
// hci0: Type: BR/EDR Bus: UART
// BD Address: 56:34:12:00:54:52 ACL MTU: 512:1 SCO MTU: 0:0
// Name: 'Full Android on Emulator'
const EMULATOR_NAME = "Full Android on Emulator";
// $ adb shell hciconfig /dev/ttyS2 class
// hci0: Type: BR/EDR Bus: UART
// BD Address: 56:34:12:00:54:52 ACL MTU: 512:1 SCO MTU: 0:0
// Class: 0x58020c
// Service Classes: Capturing, Object Transfer, Telephony
// Device Class: Phone, Smart phone
const EMULATOR_CLASS = 0x58020c;
startBluetoothTest(true, function testCaseMain(aAdapter) {
log("Checking adapter attributes ...");
is(aAdapter.name, EMULATOR_NAME, "adapter.name");
is(aAdapter.class, EMULATOR_CLASS, "adapter.class");
is(aAdapter.address, EMULATOR_ADDRESS, "adapter.address");
is(aAdapter.discovering, false, "adapter.discovering");
is(aAdapter.discoverable, false, "adapter.discoverable");
is(aAdapter.discoverableTimeout, 120, "adapter.discoverableTimeout");
});

Просмотреть файл

@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 sts=2 et filetype=javascript
* 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/. */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
let enabledEventReceived;
function onEnabled() {
enabledEventReceived = true;
}
let disabledEventReceived;
function onDisabled() {
disabledEventReceived = true;
}
function test(aEnabled) {
log("Testing 'bluetooth.enabled' => " + aEnabled);
let deferred = Promise.defer();
enabledEventReceived = false;
disabledEventReceived = false;
setBluetoothEnabled(aEnabled).then(function() {
log(" Settings set. Waiting 3 seconds and examine results.");
window.setTimeout(function() {
is(bluetoothManager.enabled, aEnabled, "bluetoothManager.enabled");
is(enabledEventReceived, aEnabled, "enabledEventReceived");
is(disabledEventReceived, !aEnabled, "disabledEventReceived");
if (bluetoothManager.enabled === aEnabled &&
enabledEventReceived === aEnabled &&
disabledEventReceived === !aEnabled) {
deferred.resolve();
} else {
deferred.reject();
}
}, 3000);
});
return deferred.promise;
}
startBluetoothTestBase(["settings-read", "settings-write"],
function testCaseMain() {
bluetoothManager.addEventListener("enabled", onEnabled);
bluetoothManager.addEventListener("disabled", onDisabled);
return getBluetoothEnabled()
.then(function(aEnabled) {
log("Original 'bluetooth.enabled' is " + aEnabled);
// Set to !aEnabled and reset back to aEnabled.
return test(!aEnabled).then(test.bind(null, aEnabled));
})
.then(function() {
bluetoothManager.removeEventListener("enabled", onEnabled);
bluetoothManager.removeEventListener("disabled", onDisabled);
});
});

Просмотреть файл

@ -65,8 +65,8 @@ BrowserElementParentFactory.prototype = {
// alive for as long as its frame element lives. // alive for as long as its frame element lives.
this._bepMap = new WeakMap(); this._bepMap = new WeakMap();
Services.obs.addObserver(this, 'remote-browser-frame-shown', /* ownsWeak = */ true); Services.obs.addObserver(this, 'remote-browser-shown', /* ownsWeak = */ true);
Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', /* ownsWeak = */ true); Services.obs.addObserver(this, 'inprocess-browser-shown', /* ownsWeak = */ true);
}, },
_browserFramesPrefEnabled: function() { _browserFramesPrefEnabled: function() {
@ -79,11 +79,19 @@ BrowserElementParentFactory.prototype = {
}, },
_observeInProcessBrowserFrameShown: function(frameLoader) { _observeInProcessBrowserFrameShown: function(frameLoader) {
// Ignore notifications that aren't from a BrowserOrApp
if (!frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsBrowserOrAppFrame) {
return;
}
debug("In-process browser frame shown " + frameLoader); debug("In-process browser frame shown " + frameLoader);
this._createBrowserElementParent(frameLoader, /* hasRemoteFrame = */ false); this._createBrowserElementParent(frameLoader, /* hasRemoteFrame = */ false);
}, },
_observeRemoteBrowserFrameShown: function(frameLoader) { _observeRemoteBrowserFrameShown: function(frameLoader) {
// Ignore notifications that aren't from a BrowserOrApp
if (!frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsBrowserOrAppFrame) {
return;
}
debug("Remote browser frame shown " + frameLoader); debug("Remote browser frame shown " + frameLoader);
this._createBrowserElementParent(frameLoader, /* hasRemoteFrame = */ true); this._createBrowserElementParent(frameLoader, /* hasRemoteFrame = */ true);
}, },
@ -103,10 +111,10 @@ BrowserElementParentFactory.prototype = {
this._init(); this._init();
} }
break; break;
case 'remote-browser-frame-shown': case 'remote-browser-shown':
this._observeRemoteBrowserFrameShown(subject); this._observeRemoteBrowserFrameShown(subject);
break; break;
case 'in-process-browser-or-app-frame-shown': case 'inprocess-browser-shown':
this._observeInProcessBrowserFrameShown(subject); this._observeInProcessBrowserFrameShown(subject);
break; break;
case 'content-document-global-created': case 'content-document-global-created':

Просмотреть файл

@ -198,6 +198,7 @@ function createDOMDownloadObject(aWindow, aDownload) {
function DOMDownloadImpl() { function DOMDownloadImpl() {
debug("DOMDownloadImpl constructor "); debug("DOMDownloadImpl constructor ");
this.wrappedJSObject = this; this.wrappedJSObject = this;
this.totalBytes = 0; this.totalBytes = 0;
this.currentBytes = 0; this.currentBytes = 0;
@ -205,9 +206,11 @@ function DOMDownloadImpl() {
this.path = null; this.path = null;
this.state = "stopped"; this.state = "stopped";
this.contentType = null; this.contentType = null;
this.startTime = Date.now();
this.error = null; this.error = null;
/* fields that require getters/setters */
this._startTime = new Date();
/* private fields */ /* private fields */
this.id = null; this.id = null;
} }
@ -244,6 +247,19 @@ DOMDownloadImpl.prototype = {
return this.__DOM_IMPL__.getEventHandler("onstatechange"); return this.__DOM_IMPL__.getEventHandler("onstatechange");
}, },
get startTime() {
return this._startTime;
},
set startTime(aStartTime) {
if (aStartTime instanceof Date) {
this._startTime = aStartTime;
}
else {
this._startTime = new Date(aStartTime);
}
},
_init: function(aWindow, aDownload) { _init: function(aWindow, aDownload) {
this._window = aWindow; this._window = aWindow;
this.id = aDownload.id; this.id = aDownload.id;

Просмотреть файл

@ -22,11 +22,6 @@ function testSteps()
objectStore.add(Bob); objectStore.add(Bob);
yield undefined; yield undefined;
// This direct eval causes locals to be aliased, and thus allocated on
// the scope chain. Comment it out (and the workarounds below) and
// the test passes. Bug 943409.
eval('');
db.transaction("foo", "readwrite").objectStore("foo") db.transaction("foo", "readwrite").objectStore("foo")
.index("name").openCursor().onsuccess = function(event) { .index("name").openCursor().onsuccess = function(event) {
event.target.transaction.oncomplete = continueToNextStep; event.target.transaction.oncomplete = continueToNextStep;

Просмотреть файл

@ -35,11 +35,6 @@ function testSteps()
let objectStore = db.createObjectStore("foo", { keyPath: "ss" }); let objectStore = db.createObjectStore("foo", { keyPath: "ss" });
objectStore.createIndex("name", "name", { unique: true }); objectStore.createIndex("name", "name", { unique: true });
// This direct eval causes locals to be aliased, and thus allocated on
// the scope chain. Comment it out (and the workarounds below) and
// the test passes. Bug 943409.
eval('');
for (let i = 0; i < objectStoreData.length - 1; i++) { for (let i = 0; i < objectStoreData.length - 1; i++) {
objectStore.add(objectStoreData[i]); objectStore.add(objectStoreData[i]);
} }

Просмотреть файл

@ -34,9 +34,6 @@ function testSteps()
event.target.onsuccess = continueToNextStep; event.target.onsuccess = continueToNextStep;
// Bug 943409.
eval('');
for (let objectStoreIndex in objectStoreData) { for (let objectStoreIndex in objectStoreData) {
const objectStoreInfo = objectStoreData[objectStoreIndex]; const objectStoreInfo = objectStoreData[objectStoreIndex];
let objectStore = db.createObjectStore(objectStoreInfo.name, let objectStore = db.createObjectStore(objectStoreInfo.name,

Просмотреть файл

@ -18,9 +18,6 @@ function testSteps()
let db = event.target.result; let db = event.target.result;
db.onerror = errorHandler; db.onerror = errorHandler;
// Bug 943409.
eval('');
for each (let autoIncrement in [false, true]) { for each (let autoIncrement in [false, true]) {
let objectStore = let objectStore =
db.createObjectStore(autoIncrement, { keyPath: "id", db.createObjectStore(autoIncrement, { keyPath: "id",

Просмотреть файл

@ -44,8 +44,8 @@ this.Keyboard = {
}, },
init: function keyboardInit() { init: function keyboardInit() {
Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false); Services.obs.addObserver(this, 'inprocess-browser-shown', false);
Services.obs.addObserver(this, 'remote-browser-frame-shown', false); Services.obs.addObserver(this, 'remote-browser-shown', false);
Services.obs.addObserver(this, 'oop-frameloader-crashed', false); Services.obs.addObserver(this, 'oop-frameloader-crashed', false);
for (let name of this._messageNames) for (let name of this._messageNames)
@ -63,6 +63,10 @@ this.Keyboard = {
ppmm.broadcastAsyncMessage('Keyboard:FocusChange', { 'type': 'blur' }); ppmm.broadcastAsyncMessage('Keyboard:FocusChange', { 'type': 'blur' });
} }
} else { } else {
// Ignore notifications that aren't from a BrowserOrApp
if (!frameLoader.ownerIsBrowserOrAppFrame) {
return;
}
this.initFormsFrameScript(mm); this.initFormsFrameScript(mm);
} }
}, },

Просмотреть файл

@ -1502,6 +1502,33 @@ ContentChild::RecvNotifyPhoneStateChange(const nsString& aState)
return true; return true;
} }
void
ContentChild::AddIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS)
{
MOZ_ASSERT(aObserver, "null idle observer");
// Make sure aObserver isn't released while we wait for the parent
aObserver->AddRef();
SendAddIdleObserver(reinterpret_cast<uint64_t>(aObserver), aIdleTimeInS);
}
void
ContentChild::RemoveIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS)
{
MOZ_ASSERT(aObserver, "null idle observer");
SendRemoveIdleObserver(reinterpret_cast<uint64_t>(aObserver), aIdleTimeInS);
aObserver->Release();
}
bool
ContentChild::RecvNotifyIdleObserver(const uint64_t& aObserver,
const nsCString& aTopic,
const nsString& aTimeStr)
{
nsIObserver* observer = reinterpret_cast<nsIObserver*>(aObserver);
observer->Observe(nullptr, aTopic.get(), aTimeStr.get());
return true;
}
bool bool
ContentChild::RecvLoadAndRegisterSheet(const URIParams& aURI, const uint32_t& aType) ContentChild::RecvLoadAndRegisterSheet(const URIParams& aURI, const uint32_t& aType)
{ {

Просмотреть файл

@ -240,6 +240,12 @@ public:
virtual bool RecvUnregisterSheet(const URIParams& aURI, const uint32_t& aType); virtual bool RecvUnregisterSheet(const URIParams& aURI, const uint32_t& aType);
virtual bool RecvNotifyPhoneStateChange(const nsString& state); virtual bool RecvNotifyPhoneStateChange(const nsString& state);
void AddIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS);
void RemoveIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS);
virtual bool RecvNotifyIdleObserver(const uint64_t& aObserver,
const nsCString& aTopic,
const nsString& aData);
#ifdef ANDROID #ifdef ANDROID
gfxIntSize GetScreenSize() { return mScreenSize; } gfxIntSize GetScreenSize() { return mScreenSize; }
#endif #endif

Просмотреть файл

@ -68,6 +68,7 @@
#include "nsIDOMWindow.h" #include "nsIDOMWindow.h"
#include "nsIExternalProtocolService.h" #include "nsIExternalProtocolService.h"
#include "nsIFilePicker.h" #include "nsIFilePicker.h"
#include "nsIIdleService.h"
#include "nsIMemoryReporter.h" #include "nsIMemoryReporter.h"
#include "nsIMozBrowserFrame.h" #include "nsIMozBrowserFrame.h"
#include "nsIMutable.h" #include "nsIMutable.h"
@ -1082,6 +1083,8 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", nullptr); obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", nullptr);
} }
mIdleListeners.Clear();
// If the child process was terminated due to a SIGKIL, ShutDownProcess // If the child process was terminated due to a SIGKIL, ShutDownProcess
// might not have been called yet. We must call it to ensure that our // might not have been called yet. We must call it to ensure that our
// channel is closed, etc. // channel is closed, etc.
@ -3155,5 +3158,47 @@ ContentParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus,
return true; return true;
} }
bool
ContentParent::RecvAddIdleObserver(const uint64_t& aObserver, const uint32_t& aIdleTimeInS)
{
nsresult rv;
nsCOMPtr<nsIIdleService> idleService =
do_GetService("@mozilla.org/widget/idleservice;1", &rv);
NS_ENSURE_SUCCESS(rv, false);
nsCOMPtr<ParentIdleListener> listener = new ParentIdleListener(this, aObserver);
mIdleListeners.Put(aObserver, listener);
idleService->AddIdleObserver(listener, aIdleTimeInS);
return true;
}
bool
ContentParent::RecvRemoveIdleObserver(const uint64_t& aObserver, const uint32_t& aIdleTimeInS)
{
nsresult rv;
nsCOMPtr<nsIIdleService> idleService =
do_GetService("@mozilla.org/widget/idleservice;1", &rv);
NS_ENSURE_SUCCESS(rv, false);
nsCOMPtr<ParentIdleListener> listener;
bool found = mIdleListeners.Get(aObserver, &listener);
if (found) {
mIdleListeners.Remove(aObserver);
idleService->RemoveIdleObserver(listener, aIdleTimeInS);
}
return true;
}
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla
NS_IMPL_ISUPPORTS1(ParentIdleListener, nsIObserver)
NS_IMETHODIMP
ParentIdleListener::Observe(nsISupports*, const char* aTopic, const PRUnichar* aData) {
mozilla::unused << mParent->SendNotifyIdleObserver(mObserver,
nsDependentCString(aTopic),
nsDependentString(aData));
return NS_OK;
}

Просмотреть файл

@ -15,7 +15,9 @@
#include "mozilla/LinkedList.h" #include "mozilla/LinkedList.h"
#include "mozilla/StaticPtr.h" #include "mozilla/StaticPtr.h"
#include "nsDataHashtable.h"
#include "nsFrameMessageManager.h" #include "nsFrameMessageManager.h"
#include "nsHashKeys.h"
#include "nsIObserver.h" #include "nsIObserver.h"
#include "nsIThreadInternal.h" #include "nsIThreadInternal.h"
#include "nsIDOMGeoPositionCallback.h" #include "nsIDOMGeoPositionCallback.h"
@ -27,7 +29,7 @@ class mozIApplication;
class nsConsoleService; class nsConsoleService;
class nsIDOMBlob; class nsIDOMBlob;
class nsIMemoryReporter; class nsIMemoryReporter;
template<class KeyClass,class DataType> class nsDataHashtable; class ParentIdleListener;
namespace mozilla { namespace mozilla {
@ -510,6 +512,9 @@ private:
virtual void ProcessingError(Result what) MOZ_OVERRIDE; virtual void ProcessingError(Result what) MOZ_OVERRIDE;
virtual bool RecvAddIdleObserver(const uint64_t& observerId, const uint32_t& aIdleTimeInS);
virtual bool RecvRemoveIdleObserver(const uint64_t& observerId, const uint32_t& aIdleTimeInS);
// If you add strong pointers to cycle collected objects here, be sure to // If you add strong pointers to cycle collected objects here, be sure to
// release these objects in ShutDownProcess. See the comment there for more // release these objects in ShutDownProcess. See the comment there for more
// details. // details.
@ -559,9 +564,25 @@ private:
nsRefPtr<nsConsoleService> mConsoleService; nsRefPtr<nsConsoleService> mConsoleService;
nsConsoleService* GetConsoleService(); nsConsoleService* GetConsoleService();
nsDataHashtable<nsUint64HashKey, nsCOMPtr<ParentIdleListener> > mIdleListeners;
}; };
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla
class ParentIdleListener : public nsIObserver {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
ParentIdleListener(mozilla::dom::ContentParent* aParent, uint64_t aObserver)
: mParent(aParent), mObserver(aObserver)
{}
virtual ~ParentIdleListener() {}
private:
nsRefPtr<mozilla::dom::ContentParent> mParent;
uint64_t mObserver;
};
#endif #endif

Просмотреть файл

@ -325,6 +325,11 @@ child:
async UnregisterSheet(URIParams uri, uint32_t type); async UnregisterSheet(URIParams uri, uint32_t type);
NotifyPhoneStateChange(nsString newState); NotifyPhoneStateChange(nsString newState);
/**
* Notify idle observers in the child
*/
NotifyIdleObserver(uint64_t observerId, nsCString topic, nsString str);
parent: parent:
/** /**
* Tell the content process some attributes of itself. This is * Tell the content process some attributes of itself. This is
@ -502,6 +507,8 @@ parent:
bool isAudio, bool isAudio,
bool isVideo); bool isVideo);
AddIdleObserver(uint64_t observerId, uint32_t idleTimeInS);
RemoveIdleObserver(uint64_t observerId, uint32_t idleTimeInS);
both: both:
AsyncMessage(nsString aMessage, ClonedMessageData aData, AsyncMessage(nsString aMessage, ClonedMessageData aData,
CpowEntry[] aCpows, Principal aPrincipal); CpowEntry[] aCpows, Principal aPrincipal);

Просмотреть файл

@ -246,7 +246,7 @@ PreallocatedProcessManagerImpl::DelayedNuwaFork()
mPreallocateAppProcessTask = nullptr; mPreallocateAppProcessTask = nullptr;
if (!mIsNuwaReady) { if (!mIsNuwaReady) {
if (!mPreallocatedAppProcess && !mShutdown) { if (!mPreallocatedAppProcess && !mShutdown && mEnabled) {
mPreallocatedAppProcess = ContentParent::RunNuwaProcess(); mPreallocatedAppProcess = ContentParent::RunNuwaProcess();
} }
// else mPreallocatedAppProcess is starting. It will NuwaFork() when ready. // else mPreallocatedAppProcess is starting. It will NuwaFork() when ready.

Просмотреть файл

@ -602,7 +602,7 @@ ParticularProcessPriorityManager::Init()
nsCOMPtr<nsIObserverService> os = services::GetObserverService(); nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) { if (os) {
os->AddObserver(this, "audio-channel-process-changed", /* ownsWeak */ true); os->AddObserver(this, "audio-channel-process-changed", /* ownsWeak */ true);
os->AddObserver(this, "remote-browser-frame-shown", /* ownsWeak */ true); os->AddObserver(this, "remote-browser-shown", /* ownsWeak */ true);
os->AddObserver(this, "ipc:browser-destroyed", /* ownsWeak */ true); os->AddObserver(this, "ipc:browser-destroyed", /* ownsWeak */ true);
os->AddObserver(this, "frameloader-visible-changed", /* ownsWeak */ true); os->AddObserver(this, "frameloader-visible-changed", /* ownsWeak */ true);
} }
@ -674,7 +674,7 @@ ParticularProcessPriorityManager::Observe(nsISupports* aSubject,
if (topic.EqualsLiteral("audio-channel-process-changed")) { if (topic.EqualsLiteral("audio-channel-process-changed")) {
OnAudioChannelProcessChanged(aSubject); OnAudioChannelProcessChanged(aSubject);
} else if (topic.EqualsLiteral("remote-browser-frame-shown")) { } else if (topic.EqualsLiteral("remote-browser-shown")) {
OnRemoteBrowserFrameShown(aSubject); OnRemoteBrowserFrameShown(aSubject);
} else if (topic.EqualsLiteral("ipc:browser-destroyed")) { } else if (topic.EqualsLiteral("ipc:browser-destroyed")) {
OnTabParentDestroyed(aSubject); OnTabParentDestroyed(aSubject);
@ -747,6 +747,13 @@ ParticularProcessPriorityManager::OnRemoteBrowserFrameShown(nsISupports* aSubjec
nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject); nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject);
NS_ENSURE_TRUE_VOID(fl); NS_ENSURE_TRUE_VOID(fl);
// Ignore notifications that aren't from a BrowserOrApp
bool isBrowserOrApp;
fl->GetOwnerIsBrowserOrAppFrame(&isBrowserOrApp);
if (!isBrowserOrApp) {
return;
}
nsCOMPtr<nsITabParent> tp; nsCOMPtr<nsITabParent> tp;
fl->GetTabParent(getter_AddRefs(tp)); fl->GetTabParent(getter_AddRefs(tp));
NS_ENSURE_TRUE_VOID(tp); NS_ENSURE_TRUE_VOID(tp);

Просмотреть файл

@ -41,4 +41,5 @@ qemu = true
[test_thread_subject.js] [test_thread_subject.js]
[test_mmdb_new.js] [test_mmdb_new.js]
[test_mmdb_setmessagedeliverybyid_sms.js] [test_mmdb_setmessagedeliverybyid_sms.js]
[test_mmdb_foreachmatchedmmsdeliveryinfo.js]
[test_replace_short_message_type.js] [test_replace_short_message_type.js]

Просмотреть файл

@ -0,0 +1,114 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
// TODO: bug 943233 - passing jsm exported objects to |Promise.resolve| gets
// empty object in return.
let mmdb;
const PHONE_0 = "+15555215500";
const PHONE_1 = "+15555215501";
const PHONE_2 = "+15555215502";
const PHONE_2_NET = "5555215502";
const PHONE_3 = "+15555215503";
const PHONE_3_NET = "5555215503";
const EMAIL_1 = "foo@bar.com";
let deliveryInfo = [
{ receiver: PHONE_1 },
{ receiver: PHONE_2 },
{ receiver: PHONE_1 },
{ receiver: PHONE_2_NET },
{ receiver: PHONE_3_NET },
{ receiver: EMAIL_1 },
{ receiver: PHONE_3 },
];
function clearTraversed(aDeliveryInfo) {
for (let element of aDeliveryInfo) {
delete element.traversed;
}
}
function doTest(aMmdb, aNeedle, aVerifyFunc, aCount) {
log(" '" + aNeedle + "': " + aCount);
clearTraversed(deliveryInfo);
let count = 0;
aMmdb.forEachMatchedMmsDeliveryInfo(deliveryInfo, aNeedle, function(aElement) {
ok(true, "checking " + aElement.receiver);
ok(!aElement.hasOwnProperty("traversed"), "element.traversed");
aVerifyFunc(aElement);
aElement.traversed = true;
++count;
});
is(count, aCount, "matched count");
}
function testNotFound(aMmdb) {
log("Testing unavailable");
// TODO: bug 943233 - passing jsm exported objects to |Promise.resolve| gets
// empty object in return.
aMmdb = mmdb;
doTest(aMmdb, PHONE_0, function(aElement) {
ok(false, "Should never have a match");
}, 0);
return aMmdb;
}
function testDirectMatch(aMmdb) {
log("Testing direct matching");
// TODO: bug 943233 - passing jsm exported objects to |Promise.resolve| gets
// empty object in return.
aMmdb = mmdb;
for (let needle of [PHONE_1, EMAIL_1]) {
let count = deliveryInfo.reduce(function(aCount, aElement) {
return aElement.receiver == needle ? aCount + 1 : aCount;
}, 0);
doTest(aMmdb, needle, function(aElement) {
is(aElement.receiver, needle, "element.receiver");
}, count);
}
return aMmdb;
}
function testPhoneMatch(aMmdb) {
log("Testing phone matching");
let verifyFunc = function(aValid, aElement) {
ok(aValid.indexOf(aElement.receiver) >= 0, "element.receiver");
};
// TODO: bug 943233 - passing jsm exported objects to |Promise.resolve| gets
// empty object in return.
aMmdb = mmdb;
let matchingGroups = [
[PHONE_2, PHONE_2_NET],
[PHONE_3, PHONE_3_NET],
];
for (let group of matchingGroups) {
for (let item of group) {
doTest(aMmdb, item, verifyFunc.bind(null, group), group.length);
}
}
return aMmdb;
}
startTestBase(function testCaseMain() {
mmdb = newMobileMessageDB();
return initMobileMessageDB(mmdb, "test_mmdb_foreachmatchedmmsdeliveryinfo", 0)
.then(testNotFound)
.then(testDirectMatch)
.then(testPhoneMatch)
.then(closeMobileMessageDB.bind(null, mmdb));
});

Просмотреть файл

@ -7,6 +7,8 @@ MARIONETTE_HEAD_JS = 'head.js';
startTestBase(function testCaseMain() { startTestBase(function testCaseMain() {
log("Test init MobileMessageDB"); log("Test init MobileMessageDB");
// TODO: bug 943233 - passing jsm exported objects to |Promise.resolve| gets
// empty object in return.
let mmdb = newMobileMessageDB(); let mmdb = newMobileMessageDB();
let dbName = "test_mmdb_new"; let dbName = "test_mmdb_new";
let dbVersion = 0; let dbVersion = 0;

Просмотреть файл

@ -107,12 +107,9 @@ if CONFIG['MOZ_NFC']:
if CONFIG['MOZ_B2G']: if CONFIG['MOZ_B2G']:
PARALLEL_DIRS += ['downloads'] PARALLEL_DIRS += ['downloads']
# bindings/test is here, because it needs to build after bindings/, and
# we build subdirectories before ourselves.
TEST_DIRS += [ TEST_DIRS += [
'tests', 'tests',
'imptests', 'imptests',
'bindings/test',
] ]
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'cocoa', 'windows', 'android', 'qt', 'os2'): if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'cocoa', 'windows', 'android', 'qt', 'os2'):

Просмотреть файл

@ -491,7 +491,7 @@ XPCOMUtils.defineLazyGetter(this, "gRadioEnabledController", function () {
receiveMessage: function(msg) { receiveMessage: function(msg) {
if (DEBUG) debug("setRadioEnabled: receiveMessage: " + JSON.stringify(msg)); if (DEBUG) debug("setRadioEnabled: receiveMessage: " + JSON.stringify(msg));
this.pendingMessages.push(msg); this.pendingMessages.push(msg);
if (this.pendingMessages.length === 1) { if (this.pendingMessages.length === 1 && !this.isDeactivatingDataCalls()) {
this._processNextMessage(); this._processNextMessage();
} }
}, },
@ -540,7 +540,6 @@ XPCOMUtils.defineLazyGetter(this, "gRadioEnabledController", function () {
} else { } else {
this.request = (function() { this.request = (function() {
radioInterface.receiveMessage(msg); radioInterface.receiveMessage(msg);
this._processNextMessage();
}).bind(this); }).bind(this);
// In some DSDS architecture with only one modem, toggling one radio may // In some DSDS architecture with only one modem, toggling one radio may
@ -596,6 +595,7 @@ XPCOMUtils.defineLazyGetter(this, "gRadioEnabledController", function () {
this.request(); this.request();
this.request = null; this.request = null;
} }
this._processNextMessage();
} }
}; };
}); });
@ -1258,6 +1258,10 @@ RadioInterface.prototype = {
case "iccmbdn": case "iccmbdn":
this.handleIccMbdn(message); this.handleIccMbdn(message);
break; break;
case "iccmwis":
gMessageManager.sendVoicemailMessage("RIL:VoicemailNotification",
this.clientId, message.mwi);
break;
case "USSDReceived": case "USSDReceived":
if (DEBUG) this.debug("USSDReceived " + JSON.stringify(message)); if (DEBUG) this.debug("USSDReceived " + JSON.stringify(message));
this.handleUSSDReceived(message); this.handleUSSDReceived(message);
@ -2104,11 +2108,6 @@ RadioInterface.prototype = {
return true; return true;
} }
// TODO: Bug #768441
// For now we don't store indicators persistently. When the mwi.discard
// flag is false, we'll need to persist the indicator to EFmwis.
// See TS 23.040 9.2.3.24.2
let mwi = message.mwi; let mwi = message.mwi;
if (mwi) { if (mwi) {
mwi.returnNumber = message.sender; mwi.returnNumber = message.sender;

Просмотреть файл

@ -1206,6 +1206,7 @@ this.GECKO_ICC_SERVICES = {
PNN: 51, PNN: 51,
OPL: 52, OPL: 52,
MDN: 53, MDN: 53,
MWIS: 54,
SPDI: 56 SPDI: 56
}, },
usim: { usim: {
@ -1221,6 +1222,7 @@ this.GECKO_ICC_SERVICES = {
PNN: 45, PNN: 45,
OPL: 46, OPL: 46,
MDN: 47, MDN: 47,
MWIS: 48,
SPDI: 51 SPDI: 51
}, },
ruim: { ruim: {

Просмотреть файл

@ -4206,6 +4206,11 @@ let RIL = {
this.sendChromeMessage(message); this.sendChromeMessage(message);
// Update MWI Status into ICC if present.
if (message.mwi && ICCUtilsHelper.isICCServiceAvailable("MWIS")) {
SimRecordHelper.updateMWIS(message.mwi);
}
// We will acknowledge receipt of the SMS after we try to store it // We will acknowledge receipt of the SMS after we try to store it
// in the database. // in the database.
return MOZ_FCS_WAIT_FOR_EXPLICIT_ACK; return MOZ_FCS_WAIT_FOR_EXPLICIT_ACK;
@ -6163,7 +6168,7 @@ RIL[UNSOLICITED_RESPONSE_NEW_SMS_STATUS_REPORT] = function UNSOLICITED_RESPONSE_
RIL[UNSOLICITED_RESPONSE_NEW_SMS_ON_SIM] = function UNSOLICITED_RESPONSE_NEW_SMS_ON_SIM(length) { RIL[UNSOLICITED_RESPONSE_NEW_SMS_ON_SIM] = function UNSOLICITED_RESPONSE_NEW_SMS_ON_SIM(length) {
let recordNumber = Buf.readInt32List()[0]; let recordNumber = Buf.readInt32List()[0];
ICCRecordHelper.readSMS( SimRecordHelper.readSMS(
recordNumber, recordNumber,
function onsuccess(message) { function onsuccess(message) {
if (message && message.simStatus === 3) { //New Unread SMS if (message && message.simStatus === 3) { //New Unread SMS
@ -10937,6 +10942,7 @@ let ICCFileHelper = {
return EF_PATH_MF_SIM + EF_PATH_DF_TELECOM; return EF_PATH_MF_SIM + EF_PATH_DF_TELECOM;
case ICC_EF_AD: case ICC_EF_AD:
case ICC_EF_MBDN: case ICC_EF_MBDN:
case ICC_EF_MWIS:
case ICC_EF_PLMNsel: case ICC_EF_PLMNsel:
case ICC_EF_SPN: case ICC_EF_SPN:
case ICC_EF_SPDI: case ICC_EF_SPDI:
@ -10961,6 +10967,7 @@ let ICCFileHelper = {
case ICC_EF_AD: case ICC_EF_AD:
case ICC_EF_FDN: case ICC_EF_FDN:
case ICC_EF_MBDN: case ICC_EF_MBDN:
case ICC_EF_MWIS:
case ICC_EF_UST: case ICC_EF_UST:
case ICC_EF_MSISDN: case ICC_EF_MSISDN:
case ICC_EF_SPN: case ICC_EF_SPN:
@ -11916,6 +11923,13 @@ let SimRecordHelper = {
if (DEBUG) debug("MDN: MDN service is not available"); if (DEBUG) debug("MDN: MDN service is not available");
} }
if (ICCUtilsHelper.isICCServiceAvailable("MWIS")) {
if (DEBUG) debug("MWIS: MWIS is available");
this.readMWIS();
} else {
if (DEBUG) debug("MWIS: MWIS is not available");
}
if (ICCUtilsHelper.isICCServiceAvailable("SPDI")) { if (ICCUtilsHelper.isICCServiceAvailable("SPDI")) {
if (DEBUG) debug("SPDI: SPDI available."); if (DEBUG) debug("SPDI: SPDI available.");
this.readSPDI(); this.readSPDI();
@ -11985,6 +11999,91 @@ let SimRecordHelper = {
callback: callback.bind(this)}); callback: callback.bind(this)});
}, },
/**
* Read ICC MWIS. (Message Waiting Indication Status)
*
* @see TS 31.102, clause 4.2.63 for USIM and TS 51.011, clause 10.3.45 for SIM.
*/
readMWIS: function readMWIS() {
function callback(options) {
let strLen = Buf.readInt32();
// Each octet is encoded into two chars.
let octetLen = strLen / 2;
let mwis = GsmPDUHelper.readHexOctetArray(octetLen);
Buf.readStringDelimiter(strLen);
if (!mwis) {
return;
}
RIL.iccInfoPrivate.mwis = mwis; //Keep raw MWIS for updateMWIS()
let mwi = {};
// b8 b7 B6 b5 b4 b3 b2 b1 4.2.63, TS 31.102 version 11.6.0
// | | | | | | | |__ Voicemail
// | | | | | | |_____ Fax
// | | | | | |________ Electronic Mail
// | | | | |___________ Other
// | | | |______________ Videomail
// |__|__|_________________ RFU
mwi.active = ((mwis[0] & 0x01) != 0);
if (mwi.active) {
// In TS 23.040 msgCount is in the range from 0 to 255.
// The value 255 shall be taken to mean 255 or greater.
//
// However, There is no definition about 0 when MWI is active.
//
// Normally, when mwi is active, the msgCount must be larger than 0.
// Refer to other reference phone,
// 0 is usually treated as UNKNOWN for storing 2nd level MWI status (DCS).
mwi.msgCount = (mwis[1] === 0) ? GECKO_VOICEMAIL_MESSAGE_COUNT_UNKNOWN
: mwis[1];
} else {
mwi.msgCount = 0;
}
RIL.sendChromeMessage({ rilMessageType: "iccmwis",
mwi: mwi });
}
ICCIOHelper.loadLinearFixedEF({ fileId: ICC_EF_MWIS,
recordNumber: 1, // Get 1st Subscriber Profile.
callback: callback });
},
/**
* Update ICC MWIS. (Message Waiting Indication Status)
*
* @see TS 31.102, clause 4.2.63 for USIM and TS 51.011, clause 10.3.45 for SIM.
*/
updateMWIS: function updateMWIS(mwi) {
if (!RIL.iccInfoPrivate.mwis) {
return;
}
function dataWriter(recordSize) {
let mwis = RIL.iccInfoPrivate.mwis;
let msgCount =
(mwi.msgCount === GECKO_VOICEMAIL_MESSAGE_COUNT_UNKNOWN) ? 0 : mwi.msgCount;
[mwis[0], mwis[1]] = (mwi.active) ? [(mwis[0] | 0x01), msgCount]
: [(mwis[0] & 0xFE), 0];
let strLen = recordSize * 2;
Buf.writeInt32(strLen);
for (let i = 0; i < mwis.length; i++) {
GsmPDUHelper.writeHexOctet(mwis[i]);
}
Buf.writeStringDelimiter(strLen);
}
ICCIOHelper.updateLinearFixedEF({ fileId: ICC_EF_MWIS,
recordNumber: 1, // Update 1st Subscriber Profile.
dataWriter: dataWriter });
},
/** /**
* Read the SPDI (Service Provider Display Information) from the (U)SIM. * Read the SPDI (Service Provider Display Information) from the (U)SIM.
* *

Просмотреть файл

@ -2330,6 +2330,10 @@ add_test(function test_reading_optional_efs() {
testEf.splice(testEf.indexOf("MDN"), 1); testEf.splice(testEf.indexOf("MDN"), 1);
}; };
record.readMWIS = function fakeReadMWIS() {
testEf.splice(testEf.indexOf("MWIS"), 1);
};
io.loadTransparentEF = function fakeLoadTransparentEF(options) { io.loadTransparentEF = function fakeLoadTransparentEF(options) {
// Write data size // Write data size
buf.writeInt32(sst.length * 2); buf.writeInt32(sst.length * 2);
@ -2356,7 +2360,7 @@ add_test(function test_reading_optional_efs() {
} }
// TODO: Add all necessary optional EFs eventually // TODO: Add all necessary optional EFs eventually
let supportedEf = ["MSISDN", "MDN"]; let supportedEf = ["MSISDN", "MDN", "MWIS"];
ril.appType = CARD_APPTYPE_SIM; ril.appType = CARD_APPTYPE_SIM;
do_test(buildSST(supportedEf), supportedEf); do_test(buildSST(supportedEf), supportedEf);
ril.appType = CARD_APPTYPE_USIM; ril.appType = CARD_APPTYPE_USIM;
@ -2435,3 +2439,174 @@ add_test(function test_fetch_icc_recodes() {
run_next_test(); run_next_test();
}); });
add_test(function test_read_mwis() {
let worker = newUint8Worker();
let helper = worker.GsmPDUHelper;
let recordHelper = worker.SimRecordHelper;
let buf = worker.Buf;
let io = worker.ICCIOHelper;
let mwisData;
let postedMessage;
worker.postMessage = function fakePostMessage(message) {
postedMessage = message;
};
io.loadLinearFixedEF = function fakeLoadLinearFixedEF(options) {
if (mwisData) {
// Write data size
buf.writeInt32(mwisData.length * 2);
// Write MWIS
for (let i = 0; i < mwisData.length; i++) {
helper.writeHexOctet(mwisData[i]);
}
// Write string delimiter
buf.writeStringDelimiter(mwisData.length * 2);
options.recordSize = mwisData.length;
if (options.callback) {
options.callback(options);
}
} else {
do_print("mwisData[] is not set.");
}
};
function buildMwisData(isActive, msgCount) {
if (msgCount < 0 || msgCount === GECKO_VOICEMAIL_MESSAGE_COUNT_UNKNOWN) {
msgCount = 0;
} else if (msgCount > 255) {
msgCount = 255;
}
mwisData = [ (isActive) ? 0x01 : 0x00,
msgCount,
0xFF, 0xFF, 0xFF ];
}
function do_test(isActive, msgCount) {
buildMwisData(isActive, msgCount);
recordHelper.readMWIS();
do_check_eq("iccmwis", postedMessage.rilMessageType);
do_check_eq(isActive, postedMessage.mwi.active);
do_check_eq((isActive) ? msgCount : 0, postedMessage.mwi.msgCount);
}
do_test(true, GECKO_VOICEMAIL_MESSAGE_COUNT_UNKNOWN);
do_test(true, 1);
do_test(true, 255);
do_test(false, 0);
do_test(false, 255); // Test the corner case when mwi is disable with incorrect msgCount.
run_next_test();
});
add_test(function test_update_mwis() {
let worker = newUint8Worker();
let pduHelper = worker.GsmPDUHelper;
let ril = worker.RIL;
ril.appType = CARD_APPTYPE_USIM;
ril.iccInfoPrivate.mwis = [0x00, 0x00, 0x00, 0x00, 0x00];
let recordHelper = worker.SimRecordHelper;
let buf = worker.Buf;
let ioHelper = worker.ICCIOHelper;
let recordSize = ril.iccInfoPrivate.mwis.length;
let recordNum = 1;
ioHelper.updateLinearFixedEF = function (options) {
options.pathId = worker.ICCFileHelper.getEFPath(options.fileId);
options.command = ICC_COMMAND_UPDATE_RECORD;
options.p1 = options.recordNumber;
options.p2 = READ_RECORD_ABSOLUTE_MODE;
options.p3 = recordSize;
ril.iccIO(options);
};
function do_test(isActive, count) {
let mwis = ril.iccInfoPrivate.mwis;
let isUpdated = false;
function buildMwisData() {
let result = mwis.slice(0);
result[0] = isActive? (mwis[0] | 0x01) : (mwis[0] & 0xFE);
result[1] = (count === GECKO_VOICEMAIL_MESSAGE_COUNT_UNKNOWN) ? 0 : count;
return result;
}
buf.sendParcel = function () {
isUpdated = true;
// Request Type.
do_check_eq(this.readInt32(), REQUEST_SIM_IO);
// Token : we don't care
this.readInt32();
// command.
do_check_eq(this.readInt32(), ICC_COMMAND_UPDATE_RECORD);
// fileId.
do_check_eq(this.readInt32(), ICC_EF_MWIS);
// pathId.
do_check_eq(this.readString(),
EF_PATH_MF_SIM + ((ril.appType === CARD_APPTYPE_USIM) ? EF_PATH_ADF_USIM : EF_PATH_DF_GSM));
// p1.
do_check_eq(this.readInt32(), recordNum);
// p2.
do_check_eq(this.readInt32(), READ_RECORD_ABSOLUTE_MODE);
// p3.
do_check_eq(this.readInt32(), recordSize);
// data.
let strLen = this.readInt32();
do_check_eq(recordSize * 2, strLen);
let expectedMwis = buildMwisData();
for (let i = 0; i < recordSize; i++) {
do_check_eq(expectedMwis[i], pduHelper.readHexOctet());
}
this.readStringDelimiter(strLen);
// pin2.
do_check_eq(this.readString(), null);
if (!worker.RILQUIRKS_V5_LEGACY) {
// AID. Ignore because it's from modem.
this.readInt32();
}
};
do_check_false(isUpdated);
recordHelper.updateMWIS({ active: isActive,
msgCount: count });
do_check_true((ril.iccInfoPrivate.mwis) ? isUpdated : !isUpdated);
}
do_test(true, GECKO_VOICEMAIL_MESSAGE_COUNT_UNKNOWN);
do_test(true, 1);
do_test(true, 255);
do_test(false, 0);
// Test if Path ID is correct for SIM.
ril.appType = CARD_APPTYPE_SIM;
do_test(false, 0);
// Test if loadLinearFixedEF() is not invoked in updateMWIS() when
// EF_MWIS is not loaded/available.
delete ril.iccInfoPrivate.mwis;
do_test(false, 0);
run_next_test();
});

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше