зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to b2g-inbound.
This commit is contained in:
Коммит
fafbd75ad2
7
CLOBBER
7
CLOBBER
|
@ -18,4 +18,9 @@
|
|||
# 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;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HyperTextAccessible::HypertextOffsetsToDOMRange(int32_t aStartHTOffset,
|
||||
int32_t aEndHTOffset,
|
||||
nsRange* aRange)
|
||||
bool
|
||||
HyperTextAccessible::OffsetsToDOMRange(int32_t aStartOffset, int32_t aEndOffset,
|
||||
nsRange* aRange)
|
||||
{
|
||||
// If the given offsets are 0 and associated editor is empty then return
|
||||
// collapsed range with editor root element as range container.
|
||||
if (aStartHTOffset == 0 && aEndHTOffset == 0) {
|
||||
DOMPoint startPoint = OffsetToDOMPoint(aStartOffset);
|
||||
if (!startPoint.node)
|
||||
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();
|
||||
if (editor) {
|
||||
bool isEmpty = false;
|
||||
|
@ -565,40 +585,36 @@ HyperTextAccessible::HypertextOffsetsToDOMRange(int32_t aStartHTOffset,
|
|||
editor->GetRootElement(getter_AddRefs(editorRootElm));
|
||||
|
||||
nsCOMPtr<nsINode> editorRoot(do_QueryInterface(editorRootElm));
|
||||
if (editorRoot) {
|
||||
aRange->SetStart(editorRoot, 0);
|
||||
aRange->SetEnd(editorRoot, 0);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
return DOMPoint(editorRoot, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<Accessible> startAcc, endAcc;
|
||||
int32_t startOffset = aStartHTOffset, endOffset = aEndHTOffset;
|
||||
nsIFrame *startFrame = nullptr, *endFrame = nullptr;
|
||||
int32_t childIdx = GetChildIndexAtOffset(aOffset);
|
||||
if (childIdx == -1)
|
||||
return DOMPoint();
|
||||
|
||||
startFrame = GetPosAndText(startOffset, endOffset, nullptr, &endFrame,
|
||||
getter_AddRefs(startAcc), getter_AddRefs(endAcc));
|
||||
if (!startAcc || !endAcc)
|
||||
return NS_ERROR_FAILURE;
|
||||
Accessible* child = GetChildAt(childIdx);
|
||||
int32_t innerOffset = aOffset - GetChildOffset(childIdx);
|
||||
|
||||
DOMPoint startPoint, endPoint;
|
||||
nsresult rv = GetDOMPointByFrameOffset(startFrame, startOffset, startAcc,
|
||||
&startPoint);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// 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();
|
||||
|
||||
rv = aRange->SetStart(startPoint.node, startPoint.idx);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return DOMPoint(content, idx);
|
||||
}
|
||||
|
||||
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);
|
||||
// Case of embedded object. The point is either before or after the element.
|
||||
NS_ASSERTION(innerOffset == 0 || innerOffset == 1, "A wrong inner offset!");
|
||||
nsINode* node = child->GetNode();
|
||||
nsINode* parentNode = node->GetParentNode();
|
||||
return parentNode ?
|
||||
DOMPoint(parentNode, parentNode->IndexOf(node) + innerOffset) :
|
||||
DOMPoint();
|
||||
}
|
||||
|
||||
int32_t
|
||||
|
@ -1580,7 +1596,8 @@ HyperTextAccessible::SetSelectionBoundsAt(int32_t aSelectionNum,
|
|||
if (!range)
|
||||
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
|
||||
// that existing selection range was changed.
|
||||
|
@ -1610,8 +1627,7 @@ HyperTextAccessible::ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset,
|
|||
uint32_t aScrollType)
|
||||
{
|
||||
nsRefPtr<nsRange> range = new nsRange(mContent);
|
||||
nsresult rv = HypertextOffsetsToDOMRange(aStartOffset, aEndOffset, range);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
if (OffsetsToDOMRange(aStartOffset, aEndOffset, range))
|
||||
nsCoreUtils::ScrollSubstringTo(GetFrame(), range, aScrollType);
|
||||
}
|
||||
|
||||
|
@ -1629,8 +1645,7 @@ HyperTextAccessible::ScrollSubstringToPoint(int32_t aStartOffset,
|
|||
this);
|
||||
|
||||
nsRefPtr<nsRange> range = new nsRange(mContent);
|
||||
nsresult rv = HypertextOffsetsToDOMRange(aStartOffset, aEndOffset, range);
|
||||
if (NS_FAILED(rv))
|
||||
if (!OffsetsToDOMRange(aStartOffset, aEndOffset, range))
|
||||
return;
|
||||
|
||||
nsPresContext* presContext = frame->PresContext();
|
||||
|
@ -1658,7 +1673,7 @@ HyperTextAccessible::ScrollSubstringToPoint(int32_t aStartOffset,
|
|||
int16_t hPercent = offsetPointX * 100 / size.width;
|
||||
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))
|
||||
return;
|
||||
|
||||
|
|
|
@ -17,6 +17,9 @@ namespace mozilla {
|
|||
namespace a11y {
|
||||
|
||||
struct DOMPoint {
|
||||
DOMPoint() : node(nullptr), idx(0) { }
|
||||
DOMPoint(nsINode* aNode, int32_t aIdx) : node(aNode), idx(aIdx) { }
|
||||
|
||||
nsINode* node;
|
||||
int32_t idx;
|
||||
};
|
||||
|
@ -128,15 +131,24 @@ public:
|
|||
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 aEndHTOffset [in] the given end hypertext offset
|
||||
* @param aRange [out] the range whose bounds to set
|
||||
* @param aStartOffset [in] the given start hypertext offset
|
||||
* @param aEndOffset [in] the given end hypertext offset
|
||||
* @param aRange [in, out] the range whose bounds to set
|
||||
* @return true if conversion was successful
|
||||
*/
|
||||
nsresult HypertextOffsetsToDOMRange(int32_t aStartHTOffset,
|
||||
int32_t aEndHTOffset,
|
||||
nsRange* aRange);
|
||||
bool OffsetsToDOMRange(int32_t aStartOffset, int32_t aEndOffset,
|
||||
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
|
||||
|
|
|
@ -118,8 +118,8 @@ this.AccessFu = {
|
|||
Output.start();
|
||||
TouchAdapter.start();
|
||||
|
||||
Services.obs.addObserver(this, 'remote-browser-frame-shown', false);
|
||||
Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false);
|
||||
Services.obs.addObserver(this, 'remote-browser-shown', false);
|
||||
Services.obs.addObserver(this, 'inprocess-browser-shown', false);
|
||||
Services.obs.addObserver(this, 'Accessibility:NextObject', false);
|
||||
Services.obs.addObserver(this, 'Accessibility:PreviousObject', false);
|
||||
Services.obs.addObserver(this, 'Accessibility:Focus', false);
|
||||
|
@ -162,8 +162,8 @@ this.AccessFu = {
|
|||
Utils.win.removeEventListener('TabClose', this);
|
||||
Utils.win.removeEventListener('TabSelect', this);
|
||||
|
||||
Services.obs.removeObserver(this, 'remote-browser-frame-shown');
|
||||
Services.obs.removeObserver(this, 'in-process-browser-or-app-frame-shown');
|
||||
Services.obs.removeObserver(this, 'remote-browser-shown');
|
||||
Services.obs.removeObserver(this, 'inprocess-browser-shown');
|
||||
Services.obs.removeObserver(this, 'Accessibility:NextObject');
|
||||
Services.obs.removeObserver(this, 'Accessibility:PreviousObject');
|
||||
Services.obs.removeObserver(this, 'Accessibility:Focus');
|
||||
|
@ -304,11 +304,15 @@ this.AccessFu = {
|
|||
case 'Accessibility:MoveByGranularity':
|
||||
this.Input.moveByGranularity(JSON.parse(aData));
|
||||
break;
|
||||
case 'remote-browser-frame-shown':
|
||||
case 'in-process-browser-or-app-frame-shown':
|
||||
case 'remote-browser-shown':
|
||||
case 'inprocess-browser-shown':
|
||||
{
|
||||
let mm = aSubject.QueryInterface(Ci.nsIFrameLoader).messageManager;
|
||||
this._handleMessageManager(mm);
|
||||
// Ignore notifications that aren't from a BrowserOrApp
|
||||
let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader);
|
||||
if (!frameLoader.ownerIsBrowserOrAppFrame) {
|
||||
return;
|
||||
}
|
||||
this._handleMessageManager(frameLoader.messageManager);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,12 +150,16 @@ let ErrorPage = {
|
|||
},
|
||||
|
||||
init: function errorPageInit() {
|
||||
Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false);
|
||||
Services.obs.addObserver(this, 'remote-browser-frame-shown', false);
|
||||
Services.obs.addObserver(this, 'inprocess-browser-shown', false);
|
||||
Services.obs.addObserver(this, 'remote-browser-shown', false);
|
||||
},
|
||||
|
||||
observe: function errorPageObserve(aSubject, aTopic, aData) {
|
||||
let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader);
|
||||
// Ignore notifications that aren't from a BrowserOrApp
|
||||
if (!frameLoader.ownerIsBrowserOrAppFrame) {
|
||||
return;
|
||||
}
|
||||
let mm = frameLoader.messageManager;
|
||||
|
||||
// This won't happen from dom/ipc/preload.js in non-OOP builds.
|
||||
|
|
|
@ -197,6 +197,7 @@
|
|||
<!-- UI tour experience -->
|
||||
<panel id="UITourTooltip"
|
||||
type="arrow"
|
||||
hidden="true"
|
||||
noautofocus="true"
|
||||
noautohide="true"
|
||||
align="start"
|
||||
|
@ -206,6 +207,7 @@
|
|||
<description id="UITourTooltipDescription" flex="1"/>
|
||||
</panel>
|
||||
<panel id="UITourHighlightContainer"
|
||||
hidden="true"
|
||||
noautofocus="true"
|
||||
noautohide="true"
|
||||
consumeoutsideclicks="false">
|
||||
|
@ -461,7 +463,7 @@
|
|||
#ifdef MENUBAR_CAN_AUTOHIDE
|
||||
toolbarname="&menubarCmd.label;"
|
||||
accesskey="&menubarCmd.accesskey;"
|
||||
#ifdef XP_LINUX
|
||||
#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
|
||||
autohide="true"
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -752,10 +752,9 @@ WriteBitmap(nsIFile* aFile, imgIContainer* aImage)
|
|||
{
|
||||
nsresult rv;
|
||||
|
||||
nsRefPtr<gfxASurface> surface;
|
||||
aImage->GetFrame(imgIContainer::FRAME_FIRST,
|
||||
imgIContainer::FLAG_SYNC_DECODE,
|
||||
getter_AddRefs(surface));
|
||||
nsRefPtr<gfxASurface> surface =
|
||||
aImage->GetFrame(imgIContainer::FRAME_FIRST,
|
||||
imgIContainer::FLAG_SYNC_DECODE);
|
||||
NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
|
||||
|
||||
nsRefPtr<gfxImageSurface> image(surface->GetAsReadableARGB32ImageSurface());
|
||||
|
|
|
@ -14,13 +14,13 @@ function test() {
|
|||
let events = win.EVENTS;
|
||||
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.
|
||||
let committed = promise.defer();
|
||||
variables.oncommit = committed.resolve;
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.test());
|
||||
yield waitForSourceAndCaretAndScopes(panel, ".html", 23);
|
||||
yield committed.promise;
|
||||
|
||||
let firstScope = variables.getScopeAtIndex(0);
|
||||
|
|
|
@ -522,7 +522,6 @@ MenuPopup.prototype = {
|
|||
aEvent.charCode); // in unsigned long charCodeArg);
|
||||
|
||||
ev.mine = true;
|
||||
this.commands.dispatchEvent(ev);
|
||||
|
||||
switch (aEvent.keyCode) {
|
||||
case aEvent.DOM_VK_ESCAPE:
|
||||
|
@ -537,6 +536,7 @@ MenuPopup.prototype = {
|
|||
if (Util.isNavigationKey(aEvent.keyCode)) {
|
||||
aEvent.stopPropagation();
|
||||
aEvent.preventDefault();
|
||||
this.commands.dispatchEvent(ev);
|
||||
} else if (!this._wantTypeBehind) {
|
||||
// Hide the context menu so you can't type behind it.
|
||||
aEvent.stopPropagation();
|
||||
|
|
|
@ -220,6 +220,7 @@ pref("extensions.blocklist.enabled", true);
|
|||
pref("extensions.blocklist.interval", 86400);
|
||||
pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/");
|
||||
pref("extensions.blocklist.detailsURL", "https://www.mozilla.org/%LOCALE%/blocklist/");
|
||||
pref("extensions.showMismatchUI", false);
|
||||
|
||||
/* block popups by default, and notify the user about blocked popups */
|
||||
pref("dom.disable_open_during_load", true);
|
||||
|
|
|
@ -464,6 +464,7 @@ this.UITour = {
|
|||
effect = this.highlightEffects[randomEffect];
|
||||
}
|
||||
highlighter.setAttribute("active", effect);
|
||||
highlighter.parentElement.hidden = false;
|
||||
|
||||
let targetRect = aTargetEl.getBoundingClientRect();
|
||||
|
||||
|
@ -514,16 +515,15 @@ this.UITour = {
|
|||
let tooltipTitle = document.getElementById("UITourTooltipTitle");
|
||||
let tooltipDesc = document.getElementById("UITourTooltipDescription");
|
||||
|
||||
tooltip.hidePopup();
|
||||
if (tooltip.state == "open") {
|
||||
tooltip.hidePopup();
|
||||
}
|
||||
|
||||
tooltipTitle.textContent = aTitle;
|
||||
tooltipDesc.textContent = aDescription;
|
||||
|
||||
tooltip.hidden = false;
|
||||
let alignment = "bottomcenter topright";
|
||||
|
||||
if (tooltip.state == "open") {
|
||||
tooltip.hidePopup();
|
||||
}
|
||||
tooltip.openPopup(aAnchorEl, alignment);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,13 +11,13 @@
|
|||
.devtools-monospace {
|
||||
%ifdef XP_MACOSX
|
||||
font-family: Menlo, monospace;
|
||||
%endif
|
||||
%ifdef XP_LINUX
|
||||
font-family: monospace;
|
||||
font-size: 80%;
|
||||
%endif
|
||||
%ifdef XP_WIN
|
||||
%elifdef XP_WIN
|
||||
font-family: Consolas, monospace;
|
||||
%else
|
||||
font-family: monospace;
|
||||
%endif
|
||||
%if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
|
||||
font-size: 80%;
|
||||
%endif
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@
|
|||
background-image: linear-gradient(to bottom, hsla(209,18%,18%,0.9), hsl(210,11%,16%));
|
||||
border-radius: 3px;
|
||||
overflow-x: hidden;
|
||||
%ifdef XP_LINUX
|
||||
%if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
|
||||
max-height: 32rem;
|
||||
%else
|
||||
max-height: 40rem;
|
||||
|
|
|
@ -54,7 +54,7 @@ html|*.highlighter-nodeinfobar-pseudo-classes {
|
|||
padding: 0;
|
||||
width: 26px;
|
||||
min-height: 26px;
|
||||
%ifndef XP_LINUX
|
||||
%if !defined(MOZ_WIDGET_GTK) && !defined(MOZ_WIDGET_QT)
|
||||
background-color: transparent;
|
||||
%endif
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
%ifdef XP_MACOSX
|
||||
%include ../osx/shared.inc
|
||||
%elifdef XP_LINUX
|
||||
%elif defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
|
||||
%include ../linux/linuxShared.inc
|
||||
%else
|
||||
%include ../windows/windowsShared.inc
|
||||
|
|
До Ширина: | Высота: | Размер: 1.5 KiB После Ширина: | Высота: | Размер: 1.5 KiB |
|
@ -13,6 +13,8 @@ mock.pth:python/mock-1.0.0
|
|||
mozilla.pth:build
|
||||
mozilla.pth:config
|
||||
mozilla.pth:xpcom/typelib/xpt/tools
|
||||
mozilla.pth:dom/bindings
|
||||
mozilla.pth:dom/bindings/parser
|
||||
moztreedocs.pth:tools/docs
|
||||
copy:build/buildconfig.py
|
||||
packages.txt:testing/mozbase/packages.txt
|
||||
|
|
|
@ -111,7 +111,7 @@ interface nsIContentViewManager : nsISupports
|
|||
readonly attribute nsIContentView rootContentView;
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(5b9949dc-56f1-47b6-b6d2-3785bb90ed6d)]
|
||||
[scriptable, builtinclass, uuid(a723673b-a26e-4cc6-ae23-ec70df9d97c9)]
|
||||
interface nsIFrameLoader : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -272,6 +272,11 @@ interface nsIFrameLoader : nsISupports
|
|||
* have a notion of visibility in the parent process when frames are OOP.
|
||||
*/
|
||||
[infallible] attribute boolean visible;
|
||||
|
||||
/**
|
||||
* Find out whether the owner content really is a browser or app frame
|
||||
*/
|
||||
readonly attribute boolean ownerIsBrowserOrAppFrame;
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
|
|
@ -948,9 +948,9 @@ nsFrameLoader::ShowRemoteFrame(const nsIntSize& size,
|
|||
EnsureMessageManager();
|
||||
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (OwnerIsBrowserOrAppFrame() && os && !mRemoteBrowserInitialized) {
|
||||
if (os && !mRemoteBrowserInitialized) {
|
||||
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
|
||||
"remote-browser-frame-shown", nullptr);
|
||||
"remote-browser-shown", nullptr);
|
||||
mRemoteBrowserInitialized = true;
|
||||
}
|
||||
} else {
|
||||
|
@ -1409,6 +1409,14 @@ nsFrameLoader::OwnerIsBrowserOrAppFrame()
|
|||
return browserFrame ? browserFrame->GetReallyIsBrowserOrApp() : false;
|
||||
}
|
||||
|
||||
// The xpcom getter version
|
||||
NS_IMETHODIMP
|
||||
nsFrameLoader::GetOwnerIsBrowserOrAppFrame(bool* aResult)
|
||||
{
|
||||
*aResult = OwnerIsBrowserOrAppFrame();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsFrameLoader::OwnerIsAppFrame()
|
||||
{
|
||||
|
@ -1677,18 +1685,16 @@ nsFrameLoader::MaybeCreateDocShell()
|
|||
mDocShell->SetIsBrowserInsideApp(containingAppId);
|
||||
}
|
||||
|
||||
if (OwnerIsBrowserOrAppFrame()) {
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (os) {
|
||||
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
|
||||
"in-process-browser-or-app-frame-shown", nullptr);
|
||||
}
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (os) {
|
||||
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
|
||||
"inprocess-browser-shown", nullptr);
|
||||
}
|
||||
|
||||
if (mMessageManager) {
|
||||
mMessageManager->LoadFrameScript(
|
||||
NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"),
|
||||
/* allowDelayedLoad = */ true);
|
||||
}
|
||||
if (OwnerIsBrowserOrAppFrame() && mMessageManager) {
|
||||
mMessageManager->LoadFrameScript(
|
||||
NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"),
|
||||
/* allowDelayedLoad = */ true);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -1471,13 +1471,6 @@ GK_ATOM(restart, "restart")
|
|||
GK_ATOM(to, "to")
|
||||
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(accent_, "accent")
|
||||
GK_ATOM(accentunder_, "accentunder")
|
||||
|
|
|
@ -789,8 +789,11 @@ nsScriptLoader::AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest)
|
|||
if (!context) {
|
||||
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);
|
||||
FillCompileOptionsForRequest(aRequest, global, &options);
|
||||
|
@ -1002,8 +1005,11 @@ nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest,
|
|||
if (!context) {
|
||||
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();
|
||||
context->SetProcessingScriptTag(true);
|
||||
|
|
|
@ -1890,7 +1890,7 @@ CanvasRenderingContext2D::Arc(double x, double y, double r,
|
|||
|
||||
EnsureWritablePath();
|
||||
|
||||
ArcToBezier(this, Point(x, y), r, startAngle, endAngle, anticlockwise);
|
||||
ArcToBezier(this, Point(x, y), Size(r, r), startAngle, endAngle, anticlockwise);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -1347,7 +1347,9 @@ nsEventListenerManager::GetScriptGlobalAndDocument(nsIDocument** aDoc)
|
|||
// XXX sXBL/XBL2 issue -- do we really want the owner here? What
|
||||
// if that's the XBL document?
|
||||
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
|
||||
// document, so use GetScopeObject here, not GetScriptHandlingObject.
|
||||
|
|
|
@ -84,6 +84,7 @@ skip-if = true # Disabled due to timeouts.
|
|||
[test_bug855741.html]
|
||||
[test_bug864040.html]
|
||||
[test_bug930374-content.html]
|
||||
[test_bug944847.html]
|
||||
skip-if = toolkit == "gonk"
|
||||
[test_clickevent_on_input.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;
|
||||
}
|
||||
|
||||
gfxIntSize oldFrameSize = mImageContainer->GetCurrentSize();
|
||||
gfx::IntSize oldFrameSize = mImageContainer->GetCurrentSize();
|
||||
TimeStamp lastPaintTime = mImageContainer->GetPaintTime();
|
||||
if (!lastPaintTime.IsNull() && !mPaintTarget.IsNull()) {
|
||||
mPaintDelay = lastPaintTime - mPaintTarget;
|
||||
|
@ -57,7 +57,7 @@ void VideoFrameContainer::SetCurrentFrame(const gfxIntSize& aIntrinsicSize,
|
|||
mImageContainer->UnlockCurrentImage();
|
||||
|
||||
mImageContainer->SetCurrentImage(aImage);
|
||||
gfxIntSize newFrameSize = mImageContainer->GetCurrentSize();
|
||||
gfx::IntSize newFrameSize = mImageContainer->GetCurrentSize();
|
||||
if (oldFrameSize != newFrameSize) {
|
||||
mImageSizeChanged = true;
|
||||
}
|
||||
|
|
|
@ -147,7 +147,7 @@ VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
|
|||
while (!iter.IsEnded()) {
|
||||
VideoChunk chunk = *iter;
|
||||
if (!chunk.IsNull()) {
|
||||
gfxIntSize imgsize = chunk.mFrame.GetImage()->GetSize();
|
||||
gfx::IntSize imgsize = chunk.mFrame.GetImage()->GetSize();
|
||||
nsresult rv = Init(imgsize.width, imgsize.height, aTrackRate);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG("[VideoTrackEncoder]: Fail to initialize the encoder!");
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "MediaPluginReader.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/dom/TimeRanges.h"
|
||||
#include "mozilla/gfx/Point.h"
|
||||
#include "MediaResource.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "MediaPluginDecoder.h"
|
||||
|
@ -172,7 +173,7 @@ bool MediaPluginReader::DecodeVideoFrame(bool &aKeyframeSkip,
|
|||
|
||||
nsAutoPtr<VideoData> v;
|
||||
if (currentImage) {
|
||||
gfxIntSize frameSize = currentImage->GetSize();
|
||||
gfx::IntSize frameSize = currentImage->GetSize();
|
||||
if (frameSize.width != mInitialFrame.width ||
|
||||
frameSize.height != mInitialFrame.height) {
|
||||
// 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,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aSampleRate < 8000 || aSampleRate > 96000 || !aLength) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
if (aSampleRate < 8000 || aSampleRate > 192000 || !aLength || !aNumberOfChannels) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -81,15 +81,21 @@ addLoadEvent(function() {
|
|||
|
||||
expectException(function() {
|
||||
context.createBuffer(2, 2048, 7999);
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
expectException(function() {
|
||||
context.createBuffer(2, 2048, 96001);
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
context.createBuffer(2, 2048, 192001);
|
||||
}, DOMException.INDEX_SIZE_ERR);
|
||||
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() {
|
||||
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();
|
||||
});
|
||||
|
||||
|
|
|
@ -21,8 +21,6 @@ var gTest = {
|
|||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
|
||||
var sp = context.createScriptProcessor(2048 * 4, 1);
|
||||
source.start(0);
|
||||
source.loop = true;
|
||||
source.loopStart = buffer.duration * 0.25;
|
||||
|
|
|
@ -9,9 +9,12 @@
|
|||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var ctx = new AudioContext();
|
||||
ctx.createBuffer(0, 1, ctx.sampleRate);
|
||||
ok(true, "The test should not crash during CC");
|
||||
try {
|
||||
var ctx = new AudioContext();
|
||||
ctx.createBuffer(0, 1, ctx.sampleRate);
|
||||
} catch (e) {
|
||||
ok(true, "The test should not crash during CC");
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "nsPresContext.h"
|
||||
#include "gfxImageSurface.h"
|
||||
#include "gfxContext.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "Layers.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
|
@ -154,8 +155,8 @@ NotifyPull(MediaStreamGraph*, SourceMediaStream* aSource, mozilla::TrackID aID,
|
|||
if (delta > 0) {
|
||||
// nullptr images are allowed
|
||||
if (image) {
|
||||
gfxIntSize size = image->GetSize();
|
||||
segment.AppendFrame(image.forget(), delta, size);
|
||||
gfx::IntSize size = image->GetSize();
|
||||
segment.AppendFrame(image.forget(), delta, gfx::ThebesIntSize(size));
|
||||
} else {
|
||||
segment.AppendFrame(nullptr, delta, gfxIntSize(0,0));
|
||||
}
|
||||
|
|
|
@ -164,6 +164,15 @@ public:
|
|||
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
|
||||
* adding to it. Animations that replace the underlying value may be called
|
||||
|
|
|
@ -27,7 +27,11 @@ public:
|
|||
explicit nsSMILRepeatCount(double 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 {
|
||||
return mCount != kNotSet && mCount != kIndefinite;
|
||||
}
|
||||
|
|
|
@ -112,8 +112,9 @@ namespace
|
|||
//----------------------------------------------------------------------
|
||||
// Helper class: AutoIntervalUpdateBatcher
|
||||
|
||||
// RAII helper to set the mDeferIntervalUpdates flag on an nsSMILTimedElement
|
||||
// and perform the UpdateCurrentInterval when the object is destroyed.
|
||||
// Stack-based helper class to set the mDeferIntervalUpdates flag on an
|
||||
// nsSMILTimedElement and perform the UpdateCurrentInterval when the object is
|
||||
// destroyed.
|
||||
//
|
||||
// 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
|
||||
|
@ -146,6 +147,31 @@ private:
|
|||
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
|
||||
|
||||
|
@ -667,19 +693,32 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, bool aEndOnly)
|
|||
NS_ASSERTION(aContainerTime >= beginTime,
|
||||
"Sample time should not precede current interval");
|
||||
nsSMILTime activeTime = aContainerTime - beginTime;
|
||||
SampleSimpleTime(activeTime);
|
||||
// We register our repeat times as milestones (except when we're
|
||||
// seeking) so we should get a sample at exactly the time we repeat.
|
||||
// (And even when we are seeking we want to update
|
||||
// mCurrentRepeatIteration so we do that first before testing the seek
|
||||
// state.)
|
||||
uint32_t prevRepeatIteration = mCurrentRepeatIteration;
|
||||
if (ActiveTimeToSimpleTime(activeTime, mCurrentRepeatIteration)==0 &&
|
||||
|
||||
// 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);
|
||||
|
||||
// We register our repeat times as milestones (except when we're
|
||||
// seeking) so we should get a sample at exactly the time we repeat.
|
||||
// (And even when we are seeking we want to update
|
||||
// mCurrentRepeatIteration so we do that first before testing the
|
||||
// seek state.)
|
||||
uint32_t prevRepeatIteration = mCurrentRepeatIteration;
|
||||
if (
|
||||
ActiveTimeToSimpleTime(activeTime, mCurrentRepeatIteration)==0 &&
|
||||
mCurrentRepeatIteration != prevRepeatIteration &&
|
||||
mCurrentRepeatIteration &&
|
||||
mSeekState == SEEK_NOT_SEEKING) {
|
||||
FireTimeEventAsync(NS_SMIL_REPEAT,
|
||||
static_cast<int32_t>(mCurrentRepeatIteration));
|
||||
FireTimeEventAsync(NS_SMIL_REPEAT,
|
||||
static_cast<int32_t>(mCurrentRepeatIteration));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -905,8 +944,10 @@ nsSMILTimedElement::UnsetEndSpec(RemovalTestFunction aRemove)
|
|||
nsresult
|
||||
nsSMILTimedElement::SetSimpleDuration(const nsAString& aDurSpec)
|
||||
{
|
||||
nsSMILTimeValue duration;
|
||||
// Update the current interval before returning
|
||||
AutoIntervalUpdater updater(*this);
|
||||
|
||||
nsSMILTimeValue duration;
|
||||
const nsAString& dur = nsSMILParserUtils::TrimWhitespace(aDurSpec);
|
||||
|
||||
// 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");
|
||||
|
||||
mSimpleDur = duration;
|
||||
UpdateCurrentInterval();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -941,8 +981,10 @@ nsSMILTimedElement::UnsetSimpleDuration()
|
|||
nsresult
|
||||
nsSMILTimedElement::SetMin(const nsAString& aMinSpec)
|
||||
{
|
||||
nsSMILTimeValue duration;
|
||||
// Update the current interval before returning
|
||||
AutoIntervalUpdater updater(*this);
|
||||
|
||||
nsSMILTimeValue duration;
|
||||
const nsAString& min = nsSMILParserUtils::TrimWhitespace(aMinSpec);
|
||||
|
||||
if (min.EqualsLiteral("media")) {
|
||||
|
@ -957,7 +999,6 @@ nsSMILTimedElement::SetMin(const nsAString& aMinSpec)
|
|||
NS_ABORT_IF_FALSE(duration.GetMillis() >= 0L, "Invalid duration");
|
||||
|
||||
mMin = duration;
|
||||
UpdateCurrentInterval();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -972,8 +1013,10 @@ nsSMILTimedElement::UnsetMin()
|
|||
nsresult
|
||||
nsSMILTimedElement::SetMax(const nsAString& aMaxSpec)
|
||||
{
|
||||
nsSMILTimeValue duration;
|
||||
// Update the current interval before returning
|
||||
AutoIntervalUpdater updater(*this);
|
||||
|
||||
nsSMILTimeValue duration;
|
||||
const nsAString& max = nsSMILParserUtils::TrimWhitespace(aMaxSpec);
|
||||
|
||||
if (max.EqualsLiteral("media") || max.EqualsLiteral("indefinite")) {
|
||||
|
@ -988,7 +1031,6 @@ nsSMILTimedElement::SetMax(const nsAString& aMaxSpec)
|
|||
}
|
||||
|
||||
mMax = duration;
|
||||
UpdateCurrentInterval();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1023,15 +1065,16 @@ nsSMILTimedElement::UnsetRestart()
|
|||
nsresult
|
||||
nsSMILTimedElement::SetRepeatCount(const nsAString& aRepeatCountSpec)
|
||||
{
|
||||
// Update the current interval before returning
|
||||
AutoIntervalUpdater updater(*this);
|
||||
|
||||
nsSMILRepeatCount newRepeatCount;
|
||||
|
||||
if (nsSMILParserUtils::ParseRepeatCount(aRepeatCountSpec, newRepeatCount)) {
|
||||
mRepeatCount = newRepeatCount;
|
||||
UpdateCurrentInterval();
|
||||
return NS_OK;
|
||||
}
|
||||
mRepeatCount.Unset();
|
||||
UpdateCurrentInterval();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -1045,6 +1088,9 @@ nsSMILTimedElement::UnsetRepeatCount()
|
|||
nsresult
|
||||
nsSMILTimedElement::SetRepeatDur(const nsAString& aRepeatDurSpec)
|
||||
{
|
||||
// Update the current interval before returning
|
||||
AutoIntervalUpdater updater(*this);
|
||||
|
||||
nsSMILTimeValue duration;
|
||||
|
||||
const nsAString& repeatDur =
|
||||
|
@ -1060,7 +1106,6 @@ nsSMILTimedElement::SetRepeatDur(const nsAString& aRepeatDurSpec)
|
|||
}
|
||||
|
||||
mRepeatDur = duration;
|
||||
UpdateCurrentInterval();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1084,12 +1129,8 @@ nsSMILTimedElement::SetFillMode(const nsAString& aFillModeSpec)
|
|||
? nsSMILFillMode(temp.GetEnumValue())
|
||||
: FILL_REMOVE;
|
||||
|
||||
// Check if we're in a fill-able state: i.e. we've played at least one
|
||||
// interval and are now between intervals or at the end of all intervals
|
||||
bool isFillable = HasPlayed() &&
|
||||
(mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE);
|
||||
|
||||
if (mClient && mFillMode != previousFillMode && isFillable) {
|
||||
// Update fill mode of client
|
||||
if (mFillMode != previousFillMode && HasClientInFillRange()) {
|
||||
mClient->Inactivate(mFillMode == FILL_FREEZE);
|
||||
SampleFillValue();
|
||||
}
|
||||
|
@ -1102,9 +1143,9 @@ nsSMILTimedElement::UnsetFillMode()
|
|||
{
|
||||
uint16_t previousFillMode = mFillMode;
|
||||
mFillMode = FILL_REMOVE;
|
||||
if ((mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE) &&
|
||||
previousFillMode == FILL_FREEZE && mClient && HasPlayed())
|
||||
if (previousFillMode == FILL_FREEZE && HasClientInFillRange()) {
|
||||
mClient->Inactivate(false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1803,11 +1844,7 @@ nsSMILTimedElement::CalcActiveEnd(const nsSMILTimeValue& aBegin,
|
|||
NS_ABORT_IF_FALSE(aBegin.IsDefinite(),
|
||||
"Indefinite or unresolved begin time in CalcActiveEnd");
|
||||
|
||||
if (mRepeatDur.IsIndefinite()) {
|
||||
result.SetIndefinite();
|
||||
} else {
|
||||
result = GetRepeatDuration();
|
||||
}
|
||||
result = GetRepeatDuration();
|
||||
|
||||
if (aEnd.IsDefinite()) {
|
||||
nsSMILTime activeDur = aEnd.GetMillis() - aBegin.GetMillis();
|
||||
|
@ -1832,29 +1869,25 @@ nsSMILTimedElement::CalcActiveEnd(const nsSMILTimeValue& aBegin,
|
|||
nsSMILTimeValue
|
||||
nsSMILTimedElement::GetRepeatDuration() const
|
||||
{
|
||||
nsSMILTimeValue result;
|
||||
|
||||
if (mRepeatCount.IsDefinite() && mRepeatDur.IsDefinite()) {
|
||||
if (mSimpleDur.IsDefinite()) {
|
||||
nsSMILTime activeDur =
|
||||
nsSMILTime(mRepeatCount * double(mSimpleDur.GetMillis()));
|
||||
result.SetMillis(std::min(activeDur, mRepeatDur.GetMillis()));
|
||||
} else {
|
||||
result = mRepeatDur;
|
||||
}
|
||||
} 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();
|
||||
nsSMILTimeValue multipliedDuration;
|
||||
if (mRepeatCount.IsDefinite() && mSimpleDur.IsDefinite()) {
|
||||
multipliedDuration.SetMillis(
|
||||
nsSMILTime(mRepeatCount * double(mSimpleDur.GetMillis())));
|
||||
} else {
|
||||
result = mSimpleDur;
|
||||
multipliedDuration.SetIndefinite();
|
||||
}
|
||||
|
||||
return result;
|
||||
nsSMILTimeValue repeatDuration;
|
||||
|
||||
if (mRepeatDur.IsResolved()) {
|
||||
repeatDuration = std::min(multipliedDuration, mRepeatDur);
|
||||
} else if (mRepeatCount.IsSet()) {
|
||||
repeatDuration = multipliedDuration;
|
||||
} else {
|
||||
repeatDuration = mSimpleDur;
|
||||
}
|
||||
|
||||
return repeatDuration;
|
||||
}
|
||||
|
||||
nsSMILTimeValue
|
||||
|
@ -1873,8 +1906,7 @@ nsSMILTimedElement::ApplyMinAndMax(const nsSMILTimeValue& aDuration) const
|
|||
if (aDuration > mMax) {
|
||||
result = mMax;
|
||||
} else if (aDuration < mMin) {
|
||||
nsSMILTimeValue repeatDur = GetRepeatDuration();
|
||||
result = mMin > repeatDur ? repeatDur : mMin;
|
||||
result = mMin;
|
||||
} else {
|
||||
result = aDuration;
|
||||
}
|
||||
|
@ -2068,16 +2100,35 @@ nsSMILTimedElement::SampleFillValue()
|
|||
if (mFillMode != FILL_FREEZE || !mClient)
|
||||
return;
|
||||
|
||||
const nsSMILInterval* prevInterval = GetPreviousInterval();
|
||||
NS_ABORT_IF_FALSE(prevInterval,
|
||||
"Attempting to sample fill value but there is no previous interval");
|
||||
NS_ABORT_IF_FALSE(prevInterval->End()->Time().IsDefinite() &&
|
||||
prevInterval->End()->IsFixedTime(),
|
||||
"Attempting to sample fill value but the endpoint of the previous "
|
||||
"interval is not resolved and fixed");
|
||||
nsSMILTime activeTime;
|
||||
|
||||
nsSMILTime activeTime = prevInterval->End()->Time().GetMillis() -
|
||||
prevInterval->Begin()->Time().GetMillis();
|
||||
if (mElementState == STATE_WAITING || mElementState == STATE_POSTACTIVE) {
|
||||
const nsSMILInterval* prevInterval = GetPreviousInterval();
|
||||
NS_ABORT_IF_FALSE(prevInterval,
|
||||
"Attempting to sample fill value but there is no previous interval");
|
||||
NS_ABORT_IF_FALSE(prevInterval->End()->Time().IsDefinite() &&
|
||||
prevInterval->End()->IsFixedTime(),
|
||||
"Attempting to sample fill value but the endpoint of the previous "
|
||||
"interval is not resolved and fixed");
|
||||
|
||||
activeTime = prevInterval->End()->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;
|
||||
nsSMILTime simpleTime =
|
||||
|
@ -2173,8 +2224,13 @@ nsSMILTimedElement::GetNextMilestone(nsSMILMilestone& aNextMilestone) const
|
|||
// Work out what comes next: the interval end or the next repeat iteration
|
||||
nsSMILTimeValue nextRepeat;
|
||||
if (mSeekState == SEEK_NOT_SEEKING && mSimpleDur.IsDefinite()) {
|
||||
nextRepeat.SetMillis(mCurrentInterval->Begin()->Time().GetMillis() +
|
||||
(mCurrentRepeatIteration + 1) * mSimpleDur.GetMillis());
|
||||
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() +
|
||||
nextRepeatActiveTime);
|
||||
}
|
||||
}
|
||||
nsSMILTimeValue nextMilestone =
|
||||
std::min(mCurrentInterval->End()->Time(), nextRepeat);
|
||||
|
@ -2283,6 +2339,15 @@ nsSMILTimedElement::GetPreviousInterval() const
|
|||
: 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
|
||||
nsSMILTimedElement::EndHasEventConditions() const
|
||||
{
|
||||
|
|
|
@ -512,6 +512,7 @@ protected:
|
|||
const nsSMILInstanceTime* GetEffectiveBeginInstance() const;
|
||||
const nsSMILInterval* GetPreviousInterval() const;
|
||||
bool HasPlayed() const { return !mOldIntervals.IsEmpty(); }
|
||||
bool HasClientInFillRange() const;
|
||||
bool EndHasEventConditions() const;
|
||||
bool AreEndTimesDependentOn(
|
||||
const nsSMILInstanceTime* aBase) const;
|
||||
|
@ -615,6 +616,9 @@ protected:
|
|||
bool mDoDeferredUpdate; // Set if an update to the current interval was
|
||||
// requested while mDeferIntervalUpdates was set
|
||||
|
||||
// Stack-based helper class to call UpdateCurrentInterval when it is destroyed
|
||||
class AutoIntervalUpdater;
|
||||
|
||||
// Recursion depth checking
|
||||
uint8_t mDeleteCount;
|
||||
uint8_t mUpdateIntervalRecursionDepth;
|
||||
|
|
|
@ -32,12 +32,15 @@ support-files =
|
|||
[test_smilGetSimpleDuration.xhtml]
|
||||
[test_smilGetStartTime.xhtml]
|
||||
[test_smilHyperlinking.xhtml]
|
||||
[test_smilInvalidValues.html]
|
||||
[test_smilKeySplines.xhtml]
|
||||
[test_smilKeyTimes.xhtml]
|
||||
[test_smilKeyTimesPacedMode.xhtml]
|
||||
[test_smilMappedAttrFromBy.xhtml]
|
||||
[test_smilMappedAttrFromTo.xhtml]
|
||||
[test_smilMappedAttrPaced.xhtml]
|
||||
[test_smilMinTiming.html]
|
||||
[test_smilRepeatDuration.html]
|
||||
[test_smilRepeatTiming.xhtml]
|
||||
[test_smilReset.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
|
||||
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;
|
||||
|
||||
GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr);
|
||||
|
@ -116,7 +125,7 @@ SVGEllipseElement::BuildPath()
|
|||
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -211,9 +211,9 @@ SVGFEImageElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance,
|
|||
|
||||
nsRefPtr<gfxASurface> currentFrame;
|
||||
if (imageContainer) {
|
||||
imageContainer->GetFrame(imgIContainer::FRAME_CURRENT,
|
||||
imgIContainer::FLAG_SYNC_DECODE,
|
||||
getter_AddRefs(currentFrame));
|
||||
currentFrame =
|
||||
imageContainer->GetFrame(imgIContainer::FRAME_CURRENT,
|
||||
imgIContainer::FLAG_SYNC_DECODE);
|
||||
}
|
||||
|
||||
if (!currentFrame) {
|
||||
|
|
|
@ -1082,28 +1082,7 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, JS::Handle<JSObject*> global,
|
|||
bool
|
||||
nsXBLBinding::AllowScripts()
|
||||
{
|
||||
if (!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());
|
||||
return mPrototypeBinding->GetAllowScripts();
|
||||
}
|
||||
|
||||
nsXBLBinding*
|
||||
|
|
|
@ -141,7 +141,7 @@ public:
|
|||
JS::MutableHandle<JSObject*> aClassObject,
|
||||
bool* aNew);
|
||||
|
||||
bool AllowScripts(); // XXX make const
|
||||
bool AllowScripts();
|
||||
|
||||
mozilla::dom::XBLChildrenElement* FindInsertionPointFor(nsIContent* aChild);
|
||||
|
||||
|
|
|
@ -403,6 +403,24 @@ nsXBLDocumentInfo::nsXBLDocumentInfo(nsIDocument* aDocument)
|
|||
mScriptAccess = allow;
|
||||
}
|
||||
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()
|
||||
{ nsCOMPtr<nsIDocument> copy = mDocument; return copy.forget(); }
|
||||
|
||||
bool GetScriptAccess() { return mScriptAccess; }
|
||||
bool GetScriptAccess() const { return mScriptAccess; }
|
||||
|
||||
nsIURI* DocumentURI() { return mDocument->GetDocumentURI(); }
|
||||
|
||||
|
|
|
@ -214,7 +214,7 @@ nsXBLPrototypeBinding::SetBindingElement(nsIContent* aElement)
|
|||
}
|
||||
|
||||
bool
|
||||
nsXBLPrototypeBinding::GetAllowScripts()
|
||||
nsXBLPrototypeBinding::GetAllowScripts() const
|
||||
{
|
||||
return mXBLDocInfoWeak->GetScriptAccess();
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ public:
|
|||
// binding URIs.
|
||||
bool CompareBindingURI(nsIURI* aURI) const;
|
||||
|
||||
bool GetAllowScripts();
|
||||
bool GetAllowScripts() const;
|
||||
|
||||
nsresult BindingAttached(nsIContent* aBoundElement);
|
||||
nsresult BindingDetached(nsIContent* aBoundElement);
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
file_bug944407.xml
|
||||
|
||||
[test_bug378518.xul]
|
||||
[test_bug398135.xul]
|
||||
|
@ -6,3 +8,4 @@
|
|||
[test_bug721452.xul]
|
||||
[test_bug723676.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_bug821850.xhtml
|
||||
file_bug844783.xhtml
|
||||
file_bug944407.html
|
||||
file_bug944407.xml
|
||||
|
||||
[test_bug310107.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 */
|
||||
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.
|
||||
bool enabled = false;
|
||||
Preferences::GetBool("dom.telephony.enabled", &enabled);
|
||||
NS_ENSURE_TRUE(enabled, false);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(global);
|
||||
return win && CheckPermission(win, "telephony");
|
||||
}
|
||||
|
||||
|
@ -1854,8 +1856,10 @@ bool Navigator::HasInputMethodSupport(JSContext* /* unused */,
|
|||
|
||||
/* static */
|
||||
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.
|
||||
bool enabled = false;
|
||||
Preferences::GetBool("dom.datastore.enabled", &enabled);
|
||||
|
@ -1866,7 +1870,7 @@ Navigator::HasDataStoreSupport(JSContext* /* unused */, JSObject* aGlobal)
|
|||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(global);
|
||||
if (!win) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -250,7 +250,7 @@ public:
|
|||
}
|
||||
static bool HasMobileMessageSupport(JSContext* /* unused */,
|
||||
JSObject* aGlobal);
|
||||
static bool HasTelephonySupport(JSContext* /* unused */,
|
||||
static bool HasTelephonySupport(JSContext* cx,
|
||||
JSObject* aGlobal);
|
||||
static bool HasCameraSupport(JSContext* /* unused */,
|
||||
JSObject* aGlobal);
|
||||
|
@ -286,7 +286,7 @@ public:
|
|||
|
||||
static bool HasInputMethodSupport(JSContext* /* unused */, JSObject* aGlobal);
|
||||
|
||||
static bool HasDataStoreSupport(JSContext* /* unused */, JSObject* aGlobal);
|
||||
static bool HasDataStoreSupport(JSContext* cx, JSObject* aGlobal);
|
||||
|
||||
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):
|
||||
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):
|
||||
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
|
||||
# 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/.
|
||||
# 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/.
|
||||
|
||||
abs_dist := $(abspath $(DIST))
|
||||
webidl_base := $(topsrcdir)/dom/webidl
|
||||
|
||||
webidl_base = $(topsrcdir)/dom/webidl
|
||||
# Generated by moz.build
|
||||
include webidlsrcs.mk
|
||||
|
||||
binding_include_path := mozilla/dom
|
||||
webidl_files += $(generated_events_webidl_files)
|
||||
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)
|
||||
ifdef GNU_CC
|
||||
OS_CXXFLAGS += -Wno-uninitialized
|
||||
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)
|
||||
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
|
||||
PYTHON_UNIT_TESTS += $(srcdir)/mozwebidlcodegen/test/test_mozwebidlcodegen.py
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
ifdef GNU_CC
|
||||
CXXFLAGS += -Wno-uninitialized
|
||||
endif
|
||||
|
||||
# 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 \
|
||||
# TODO This list should be emitted to a .pp file via
|
||||
# GenerateCSS2PropertiesWebIDL.py.
|
||||
css2properties_dependencies = \
|
||||
$(topsrcdir)/layout/style/nsCSSPropList.h \
|
||||
$(topsrcdir)/layout/style/nsCSSPropAliasList.h \
|
||||
$(webidl_base)/CSS2Properties.webidl.in \
|
||||
$(webidl_base)/CSS2PropertiesProps.h \
|
||||
$(srcdir)/GenerateCSS2PropertiesWebIDL.py \
|
||||
$(GLOBAL_DEPS) \
|
||||
$(NULL)
|
||||
|
||||
CSS2Properties.webidl: $(topsrcdir)/layout/style/nsCSSPropList.h \
|
||||
$(topsrcdir)/layout/style/nsCSSPropAliasList.h \
|
||||
$(webidl_base)/CSS2Properties.webidl.in \
|
||||
$(webidl_base)/CSS2PropertiesProps.h \
|
||||
$(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
|
||||
CSS2Properties.webidl: $(css2properties_dependencies)
|
||||
$(CPP) $(DEFINES) $(ACDEFINES) -I$(topsrcdir)/layout/style \
|
||||
$(webidl_base)/CSS2PropertiesProps.h | \
|
||||
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) \
|
||||
$(srcdir)/GenerateCSS2PropertiesWebIDL.py \
|
||||
$(webidl_base)/CSS2Properties.webidl.in > $@
|
||||
|
||||
$(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 \
|
||||
# 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)
|
||||
|
||||
$(CACHE_DIR)/.done:
|
||||
$(MKDIR) -p $(CACHE_DIR)
|
||||
$(call include_deps,codegen.pp)
|
||||
|
||||
codegen.pp: $(codegen_dependencies)
|
||||
$(call py_action,webidl,$(srcdir))
|
||||
@$(TOUCH) $@
|
||||
|
||||
# Running GlobalGen.py updates ParserResults.pkl as a side-effect
|
||||
ParserResults.pkl: $(globalgen_dependencies)
|
||||
$(info Generating global WebIDL files)
|
||||
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) $@
|
||||
.PHONY: compiletests
|
||||
compiletests:
|
||||
$(call SUBMAKE,libs,test)
|
||||
|
||||
GARBAGE += \
|
||||
webidlyacc.py \
|
||||
codegen.pp \
|
||||
codegen.json \
|
||||
parser.out \
|
||||
$(wildcard *-example.h) \
|
||||
$(wildcard *-example.cpp) \
|
||||
.BindingGen \
|
||||
.all-webidl-file-list \
|
||||
.generated-events-webidl-files \
|
||||
.changed-dependency-list \
|
||||
$(binding_dependency_trackers) \
|
||||
WebIDLGrammar.pkl \
|
||||
$(wildcard *.h) \
|
||||
$(wildcard *Binding.cpp) \
|
||||
$(wildcard *Event.cpp) \
|
||||
$(wildcard *-event.cpp) \
|
||||
$(wildcard *.webidl) \
|
||||
$(NULL)
|
||||
|
||||
# Make sure all binding header files are created during the export stage, so we
|
||||
# don't have issues with .cpp files being compiled before we've generated the
|
||||
# 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)
|
||||
DIST_GARBAGE += \
|
||||
file-lists.json \
|
||||
$(NULL)
|
||||
|
|
|
@ -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
|
||||
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',
|
||||
description='Run WebIDL tests.')
|
||||
@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
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
TEST_DIRS += ['test']
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'ErrorResult.h',
|
||||
]
|
||||
|
@ -85,3 +87,6 @@ if CONFIG['MOZ_AUDIO_CHANNEL_MANAGER']:
|
|||
]
|
||||
|
||||
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,
|
||||
# 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
|
||||
|
||||
# But the webidl actually lives in our parent dir
|
||||
test_webidl_files := $(addprefix ../,$(test_webidl_files))
|
||||
# Store the actual locations of our source preprocessed files, so we
|
||||
# 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)
|
||||
# $(test_sources) comes from webidlsrcs.mk.
|
||||
# TODO Update this variable in backend.mk.
|
||||
CPPSRCS += $(addprefix ../,$(test_sources))
|
||||
|
||||
ifdef GNU_CC
|
||||
CXXFLAGS += -Wno-uninitialized
|
||||
OS_CXXFLAGS += -Wno-uninitialized
|
||||
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
|
||||
# rules.mk and running make with no target in this dir does the right thing.
|
||||
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::
|
||||
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.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']
|
||||
|
||||
TEST_WEBIDL_FILES += [
|
||||
'TestDictionary.webidl',
|
||||
'TestJSImplInheritanceGen.webidl',
|
||||
'TestTypedef.webidl',
|
||||
]
|
||||
|
||||
PREPROCESSED_TEST_WEBIDL_FILES += [
|
||||
'TestCodeGen.webidl',
|
||||
'TestExampleGen.webidl',
|
||||
'TestJSImplGen.webidl',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/bindings',
|
||||
'/js/xpconnect/src',
|
||||
'/js/xpconnect/wrappers',
|
||||
]
|
||||
|
||||
|
|
|
@ -65,8 +65,8 @@ BrowserElementParentFactory.prototype = {
|
|||
// alive for as long as its frame element lives.
|
||||
this._bepMap = new WeakMap();
|
||||
|
||||
Services.obs.addObserver(this, 'remote-browser-frame-shown', /* ownsWeak = */ true);
|
||||
Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', /* ownsWeak = */ true);
|
||||
Services.obs.addObserver(this, 'remote-browser-shown', /* ownsWeak = */ true);
|
||||
Services.obs.addObserver(this, 'inprocess-browser-shown', /* ownsWeak = */ true);
|
||||
},
|
||||
|
||||
_browserFramesPrefEnabled: function() {
|
||||
|
@ -79,11 +79,19 @@ BrowserElementParentFactory.prototype = {
|
|||
},
|
||||
|
||||
_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);
|
||||
this._createBrowserElementParent(frameLoader, /* hasRemoteFrame = */ false);
|
||||
},
|
||||
|
||||
_observeRemoteBrowserFrameShown: function(frameLoader) {
|
||||
// Ignore notifications that aren't from a BrowserOrApp
|
||||
if (!frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerIsBrowserOrAppFrame) {
|
||||
return;
|
||||
}
|
||||
debug("Remote browser frame shown " + frameLoader);
|
||||
this._createBrowserElementParent(frameLoader, /* hasRemoteFrame = */ true);
|
||||
},
|
||||
|
@ -103,10 +111,10 @@ BrowserElementParentFactory.prototype = {
|
|||
this._init();
|
||||
}
|
||||
break;
|
||||
case 'remote-browser-frame-shown':
|
||||
case 'remote-browser-shown':
|
||||
this._observeRemoteBrowserFrameShown(subject);
|
||||
break;
|
||||
case 'in-process-browser-or-app-frame-shown':
|
||||
case 'inprocess-browser-shown':
|
||||
this._observeInProcessBrowserFrameShown(subject);
|
||||
break;
|
||||
case 'content-document-global-created':
|
||||
|
|
|
@ -22,11 +22,6 @@ function testSteps()
|
|||
objectStore.add(Bob);
|
||||
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")
|
||||
.index("name").openCursor().onsuccess = function(event) {
|
||||
event.target.transaction.oncomplete = continueToNextStep;
|
||||
|
|
|
@ -35,11 +35,6 @@ function testSteps()
|
|||
let objectStore = db.createObjectStore("foo", { keyPath: "ss" });
|
||||
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++) {
|
||||
objectStore.add(objectStoreData[i]);
|
||||
}
|
||||
|
|
|
@ -34,9 +34,6 @@ function testSteps()
|
|||
|
||||
event.target.onsuccess = continueToNextStep;
|
||||
|
||||
// Bug 943409.
|
||||
eval('');
|
||||
|
||||
for (let objectStoreIndex in objectStoreData) {
|
||||
const objectStoreInfo = objectStoreData[objectStoreIndex];
|
||||
let objectStore = db.createObjectStore(objectStoreInfo.name,
|
||||
|
|
|
@ -18,9 +18,6 @@ function testSteps()
|
|||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
// Bug 943409.
|
||||
eval('');
|
||||
|
||||
for each (let autoIncrement in [false, true]) {
|
||||
let objectStore =
|
||||
db.createObjectStore(autoIncrement, { keyPath: "id",
|
||||
|
|
|
@ -44,8 +44,8 @@ this.Keyboard = {
|
|||
},
|
||||
|
||||
init: function keyboardInit() {
|
||||
Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false);
|
||||
Services.obs.addObserver(this, 'remote-browser-frame-shown', false);
|
||||
Services.obs.addObserver(this, 'inprocess-browser-shown', false);
|
||||
Services.obs.addObserver(this, 'remote-browser-shown', false);
|
||||
Services.obs.addObserver(this, 'oop-frameloader-crashed', false);
|
||||
|
||||
for (let name of this._messageNames)
|
||||
|
@ -63,6 +63,10 @@ this.Keyboard = {
|
|||
ppmm.broadcastAsyncMessage('Keyboard:FocusChange', { 'type': 'blur' });
|
||||
}
|
||||
} else {
|
||||
// Ignore notifications that aren't from a BrowserOrApp
|
||||
if (!frameLoader.ownerIsBrowserOrAppFrame) {
|
||||
return;
|
||||
}
|
||||
this.initFormsFrameScript(mm);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -602,7 +602,7 @@ ParticularProcessPriorityManager::Init()
|
|||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (os) {
|
||||
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, "frameloader-visible-changed", /* ownsWeak */ true);
|
||||
}
|
||||
|
@ -674,7 +674,7 @@ ParticularProcessPriorityManager::Observe(nsISupports* aSubject,
|
|||
|
||||
if (topic.EqualsLiteral("audio-channel-process-changed")) {
|
||||
OnAudioChannelProcessChanged(aSubject);
|
||||
} else if (topic.EqualsLiteral("remote-browser-frame-shown")) {
|
||||
} else if (topic.EqualsLiteral("remote-browser-shown")) {
|
||||
OnRemoteBrowserFrameShown(aSubject);
|
||||
} else if (topic.EqualsLiteral("ipc:browser-destroyed")) {
|
||||
OnTabParentDestroyed(aSubject);
|
||||
|
@ -747,6 +747,13 @@ ParticularProcessPriorityManager::OnRemoteBrowserFrameShown(nsISupports* aSubjec
|
|||
nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject);
|
||||
NS_ENSURE_TRUE_VOID(fl);
|
||||
|
||||
// Ignore notifications that aren't from a BrowserOrApp
|
||||
bool isBrowserOrApp;
|
||||
fl->GetOwnerIsBrowserOrAppFrame(&isBrowserOrApp);
|
||||
if (!isBrowserOrApp) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITabParent> tp;
|
||||
fl->GetTabParent(getter_AddRefs(tp));
|
||||
NS_ENSURE_TRUE_VOID(tp);
|
||||
|
|
|
@ -107,12 +107,9 @@ if CONFIG['MOZ_NFC']:
|
|||
if CONFIG['MOZ_B2G']:
|
||||
PARALLEL_DIRS += ['downloads']
|
||||
|
||||
# bindings/test is here, because it needs to build after bindings/, and
|
||||
# we build subdirectories before ourselves.
|
||||
TEST_DIRS += [
|
||||
'tests',
|
||||
'imptests',
|
||||
'bindings/test',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'cocoa', 'windows', 'android', 'qt', 'os2'):
|
||||
|
|
|
@ -69,7 +69,6 @@ var ecmaGlobals =
|
|||
{name: "NaN", xbl: false},
|
||||
"Number",
|
||||
"Object",
|
||||
{name: "ParallelArray", nightly: true},
|
||||
"Proxy",
|
||||
"RangeError",
|
||||
"ReferenceError",
|
||||
|
|
|
@ -553,18 +553,6 @@ if CONFIG['MOZ_B2G_FM']:
|
|||
'FMRadio.webidl',
|
||||
]
|
||||
|
||||
if CONFIG['ENABLE_TESTS']:
|
||||
TEST_WEBIDL_FILES += [
|
||||
'TestDictionary.webidl',
|
||||
'TestJSImplInheritanceGen.webidl',
|
||||
'TestTypedef.webidl',
|
||||
]
|
||||
PREPROCESSED_TEST_WEBIDL_FILES += [
|
||||
'TestCodeGen.webidl',
|
||||
'TestExampleGen.webidl',
|
||||
'TestJSImplGen.webidl',
|
||||
]
|
||||
|
||||
GENERATED_EVENTS_WEBIDL_FILES = [
|
||||
'BlobEvent.webidl',
|
||||
'CallGroupErrorEvent.webidl',
|
||||
|
|
|
@ -1208,7 +1208,7 @@ public:
|
|||
JS::Rooted<JSObject*> target(aCx, JS::CurrentGlobalOrNull(aCx));
|
||||
NS_ASSERTION(target, "This should never be null!");
|
||||
|
||||
bool preventDefaultCalled;
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
nsIScriptGlobalObject* sgo;
|
||||
|
||||
if (aWorkerPrivate) {
|
||||
|
@ -1222,15 +1222,12 @@ public:
|
|||
event.fileName = aFilename.get();
|
||||
event.typeString = NS_LITERAL_STRING("error");
|
||||
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
nsIDOMEventTarget* target = static_cast<nsIDOMEventTarget*>(globalTarget);
|
||||
if (NS_FAILED(nsEventDispatcher::Dispatch(target, nullptr, &event,
|
||||
nullptr, &status))) {
|
||||
NS_WARNING("Failed to dispatch worker thread error event!");
|
||||
status = nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
preventDefaultCalled = status == nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
else if ((sgo = nsJSUtils::GetStaticScriptGlobal(target))) {
|
||||
// Icky, we have to fire an InternalScriptErrorEvent...
|
||||
|
@ -1239,16 +1236,14 @@ public:
|
|||
event.errorMsg = aMessage.get();
|
||||
event.fileName = aFilename.get();
|
||||
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
if (NS_FAILED(sgo->HandleScriptError(&event, &status))) {
|
||||
NS_WARNING("Failed to dispatch main thread error event!");
|
||||
status = nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
preventDefaultCalled = status == nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
if (preventDefaultCalled) {
|
||||
// Was preventDefault() called?
|
||||
if (status == nsEventStatus_eConsumeNoDefault) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,7 +121,7 @@ void
|
|||
FlattenedPath::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
|
||||
float aEndAngle, bool aAntiClockwise)
|
||||
{
|
||||
ArcToBezier(this, aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise);
|
||||
ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
|
||||
}
|
||||
|
||||
Float
|
||||
|
@ -407,7 +407,8 @@ FlattenBezier(const BezierControlPoints &aControlPoints,
|
|||
return;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
// Check that at least one of the inflection points is inside [0..1]
|
||||
if (count == 0 || ((t1 < 0 || t1 > 1.0) && ((t2 < 0 || t2 > 1.0) || count == 1)) ) {
|
||||
FlattenBezierCurveSegment(aControlPoints, aSink, aTolerance);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ void
|
|||
PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
|
||||
float aEndAngle, bool aAntiClockwise)
|
||||
{
|
||||
ArcToBezier(this, aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise);
|
||||
ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
|
||||
}
|
||||
|
||||
Point
|
||||
|
|
|
@ -13,11 +13,11 @@ namespace mozilla {
|
|||
namespace gfx {
|
||||
|
||||
template <typename T>
|
||||
void ArcToBezier(T* aSink, const Point &aOrigin, float aRadius, float aStartAngle,
|
||||
float aEndAngle, bool aAntiClockwise)
|
||||
void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius,
|
||||
float aStartAngle, float aEndAngle, bool aAntiClockwise)
|
||||
{
|
||||
Point startPoint(aOrigin.x + cos(aStartAngle) * aRadius,
|
||||
aOrigin.y + sin(aStartAngle) * aRadius);
|
||||
Point startPoint(aOrigin.x + cos(aStartAngle) * aRadius.width,
|
||||
aOrigin.y + sin(aStartAngle) * aRadius.height);
|
||||
|
||||
aSink->LineTo(startPoint);
|
||||
|
||||
|
@ -56,23 +56,25 @@ void ArcToBezier(T* aSink, const Point &aOrigin, float aRadius, float aStartAngl
|
|||
currentEndAngle = currentStartAngle + arcSweepLeft * sweepDirection;
|
||||
}
|
||||
|
||||
Point currentStartPoint(aOrigin.x + cos(currentStartAngle) * aRadius,
|
||||
aOrigin.y + sin(currentStartAngle) * aRadius);
|
||||
Point currentEndPoint(aOrigin.x + cos(currentEndAngle) * aRadius,
|
||||
aOrigin.y + sin(currentEndAngle) * aRadius);
|
||||
Point currentStartPoint(aOrigin.x + cos(currentStartAngle) * aRadius.width,
|
||||
aOrigin.y + sin(currentStartAngle) * aRadius.height);
|
||||
Point currentEndPoint(aOrigin.x + cos(currentEndAngle) * aRadius.width,
|
||||
aOrigin.y + sin(currentEndAngle) * aRadius.height);
|
||||
|
||||
// Calculate kappa constant for partial curve. The sign of angle in the
|
||||
// tangent will actually ensure this is negative for a counter clockwise
|
||||
// sweep, so changing signs later isn't needed.
|
||||
Float kappa = (4.0f / 3.0f) * tan((currentEndAngle - currentStartAngle) / 4.0f) * aRadius;
|
||||
Float kappaFactor = (4.0f / 3.0f) * tan((currentEndAngle - currentStartAngle) / 4.0f);
|
||||
Float kappaX = kappaFactor * aRadius.width;
|
||||
Float kappaY = kappaFactor * aRadius.height;
|
||||
|
||||
Point tangentStart(-sin(currentStartAngle), cos(currentStartAngle));
|
||||
Point cp1 = currentStartPoint;
|
||||
cp1 += tangentStart * kappa;
|
||||
cp1 += Point(tangentStart.x * kappaX, tangentStart.y * kappaY);
|
||||
|
||||
Point revTangentEnd(sin(currentEndAngle), -cos(currentEndAngle));
|
||||
Point cp2 = currentEndPoint;
|
||||
cp2 += revTangentEnd * kappa;
|
||||
cp2 += Point(revTangentEnd.x * kappaX, revTangentEnd.y * kappaY);
|
||||
|
||||
aSink->BezierTo(cp1, cp2, currentEndPoint);
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ void
|
|||
PathBuilderSkia::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
|
||||
float aEndAngle, bool aAntiClockwise)
|
||||
{
|
||||
ArcToBezier(this, aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise);
|
||||
ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
|
||||
}
|
||||
|
||||
Point
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace gl {
|
|||
|
||||
already_AddRefed<TextureImage>
|
||||
CreateTextureImage(GLContext* gl,
|
||||
const nsIntSize& aSize,
|
||||
const gfx::IntSize& aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
GLenum aWrapMode,
|
||||
TextureImage::Flags aFlags,
|
||||
|
@ -101,7 +101,7 @@ TextureImage::UpdateFromDataSource(gfx::DataSourceSurface *aSurface,
|
|||
}
|
||||
|
||||
gfx::IntRect TextureImage::GetTileRect() {
|
||||
return gfx::IntRect(gfx::IntPoint(0,0), ToIntSize(mSize));
|
||||
return gfx::IntRect(gfx::IntPoint(0,0), mSize);
|
||||
}
|
||||
|
||||
gfx::IntRect TextureImage::GetSrcTileRect() {
|
||||
|
@ -133,13 +133,13 @@ BasicTextureImage::BeginUpdate(nsIntRegion& aRegion)
|
|||
if (CanUploadSubTextures(mGLContext)) {
|
||||
GetUpdateRegion(aRegion);
|
||||
} else {
|
||||
aRegion = nsIntRect(nsIntPoint(0, 0), mSize);
|
||||
aRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize));
|
||||
}
|
||||
|
||||
mUpdateRegion = aRegion;
|
||||
|
||||
nsIntRect rgnSize = mUpdateRegion.GetBounds();
|
||||
if (!nsIntRect(nsIntPoint(0, 0), mSize).Contains(rgnSize)) {
|
||||
if (!nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)).Contains(rgnSize)) {
|
||||
NS_ERROR("update outside of image");
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ BasicTextureImage::GetUpdateRegion(nsIntRegion& aForRegion)
|
|||
// changed, we need to recreate our backing surface and force the
|
||||
// client to paint everything
|
||||
if (mTextureState != Valid)
|
||||
aForRegion = nsIntRect(nsIntPoint(0, 0), mSize);
|
||||
aForRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -248,7 +248,7 @@ BasicTextureImage::DirectUpdate(gfxASurface* aSurf, const nsIntRegion& aRegion,
|
|||
}
|
||||
|
||||
void
|
||||
BasicTextureImage::Resize(const nsIntSize& aSize)
|
||||
BasicTextureImage::Resize(const gfx::IntSize& aSize)
|
||||
{
|
||||
NS_ASSERTION(!mUpdateSurface, "Resize() while in update?");
|
||||
|
||||
|
@ -268,13 +268,8 @@ BasicTextureImage::Resize(const nsIntSize& aSize)
|
|||
mSize = aSize;
|
||||
}
|
||||
|
||||
// Moz2D equivalents...
|
||||
void TextureImage::Resize(const gfx::IntSize& aSize) {
|
||||
Resize(ThebesIntSize(aSize));
|
||||
}
|
||||
|
||||
gfx::IntSize TextureImage::GetSize() const {
|
||||
return ToIntSize(mSize);
|
||||
return mSize;
|
||||
}
|
||||
|
||||
TextureImage::TextureImage(const gfx::IntSize& aSize,
|
||||
|
@ -316,16 +311,6 @@ BasicTextureImage::BasicTextureImage(GLuint aTexture,
|
|||
, mUpdateOffset(0, 0)
|
||||
{}
|
||||
|
||||
already_AddRefed<TextureImage>
|
||||
CreateBasicTextureImage(GLContext* aGL,
|
||||
const gfx::IntSize& aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
GLenum aWrapMode,
|
||||
TextureImage::Flags aFlags)
|
||||
{
|
||||
return CreateBasicTextureImage(aGL, ThebesIntSize(aSize), aContentType, aWrapMode, aFlags);
|
||||
}
|
||||
|
||||
static bool
|
||||
WantsSmallTiles(GLContext* gl)
|
||||
{
|
||||
|
@ -345,7 +330,7 @@ WantsSmallTiles(GLContext* gl)
|
|||
}
|
||||
|
||||
TiledTextureImage::TiledTextureImage(GLContext* aGL,
|
||||
nsIntSize aSize,
|
||||
gfx::IntSize aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
TextureImage::Flags aFlags,
|
||||
TextureImage::ImageFormat aImageFormat)
|
||||
|
@ -438,7 +423,7 @@ TiledTextureImage::GetUpdateRegion(nsIntRegion& aForRegion)
|
|||
// if the texture hasn't been initialized yet, or something important
|
||||
// changed, we need to recreate our backing surface and force the
|
||||
// client to paint everything
|
||||
aForRegion = nsIntRect(nsIntPoint(0, 0), mSize);
|
||||
aForRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -484,7 +469,7 @@ TiledTextureImage::BeginUpdate(nsIntRegion& aRegion)
|
|||
// if the texture hasn't been initialized yet, or something important
|
||||
// changed, we need to recreate our backing surface and force the
|
||||
// client to paint everything
|
||||
aRegion = nsIntRect(nsIntPoint(0, 0), mSize);
|
||||
aRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize));
|
||||
}
|
||||
|
||||
nsIntRect bounds = aRegion.GetBounds();
|
||||
|
@ -637,7 +622,7 @@ TiledTextureImage::BindTexture(GLenum aTextureUnit)
|
|||
* each column, and extra rows are pruned after iteration over the entire image
|
||||
* finishes.
|
||||
*/
|
||||
void TiledTextureImage::Resize(const nsIntSize& aSize)
|
||||
void TiledTextureImage::Resize(const gfx::IntSize& aSize)
|
||||
{
|
||||
if (mSize == aSize && mTextureState != Created) {
|
||||
return;
|
||||
|
@ -734,7 +719,7 @@ uint32_t TiledTextureImage::GetTileCount()
|
|||
|
||||
already_AddRefed<TextureImage>
|
||||
CreateBasicTextureImage(GLContext* aGL,
|
||||
const nsIntSize& aSize,
|
||||
const gfx::IntSize& aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
GLenum aWrapMode,
|
||||
TextureImage::Flags aFlags,
|
||||
|
|
|
@ -158,14 +158,12 @@ public:
|
|||
* If this isn't implemented by a subclass, it will just perform
|
||||
* a dummy BeginUpdate/EndUpdate pair.
|
||||
*/
|
||||
virtual void Resize(const nsIntSize& aSize) {
|
||||
virtual void Resize(const gfx::IntSize& aSize) {
|
||||
mSize = aSize;
|
||||
nsIntRegion r(nsIntRect(0, 0, aSize.width, aSize.height));
|
||||
BeginUpdate(r);
|
||||
EndUpdate();
|
||||
}
|
||||
// Moz2D equivalent...
|
||||
void Resize(const gfx::IntSize& aSize);
|
||||
|
||||
/**
|
||||
* Mark this texture as having valid contents. Call this after modifying
|
||||
|
@ -240,7 +238,7 @@ protected:
|
|||
|
||||
virtual gfx::IntRect GetSrcTileRect();
|
||||
|
||||
nsIntSize mSize;
|
||||
gfx::IntSize mSize;
|
||||
GLenum mWrapMode;
|
||||
ContentType mContentType;
|
||||
ImageFormat mImageFormat;
|
||||
|
@ -302,7 +300,7 @@ public:
|
|||
|
||||
virtual bool InUpdate() const { return !!mUpdateSurface; }
|
||||
|
||||
virtual void Resize(const nsIntSize& aSize);
|
||||
virtual void Resize(const gfx::IntSize& aSize);
|
||||
|
||||
protected:
|
||||
GLuint mTexture;
|
||||
|
@ -325,7 +323,7 @@ class TiledTextureImage
|
|||
{
|
||||
public:
|
||||
TiledTextureImage(GLContext* aGL,
|
||||
nsIntSize aSize,
|
||||
gfx::IntSize aSize,
|
||||
TextureImage::ContentType,
|
||||
TextureImage::Flags aFlags = TextureImage::NoFlags,
|
||||
TextureImage::ImageFormat aImageFormat = gfxImageFormatUnknown);
|
||||
|
@ -334,7 +332,7 @@ public:
|
|||
virtual gfxASurface* BeginUpdate(nsIntRegion& aRegion);
|
||||
virtual void GetUpdateRegion(nsIntRegion& aForRegion);
|
||||
virtual void EndUpdate();
|
||||
virtual void Resize(const nsIntSize& aSize);
|
||||
virtual void Resize(const gfx::IntSize& aSize);
|
||||
virtual uint32_t GetTileCount();
|
||||
virtual void BeginTileIteration();
|
||||
virtual bool NextTile();
|
||||
|
@ -356,7 +354,7 @@ protected:
|
|||
void* mIterationCallbackData;
|
||||
nsTArray< nsRefPtr<TextureImage> > mImages;
|
||||
bool mInUpdate;
|
||||
nsIntSize mSize;
|
||||
gfx::IntSize mSize;
|
||||
unsigned int mTileSize;
|
||||
unsigned int mRows, mColumns;
|
||||
GLContext* mGL;
|
||||
|
@ -375,19 +373,12 @@ protected:
|
|||
*/
|
||||
already_AddRefed<TextureImage>
|
||||
CreateBasicTextureImage(GLContext* aGL,
|
||||
const nsIntSize& aSize,
|
||||
const gfx::IntSize& aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
GLenum aWrapMode,
|
||||
TextureImage::Flags aFlags,
|
||||
TextureImage::ImageFormat aImageFormat = gfxImageFormatUnknown);
|
||||
|
||||
already_AddRefed<TextureImage>
|
||||
CreateBasicTextureImage(GLContext* aGL,
|
||||
const gfx::IntSize& aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
GLenum aWrapMode,
|
||||
TextureImage::Flags aFlags);
|
||||
|
||||
/**
|
||||
* Return a valid, allocated TextureImage of |aSize| with
|
||||
* |aContentType|. If |aContentType| is COLOR, |aImageFormat| can be used
|
||||
|
@ -406,7 +397,7 @@ CreateBasicTextureImage(GLContext* aGL,
|
|||
*/
|
||||
already_AddRefed<TextureImage>
|
||||
CreateTextureImage(GLContext* gl,
|
||||
const nsIntSize& aSize,
|
||||
const gfx::IntSize& aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
GLenum aWrapMode,
|
||||
TextureImage::Flags aFlags = TextureImage::NoFlags,
|
||||
|
|
|
@ -47,7 +47,7 @@ private:
|
|||
|
||||
already_AddRefed<TextureImage>
|
||||
CreateTextureImageCGL(GLContext *gl,
|
||||
const nsIntSize& aSize,
|
||||
const gfx::IntSize& aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
GLenum aWrapMode,
|
||||
TextureImage::Flags aFlags,
|
||||
|
|
|
@ -108,7 +108,7 @@ TextureImageCGL::FinishedSurfaceUpload()
|
|||
|
||||
already_AddRefed<TextureImage>
|
||||
CreateTextureImageCGL(GLContext* gl,
|
||||
const nsIntSize& aSize,
|
||||
const gfx::IntSize& aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
GLenum aWrapMode,
|
||||
TextureImage::Flags aFlags,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "GLContext.h"
|
||||
#include "GLUploadHelpers.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "mozilla/gfx/Types.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -103,7 +104,7 @@ TextureImageEGL::GetUpdateRegion(nsIntRegion& aForRegion)
|
|||
if (mTextureState != Valid) {
|
||||
// if the texture hasn't been initialized yet, force the
|
||||
// client to paint everything
|
||||
aForRegion = nsIntRect(nsIntPoint(0, 0), mSize);
|
||||
aForRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize));
|
||||
}
|
||||
|
||||
// We can only draw a rectangle, not subregions due to
|
||||
|
@ -123,7 +124,7 @@ TextureImageEGL::BeginUpdate(nsIntRegion& aRegion)
|
|||
mUpdateRect = aRegion.GetBounds();
|
||||
|
||||
//printf_stderr("BeginUpdate with updateRect [%d %d %d %d]\n", mUpdateRect.x, mUpdateRect.y, mUpdateRect.width, mUpdateRect.height);
|
||||
if (!nsIntRect(nsIntPoint(0, 0), mSize).Contains(mUpdateRect)) {
|
||||
if (!nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)).Contains(mUpdateRect)) {
|
||||
NS_ERROR("update outside of image");
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -173,7 +174,7 @@ TextureImageEGL::EndUpdate()
|
|||
|
||||
if (mTextureState != Valid) {
|
||||
NS_ASSERTION(mUpdateRect.x == 0 && mUpdateRect.y == 0 &&
|
||||
mUpdateRect.Size() == mSize,
|
||||
mUpdateRect.Size() == gfx::ThebesIntSize(mSize),
|
||||
"Bad initial update on non-created texture!");
|
||||
|
||||
mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D,
|
||||
|
@ -242,7 +243,7 @@ TextureImageEGL::BindTexture(GLenum aTextureUnit)
|
|||
}
|
||||
|
||||
void
|
||||
TextureImageEGL::Resize(const nsIntSize& aSize)
|
||||
TextureImageEGL::Resize(const gfx::IntSize& aSize)
|
||||
{
|
||||
NS_ASSERTION(!mUpdateSurface, "Resize() while in update?");
|
||||
|
||||
|
@ -313,7 +314,7 @@ TextureImageEGL::DestroyEGLSurface(void)
|
|||
|
||||
already_AddRefed<TextureImage>
|
||||
CreateTextureImageEGL(GLContext *gl,
|
||||
const nsIntSize& aSize,
|
||||
const gfx::IntSize& aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
GLenum aWrapMode,
|
||||
TextureImage::Flags aFlags,
|
||||
|
|
|
@ -47,7 +47,7 @@ public:
|
|||
|
||||
virtual bool InUpdate() const { return !!mUpdateSurface; }
|
||||
|
||||
virtual void Resize(const nsIntSize& aSize);
|
||||
virtual void Resize(const gfx::IntSize& aSize);
|
||||
|
||||
bool BindTexImage();
|
||||
|
||||
|
@ -79,7 +79,7 @@ protected:
|
|||
|
||||
already_AddRefed<TextureImage>
|
||||
CreateTextureImageEGL(GLContext *gl,
|
||||
const nsIntSize& aSize,
|
||||
const gfx::IntSize& aSize,
|
||||
TextureImage::ContentType aContentType,
|
||||
GLenum aWrapMode,
|
||||
TextureImage::Flags aFlags,
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "D3D9SurfaceImage.h"
|
||||
#include "gfxImageSurface.h"
|
||||
#include "gfx2DGlue.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
@ -73,7 +74,7 @@ D3D9SurfaceImage::SetData(const Data& aData)
|
|||
|
||||
mTexture = texture;
|
||||
mShareHandle = shareHandle;
|
||||
mSize = gfxIntSize(region.width, region.height);
|
||||
mSize = gfx::IntSize(region.width, region.height);
|
||||
mQuery = query;
|
||||
|
||||
return S_OK;
|
||||
|
@ -109,7 +110,7 @@ D3D9SurfaceImage::GetDesc() const
|
|||
return mDesc;
|
||||
}
|
||||
|
||||
gfxIntSize
|
||||
gfx::IntSize
|
||||
D3D9SurfaceImage::GetSize()
|
||||
{
|
||||
return mSize;
|
||||
|
@ -122,7 +123,7 @@ D3D9SurfaceImage::GetAsSurface()
|
|||
|
||||
HRESULT hr;
|
||||
nsRefPtr<gfxImageSurface> surface =
|
||||
new gfxImageSurface(mSize, gfxImageFormatRGB24);
|
||||
new gfxImageSurface(gfx::ThebesIntSize(mSize), gfxImageFormatRGB24);
|
||||
|
||||
if (!surface->CairoSurface() || surface->CairoStatus()) {
|
||||
NS_WARNING("Failed to created Cairo image surface for D3D9SurfaceImage.");
|
||||
|
|
|
@ -44,7 +44,7 @@ public:
|
|||
// complete.
|
||||
HANDLE GetShareHandle();
|
||||
|
||||
gfxIntSize GetSize() MOZ_OVERRIDE;
|
||||
gfx::IntSize GetSize() MOZ_OVERRIDE;
|
||||
|
||||
already_AddRefed<gfxASurface> GetAsSurface() MOZ_OVERRIDE;
|
||||
|
||||
|
@ -54,7 +54,7 @@ private:
|
|||
// is complete, whereupon the texture is safe to use.
|
||||
void EnsureSynchronized();
|
||||
|
||||
gfxIntSize mSize;
|
||||
gfx::IntSize mSize;
|
||||
RefPtr<IDirect3DTexture9> mTexture;
|
||||
RefPtr<IDirect3DQuery9> mQuery;
|
||||
HANDLE mShareHandle;
|
||||
|
|
|
@ -230,7 +230,7 @@ GrallocImage::GetAsSurface()
|
|||
}
|
||||
|
||||
nsRefPtr<gfxImageSurface> imageSurface =
|
||||
new gfxImageSurface(GetSize(), gfxImageFormatRGB16_565);
|
||||
new gfxImageSurface(gfx::ThebesIntSize(GetSize()), gfxImageFormatRGB16_565);
|
||||
|
||||
uint32_t width = GetSize().width;
|
||||
uint32_t height = GetSize().height;
|
||||
|
@ -295,9 +295,7 @@ GrallocImage::GetTextureClient()
|
|||
flags |= TEXTURE_RB_SWAPPED;
|
||||
}
|
||||
GrallocBufferActor* actor = static_cast<GrallocBufferActor*>(desc.bufferChild());
|
||||
mTextureClient = new GrallocTextureClientOGL(actor,
|
||||
gfx::ToIntSize(mSize),
|
||||
flags);
|
||||
mTextureClient = new GrallocTextureClientOGL(actor, mSize, flags);
|
||||
mTextureClient->SetGraphicBufferLocked(mGraphicBuffer);
|
||||
}
|
||||
return mTextureClient;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "ImageContainer.h"
|
||||
#include <string.h> // for memcpy, memset
|
||||
#include "SharedTextureImage.h" // for SharedTextureImage
|
||||
#include "gfx2DGlue.h"
|
||||
#include "gfxImageSurface.h" // for gfxImageSurface
|
||||
#include "gfxPlatform.h" // for gfxPlatform
|
||||
#include "gfxUtils.h" // for gfxUtils
|
||||
|
@ -16,6 +17,7 @@
|
|||
#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
|
||||
#include "mozilla/layers/ImageClient.h" // for ImageClient
|
||||
#include "nsISupportsUtils.h" // for NS_IF_ADDREF
|
||||
#include "YCbCrUtils.h" // for YCbCr conversions
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "GrallocImages.h"
|
||||
#endif
|
||||
|
@ -50,7 +52,7 @@ Atomic<int32_t> Image::sSerialCounter(0);
|
|||
already_AddRefed<Image>
|
||||
ImageFactory::CreateImage(const ImageFormat *aFormats,
|
||||
uint32_t aNumFormats,
|
||||
const gfxIntSize &,
|
||||
const gfx::IntSize &,
|
||||
BufferRecycleBin *aRecycleBin)
|
||||
{
|
||||
if (!aNumFormats) {
|
||||
|
@ -287,7 +289,7 @@ ImageContainer::LockCurrentImage()
|
|||
}
|
||||
|
||||
already_AddRefed<gfxASurface>
|
||||
ImageContainer::LockCurrentAsSurface(gfxIntSize *aSize, Image** aCurrentImage)
|
||||
ImageContainer::LockCurrentAsSurface(gfx::IntSize *aSize, Image** aCurrentImage)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
||||
|
@ -345,7 +347,7 @@ ImageContainer::UnlockCurrentImage()
|
|||
}
|
||||
|
||||
already_AddRefed<gfxASurface>
|
||||
ImageContainer::GetCurrentAsSurface(gfxIntSize *aSize)
|
||||
ImageContainer::GetCurrentAsSurface(gfx::IntSize *aSize)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
||||
|
@ -364,7 +366,7 @@ ImageContainer::GetCurrentAsSurface(gfxIntSize *aSize)
|
|||
return mActiveImage->GetAsSurface();
|
||||
}
|
||||
|
||||
gfxIntSize
|
||||
gfx::IntSize
|
||||
ImageContainer::GetCurrentSize()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
@ -556,9 +558,9 @@ PlanarYCbCrImage::GetAsSurface()
|
|||
return result.forget();
|
||||
}
|
||||
|
||||
gfxImageFormat format = GetOffscreenFormat();
|
||||
gfxIntSize size(mSize);
|
||||
gfxUtils::GetYCbCrToRGBDestFormatAndSize(mData, format, size);
|
||||
gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
|
||||
gfx::IntSize size(mSize);
|
||||
gfx::GetYCbCrToRGBDestFormatAndSize(mData, format, size);
|
||||
if (size.width > PlanarYCbCrImage::MAX_DIMENSION ||
|
||||
size.height > PlanarYCbCrImage::MAX_DIMENSION) {
|
||||
NS_ERROR("Illegal image dest width or height");
|
||||
|
@ -566,11 +568,9 @@ PlanarYCbCrImage::GetAsSurface()
|
|||
}
|
||||
|
||||
nsRefPtr<gfxImageSurface> imageSurface =
|
||||
new gfxImageSurface(mSize, format);
|
||||
new gfxImageSurface(gfx::ThebesIntSize(mSize), gfx::SurfaceFormatToImageFormat(format));
|
||||
|
||||
gfxUtils::ConvertYCbCrToRGB(mData, format, mSize,
|
||||
imageSurface->Data(),
|
||||
imageSurface->Stride());
|
||||
gfx::ConvertYCbCrToRGB(mData, format, mSize, imageSurface->Data(), imageSurface->Stride());
|
||||
|
||||
mSurface = imageSurface;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "mozilla/Mutex.h" // for Mutex
|
||||
#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitorAutoEnter, etc
|
||||
#include "mozilla/TimeStamp.h" // for TimeStamp
|
||||
#include "mozilla/gfx/Point.h" // For IntSize
|
||||
#include "mozilla/layers/LayersTypes.h" // for LayersBackend, etc
|
||||
#include "mozilla/mozalloc.h" // for operator delete, etc
|
||||
#include "nsAutoPtr.h" // for nsRefPtr, nsAutoArrayPtr, etc
|
||||
|
@ -147,7 +148,7 @@ public:
|
|||
void* GetImplData() { return mImplData; }
|
||||
|
||||
virtual already_AddRefed<gfxASurface> GetAsSurface() = 0;
|
||||
virtual gfxIntSize GetSize() = 0;
|
||||
virtual gfx::IntSize GetSize() = 0;
|
||||
virtual nsIntRect GetPictureRect()
|
||||
{
|
||||
return nsIntRect(0, 0, GetSize().width, GetSize().height);
|
||||
|
@ -262,7 +263,7 @@ protected:
|
|||
|
||||
virtual already_AddRefed<Image> CreateImage(const ImageFormat* aFormats,
|
||||
uint32_t aNumFormats,
|
||||
const gfxIntSize &aScaleHint,
|
||||
const gfx::IntSize &aScaleHint,
|
||||
BufferRecycleBin *aRecycleBin);
|
||||
|
||||
};
|
||||
|
@ -483,7 +484,7 @@ public:
|
|||
* the lock methods should be used to avoid the copy, however this should be
|
||||
* avoided if the surface is required for a long period of time.
|
||||
*/
|
||||
already_AddRefed<gfxASurface> GetCurrentAsSurface(gfxIntSize* aSizeResult);
|
||||
already_AddRefed<gfxASurface> GetCurrentAsSurface(gfx::IntSize* aSizeResult);
|
||||
|
||||
/**
|
||||
* This is similar to GetCurrentAsSurface, however this does not make a copy
|
||||
|
@ -493,7 +494,7 @@ public:
|
|||
* type of image. Optionally a pointer can be passed to receive the current
|
||||
* image.
|
||||
*/
|
||||
already_AddRefed<gfxASurface> LockCurrentAsSurface(gfxIntSize* aSizeResult,
|
||||
already_AddRefed<gfxASurface> LockCurrentAsSurface(gfx::IntSize* aSizeResult,
|
||||
Image** aCurrentImage = nullptr);
|
||||
|
||||
/**
|
||||
|
@ -501,7 +502,7 @@ public:
|
|||
* Can be called on any thread. This method takes mReentrantMonitor when accessing
|
||||
* thread-shared state.
|
||||
*/
|
||||
gfxIntSize GetCurrentSize();
|
||||
gfx::IntSize GetCurrentSize();
|
||||
|
||||
/**
|
||||
* Sets a size that the image is expected to be rendered at.
|
||||
|
@ -673,7 +674,7 @@ public:
|
|||
~AutoLockImage() { if (mContainer) { mContainer->UnlockCurrentImage(); } }
|
||||
|
||||
Image* GetImage() { return mImage; }
|
||||
const gfxIntSize &GetSize() { return mSize; }
|
||||
const gfx::IntSize &GetSize() { return mSize; }
|
||||
|
||||
void Unlock() {
|
||||
if (mContainer) {
|
||||
|
@ -698,7 +699,7 @@ public:
|
|||
private:
|
||||
ImageContainer *mContainer;
|
||||
nsRefPtr<Image> mImage;
|
||||
gfxIntSize mSize;
|
||||
gfx::IntSize mSize;
|
||||
};
|
||||
|
||||
struct PlanarYCbCrData {
|
||||
|
@ -819,7 +820,7 @@ public:
|
|||
|
||||
virtual bool IsValid() { return !!mBufferSize; }
|
||||
|
||||
virtual gfxIntSize GetSize() { return mSize; }
|
||||
virtual gfx::IntSize GetSize() { return mSize; }
|
||||
|
||||
PlanarYCbCrImage(BufferRecycleBin *aRecycleBin);
|
||||
|
||||
|
@ -849,7 +850,7 @@ protected:
|
|||
nsAutoArrayPtr<uint8_t> mBuffer;
|
||||
uint32_t mBufferSize;
|
||||
Data mData;
|
||||
gfxIntSize mSize;
|
||||
gfx::IntSize mSize;
|
||||
gfxImageFormat mOffscreenFormat;
|
||||
nsCountedRef<nsMainThreadSurfaceRef> mSurface;
|
||||
nsRefPtr<BufferRecycleBin> mRecycleBin;
|
||||
|
@ -885,7 +886,7 @@ public:
|
|||
return surface.forget();
|
||||
}
|
||||
|
||||
gfxIntSize GetSize() { return mSize; }
|
||||
gfx::IntSize GetSize() { return mSize; }
|
||||
|
||||
CairoImage() : Image(nullptr, CAIRO_SURFACE) {}
|
||||
|
||||
|
@ -899,7 +900,7 @@ public:
|
|||
|
||||
already_AddRefed<gfxASurface> GetAsSurface();
|
||||
|
||||
gfxIntSize GetSize() { return mSize; }
|
||||
gfx::IntSize GetSize() { return mSize; }
|
||||
|
||||
unsigned char *mData;
|
||||
int mStride;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "gfxRect.h" // for gfxRect
|
||||
#include "nsDebug.h" // for NS_ASSERTION
|
||||
#include "nsISupportsImpl.h" // for ImageContainer::Release, etc
|
||||
#include "gfx2DGlue.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
@ -33,7 +34,7 @@ void ImageLayer::ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurfa
|
|||
// Snap image edges to pixel boundaries
|
||||
gfxRect sourceRect(0, 0, 0, 0);
|
||||
if (mContainer) {
|
||||
sourceRect.SizeTo(mContainer->GetCurrentSize());
|
||||
sourceRect.SizeTo(gfx::ThebesIntSize(mContainer->GetCurrentSize()));
|
||||
if (mScaleMode != SCALE_NONE &&
|
||||
sourceRect.width != 0.0 && sourceRect.height != 0.0) {
|
||||
NS_ASSERTION(mScaleMode == SCALE_STRETCH,
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "ReadbackLayer.h" // for ReadbackLayer
|
||||
#include "gfxPlatform.h" // for gfxPlatform
|
||||
#include "gfxUtils.h" // for gfxUtils, etc
|
||||
#include "gfx2DGlue.h"
|
||||
#include "mozilla/DebugOnly.h" // for DebugOnly
|
||||
#include "mozilla/Telemetry.h" // for Accumulate
|
||||
#include "mozilla/TelemetryHistogramEnums.h"
|
||||
|
@ -509,7 +510,7 @@ Layer::SnapTransform(const gfx3DMatrix& aTransform,
|
|||
gfx3DMatrix result;
|
||||
if (mManager->IsSnappingEffectiveTransforms() &&
|
||||
aTransform.Is2D(&matrix2D) &&
|
||||
gfxSize(1.0, 1.0) <= aSnapRect.Size() &&
|
||||
gfx::Size(1.0, 1.0) <= ToSize(aSnapRect.Size()) &&
|
||||
matrix2D.PreservesAxisAlignedRectangles()) {
|
||||
gfxPoint transformedTopLeft = matrix2D.Transform(aSnapRect.TopLeft());
|
||||
transformedTopLeft.Round();
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "ImageContainer.h"
|
||||
#include "mozilla/gfx/MacIOSurface.h"
|
||||
#include "mozilla/gfx/Point.h"
|
||||
#include "mozilla/layers/TextureClient.h"
|
||||
#include "gfxImageSurface.h"
|
||||
|
||||
|
@ -21,7 +22,7 @@ public:
|
|||
void SetSurface(MacIOSurface* aSurface) { mSurface = aSurface; }
|
||||
MacIOSurface* GetSurface() { return mSurface; }
|
||||
|
||||
gfxIntSize GetSize() {
|
||||
gfx::IntSize GetSize() {
|
||||
return gfxIntSize(mSurface->GetDevicePixelWidth(), mSurface->GetDevicePixelHeight());
|
||||
}
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче