Merge mozilla-central to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2014-05-28 14:57:27 +02:00
commit c95e73435f
260 changed files with 10162 additions and 7254 deletions

View File

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 994964 apparently requires a clobber, unclear why
Bug 999651 et al. apparently require a clobber, unclear why

View File

@ -429,6 +429,9 @@ HyperTextAccessible::FindOffset(uint32_t aOffset, nsDirection aDirection,
nsSelectionAmount aAmount,
EWordMovementType aWordMovementType)
{
NS_ASSERTION(aDirection == eDirPrevious || aAmount != eSelectBeginLine,
"eSelectBeginLine should only be used with eDirPrevious");
// Find a leaf accessible frame to start with. PeekOffset wants this.
HyperTextAccessible* text = this;
Accessible* child = nullptr;
@ -447,22 +450,32 @@ HyperTextAccessible::FindOffset(uint32_t aOffset, nsDirection aDirection,
if (text->IsHTMLListItem()) {
HTMLLIAccessible* li = text->AsHTMLListItem();
if (child == li->Bullet()) {
// It works only when the bullet is one single char.
if (aDirection == eDirPrevious)
return text != this ? TransformOffset(text, 0, false) : 0;
if (aAmount == eSelectEndLine || aAmount == eSelectLine) {
if (text != this)
return TransformOffset(text, 1, true);
// Ask a text leaf next (if not empty) to the bullet for an offset
// since list item may be multiline.
return aOffset + 1 < CharacterCount() ?
FindOffset(aOffset + 1, aDirection, aAmount, aWordMovementType) : 1;
// XXX: the logic is broken for multichar bullets in moving by
// char/cluster/word cases.
if (text != this) {
return aDirection == eDirPrevious ?
TransformOffset(text, 0, false) :
TransformOffset(text, 1, true);
}
if (aDirection == eDirPrevious)
return 0;
// Case of word and char boundaries.
return text != this ? TransformOffset(text, 1, true) : 1;
uint32_t nextOffset = GetChildOffset(1);
if (nextOffset == 0)
return 0;
switch (aAmount) {
case eSelectLine:
case eSelectEndLine:
// Ask a text leaf next (if not empty) to the bullet for an offset
// since list item may be multiline.
return nextOffset < CharacterCount() ?
FindOffset(nextOffset, aDirection, aAmount, aWordMovementType) :
nextOffset;
default:
return nextOffset;
}
}
}
@ -521,8 +534,12 @@ HyperTextAccessible::FindOffset(uint32_t aOffset, nsDirection aDirection,
return 0;
// PeekOffset stops right before bullet so return 0 to workaround it.
if (IsHTMLListItem() && aAmount == eSelectBeginLine && hyperTextOffset == 1)
return 0;
if (IsHTMLListItem() && aAmount == eSelectBeginLine &&
hyperTextOffset > 0) {
Accessible* prevOffsetChild = GetChildAtOffset(hyperTextOffset - 1);
if (prevOffsetChild == AsHTMLListItem()->Bullet())
return 0;
}
}
return hyperTextOffset;

View File

@ -126,11 +126,34 @@
[ 8, 11, "and ", 8, 12 ] ]);
testTextAtOffset([ "li4" ], BOUNDARY_LINE_START,
[ [ 0, 6, kDiscBulletChar + "a " + kEmbedChar + " c", 0, 6 ] ]);
testTextAtOffset([ "li5" ], BOUNDARY_LINE_START,
[ [ 0, 1, kDiscBulletChar + "\n", 0, 2 ],
[ 2, 6, "hello", 2, 7 ] ]);
testTextAtOffset([ "ul1" ], BOUNDARY_LINE_START,
[ [ 0, 0, kEmbedChar, 0, 1 ],
[ 1, 1, kEmbedChar, 1, 2 ],
[ 2, 2, kEmbedChar, 2, 3 ],
[ 3, 4, kEmbedChar, 3, 4 ] ]);
[ 3, 3, kEmbedChar, 3, 4 ],
[ 4, 5, kEmbedChar, 4, 5 ] ]);
testTextAtOffset([ "li6" ], BOUNDARY_LINE_START,
[ [ 0, 6, "1.Item", 0, 6 ] ]);
testTextAtOffset([ "li7" ], BOUNDARY_LINE_START,
[ [ 0, 2, "2.", 0, 2 ] ]);
testTextAtOffset([ "li8" ], BOUNDARY_LINE_START,
[ [ 0, 8, "3.a long ", 0, 9 ],
[ 9, 12, "and ", 9, 13 ] ]);
testTextAtOffset([ "li9" ], BOUNDARY_LINE_START,
[ [ 0, 7, "4.a " + kEmbedChar + " c", 0, 7 ] ]);
testTextAtOffset([ "li10" ], BOUNDARY_LINE_START,
[ [ 0, 2, "5.\n", 0, 3 ],
[ 3, 7, "hello", 3, 8 ] ]);
testTextAtOffset([ "ol1" ], BOUNDARY_LINE_START,
[ [ 0, 0, kEmbedChar, 0, 1 ],
[ 1, 1, kEmbedChar, 1, 2 ],
[ 2, 2, kEmbedChar, 2, 3 ],
[ 3, 3, kEmbedChar, 3, 4 ],
[ 4, 5, kEmbedChar, 4, 5 ] ]);
//////////////////////////////////////////////////////////////////////////
// Nested hypertexts
@ -210,8 +233,17 @@ two words
<li id="li2"></li>
<li id="li3" style="width:10ex; font-family:monospace; font-size:10pt;">a long and winding road that lead me to your door</li>
<li id="li4">a <a href=''>b</a> c</li>
<li id="li5"><br>hello</li>
</ul>
<ol id="ol1">
<li id="li6">Item</li>
<li id="li7"></li>
<li id="li8" style="width:10ex; font-family:monospace; font-size:10pt;">a long and winding road that lead me to your door</li>
<li id="li9">a <a href=''>b</a> c</li>
<li id="li10"><br>hello</li>
</ol>
<div id="ht_5">
<div>
<p>sectiounus</p>

View File

@ -1464,7 +1464,7 @@ pref("browser.newtabpage.rows", 3);
// number of columns of newtab grid
pref("browser.newtabpage.columns", 3);
pref("browser.newtabpage.directorySource", "chrome://global/content/directoryLinks.json");
pref("browser.newtabpage.directory.source", "chrome://global/content/directoryLinks.json");
// Enable the DOM fullscreen API.
pref("full-screen-api.enabled", true);

View File

@ -784,7 +784,8 @@ var gBrowserInit = {
gPageStyleMenu.init();
LanguageDetectionListener.init();
messageManager.loadFrameScript("chrome://browser/content/content.js", true);
let mm = window.getGroupMessageManager("browsers");
mm.loadFrameScript("chrome://browser/content/content.js", true);
// initialize observers and listeners
// and give C++ access to gBrowser

View File

@ -217,6 +217,7 @@ let gPage = {
}
}
DirectoryLinksProvider.reportShownCount(directoryCount);
// Record how many directory sites were shown, but place counts over the
// default 9 in the same bucket
for (let type of Object.keys(directoryCount)) {

View File

@ -34,7 +34,7 @@
<xul:hbox flex="1" class="browserSidebarContainer">
<xul:vbox flex="1" class="browserContainer">
<xul:stack flex="1" class="browserStack" anonid="browserStack">
<xul:browser anonid="initialBrowser" type="content-primary" message="true"
<xul:browser anonid="initialBrowser" type="content-primary" message="true" messagemanagergroup="browsers"
xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectpopup"/>
</xul:stack>
</xul:vbox>
@ -1529,6 +1529,7 @@
"browser");
b.setAttribute("type", "content-targetable");
b.setAttribute("message", "true");
b.setAttribute("messagemanagergroup", "browsers");
b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
@ -3336,11 +3337,8 @@
<handler event="overflow"><![CDATA[
if (event.detail == 0)
return; // Ignore vertical events
var tabs = document.getBindingParent(this);
var numberOfTabs = tabs.tabbrowser.visibleTabs.length;
if (numberOfTabs == 1)
return;
var tabs = document.getBindingParent(this);
tabs.setAttribute("overflow", "true");
tabs._positionPinnedTabs();
tabs._handleTabSelect(false);
@ -3860,14 +3858,12 @@
TabsInTitlebar.updateAppearance();
if (this.tabbrowser.visibleTabs.length > 1) {
var width = this.mTabstrip.boxObject.width;
if (width != this.mTabstripWidth) {
this.adjustTabstrip();
this._fillTrailingGap();
this._handleTabSelect();
this.mTabstripWidth = width;
}
var width = this.mTabstrip.boxObject.width;
if (width != this.mTabstripWidth) {
this.adjustTabstrip();
this._fillTrailingGap();
this._handleTabSelect();
this.mTabstripWidth = width;
}
this.tabbrowser.updateWindowResizers();

View File

@ -589,8 +589,7 @@ let gTests = [
expectObserverCalled("recording-window-ended");
}
else {
let allow = (aAllowVideo && aRequestVideo) || (aAllowAudio && aRequestAudio);
let expectedMessage = allow ? "ok" : "error: PERMISSION_DENIED";
let expectedMessage = aExpectStream ? "ok" : "error: PERMISSION_DENIED";
yield promiseMessage(expectedMessage, gum);
if (expectedMessage == "ok") {
@ -635,9 +634,9 @@ let gTests = [
info("deny audio, allow video, request audio+video, expect ok (video)");
yield usePerm(false, true, true, true, true);
info("deny audio, allow video, request audio, expect denied");
yield usePerm(false, true, true, false, true);
yield usePerm(false, true, true, false, false);
info("deny audio, allow video, request video, expect ok (video)");
yield usePerm(false, true, false, true, false);
yield usePerm(false, true, false, true, true);
// Allow audio, video not set.
info("allow audio, request audio+video, expect prompt");

View File

@ -56,7 +56,7 @@ const EXPECTED_REFLOWS = [
];
const PREF_PRELOAD = "browser.newtab.preload";
const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directorySource";
const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directory.source";
/*
* This test ensures that there are no unexpected
@ -64,26 +64,47 @@ const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directorySource";
*/
function test() {
waitForExplicitFinish();
let DirectoryLinksProvider = Cu.import("resource://gre/modules/DirectoryLinksProvider.jsm", {}).DirectoryLinksProvider;
let Promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
// resolves promise when directory links are downloaded and written to disk
function watchLinksChangeOnce() {
let deferred = Promise.defer();
let observer = {
onManyLinksChanged: () => {
DirectoryLinksProvider.removeObserver(observer);
deferred.resolve();
}
};
observer.onDownloadFail = observer.onManyLinksChanged;
DirectoryLinksProvider.addObserver(observer);
return deferred.promise;
};
Services.prefs.setBoolPref(PREF_PRELOAD, false);
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, "data:application/json,{}");
registerCleanupFunction(() => {
Services.prefs.clearUserPref(PREF_PRELOAD);
Services.prefs.clearUserPref(PREF_NEWTAB_DIRECTORYSOURCE);
return watchLinksChangeOnce();
});
// Add a reflow observer and open a new tab.
docShell.addWeakReflowObserver(observer);
BrowserOpenTab();
// run tests when directory source change completes
watchLinksChangeOnce().then(() => {
// Add a reflow observer and open a new tab.
docShell.addWeakReflowObserver(observer);
BrowserOpenTab();
// Wait until the tabopen animation has finished.
waitForTransitionEnd(function () {
// Remove reflow observer and clean up.
docShell.removeWeakReflowObserver(observer);
gBrowser.removeCurrentTab();
finish();
// Wait until the tabopen animation has finished.
waitForTransitionEnd(function () {
// Remove reflow observer and clean up.
docShell.removeWeakReflowObserver(observer);
gBrowser.removeCurrentTab();
finish();
});
});
Services.prefs.setBoolPref(PREF_PRELOAD, false);
// set directory source to empty links
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, "data:application/json,{}");
}
let observer = {

View File

@ -2,20 +2,19 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
const PREF_NEWTAB_ENABLED = "browser.newtabpage.enabled";
const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directorySource";
const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directory.source";
Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, true);
// start with no directory links by default
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, "data:application/json,{}");
let tmp = {};
Cu.import("resource://gre/modules/Promise.jsm", tmp);
Cu.import("resource://gre/modules/NewTabUtils.jsm", tmp);
Cu.import("resource://gre/modules/DirectoryLinksProvider.jsm", tmp);
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript("chrome://browser/content/sanitize.js", tmp);
Cu.import("resource://gre/modules/Timer.jsm", tmp);
let {Promise, NewTabUtils, Sanitizer, clearTimeout} = tmp;
let {Promise, NewTabUtils, Sanitizer, clearTimeout, DirectoryLinksProvider} = tmp;
let uri = Services.io.newURI("about:newtab", null, null);
let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
@ -60,22 +59,45 @@ registerCleanupFunction(function () {
if (oldInnerHeight)
gBrowser.contentWindow.innerHeight = oldInnerHeight;
Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED);
Services.prefs.clearUserPref(PREF_NEWTAB_DIRECTORYSOURCE);
// Stop any update timers to prevent unexpected updates in later tests
let timer = NewTabUtils.allPages._scheduleUpdateTimeout;
if (timer) {
clearTimeout(timer);
delete NewTabUtils.allPages._scheduleUpdateTimeout;
}
Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED);
Services.prefs.clearUserPref(PREF_NEWTAB_DIRECTORYSOURCE);
return watchLinksChangeOnce();
});
/**
* Resolves promise when directory links are downloaded and written to disk
*/
function watchLinksChangeOnce() {
let deferred = Promise.defer();
let observer = {
onManyLinksChanged: () => {
DirectoryLinksProvider.removeObserver(observer);
deferred.resolve();
}
};
observer.onDownloadFail = observer.onManyLinksChanged;
DirectoryLinksProvider.addObserver(observer);
return deferred.promise;
};
/**
* Provide the default test function to start our test runner.
*/
function test() {
TestRunner.run();
waitForExplicitFinish();
// start TestRunner.run() after directory links is downloaded and written to disk
watchLinksChangeOnce().then(() => {
TestRunner.run();
});
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, "data:application/json,{}");
}
/**
@ -86,8 +108,6 @@ let TestRunner = {
* Starts the test runner.
*/
run: function () {
waitForExplicitFinish();
this._iter = runTests();
this.next();
},

View File

@ -18,6 +18,7 @@ PARALLEL_DIRS += [
'shell',
'sidebar',
'tabview',
'translation',
'migration',
]

View File

@ -63,7 +63,7 @@ function test() {
is(gBrowser.tabs.length, preTabNo, "Return key did not open new tab");
is(event.originalTarget, preSelectedBrowser.contentDocument,
"Return key loaded results in current tab");
is(event.originalTarget.URL, expectedURL(searchBar.value), "Check URL of search page opened");
is(event.originalTarget.URL, expectedURL(searchBar.value), "testReturn opened correct search page");
testAltReturn();
});
@ -79,7 +79,7 @@ function test() {
"Alt+Return key loaded results in new tab");
is(event.originalTarget, gBrowser.contentDocument,
"Alt+Return key loaded results in foreground tab");
is(event.originalTarget.URL, expectedURL(searchBar.value), "Check URL of search page opened");
is(event.originalTarget.URL, expectedURL(searchBar.value), "testAltReturn opened correct search page");
//Shift key has no effect for now, so skip it
//testShiftAltReturn();
@ -97,7 +97,7 @@ function test() {
"Shift+Alt+Return key loaded results in new tab");
isnot(event.originalTarget, gBrowser.contentDocument,
"Shift+Alt+Return key loaded results in background tab");
is(event.originalTarget.URL, expectedURL(searchBar.value), "Check URL of search page opened");
is(event.originalTarget.URL, expectedURL(searchBar.value), "testShiftAltReturn opened correct search page");
testLeftClick();
});
@ -111,7 +111,7 @@ function test() {
is(gBrowser.tabs.length, preTabNo, "LeftClick did not open new tab");
is(event.originalTarget, preSelectedBrowser.contentDocument,
"LeftClick loaded results in current tab");
is(event.originalTarget.URL, expectedURL(searchBar.value), "Check URL of search page opened");
is(event.originalTarget.URL, expectedURL(searchBar.value), "testLeftClick opened correct search page");
testMiddleClick();
});
@ -127,7 +127,7 @@ function test() {
"MiddleClick loaded results in new tab");
is(event.originalTarget, gBrowser.contentDocument,
"MiddleClick loaded results in foreground tab");
is(event.originalTarget.URL, expectedURL(searchBar.value), "Check URL of search page opened");
is(event.originalTarget.URL, expectedURL(searchBar.value), "testMiddleClick opened correct search page");
testShiftMiddleClick();
});
@ -143,7 +143,7 @@ function test() {
"Shift+MiddleClick loaded results in new tab");
isnot(event.originalTarget, gBrowser.contentDocument,
"Shift+MiddleClick loaded results in background tab");
is(event.originalTarget.URL, expectedURL(searchBar.value), "Check URL of search page opened");
is(event.originalTarget.URL, expectedURL(searchBar.value), "testShiftMiddleClick opened correct search page");
testDropText();
});

View File

@ -764,7 +764,7 @@ let SessionStoreInternal = {
// internal data about the window.
aWindow.__SSi = this._generateWindowID();
let mm = aWindow.messageManager;
let mm = aWindow.getGroupMessageManager("browsers");
MESSAGES.forEach(msg => mm.addMessageListener(msg, this));
// Load the frame script after registering listeners.
@ -1093,7 +1093,7 @@ let SessionStoreInternal = {
// Cache the window state until it is completely gone.
DyingWindowCache.set(aWindow, winData);
let mm = aWindow.messageManager;
let mm = aWindow.getGroupMessageManager("browsers");
MESSAGES.forEach(msg => mm.removeMessageListener(msg, this));
delete aWindow.__SSi;

View File

@ -76,7 +76,8 @@ add_task(function () {
// Create a new window to attach our frame script to.
let win = yield promiseNewWindowLoaded();
win.messageManager.loadFrameScript(FRAME_SCRIPT, true);
let mm = win.getGroupMessageManager("browsers");
mm.loadFrameScript(FRAME_SCRIPT, true);
// Create a new tab in the new window that will load the frame script.
let tab = win.gBrowser.addTab("about:mozilla");

View File

@ -174,7 +174,7 @@ let UI = {
});
// ___ setup DOMWillOpenModalDialog message handler
let mm = gWindow.messageManager;
let mm = gWindow.getGroupMessageManager("browsers");
let callback = this._onDOMWillOpenModalDialog.bind(this);
mm.addMessageListener("Panorama:DOMWillOpenModalDialog", callback);
@ -235,7 +235,7 @@ let UI = {
// ___ load frame script
let frameScript = "chrome://browser/content/tabview-content.js";
gWindow.messageManager.loadFrameScript(frameScript, true);
mm.loadFrameScript(frameScript, true);
// ___ Done
this._frameInitialized = true;

View File

@ -7,14 +7,17 @@
Components.utils.import("resource:///modules/translation/Translation.jsm");
const kLanguagesPref = "browser.translation.neverForLanguages";
const kShowUIPref = "browser.translation.ui.show";
function test() {
waitForExplicitFinish();
Services.prefs.setBoolPref(kShowUIPref, true);
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
registerCleanupFunction(function () {
gBrowser.removeTab(tab);
Services.prefs.clearUserPref(kShowUIPref);
});
tab.linkedBrowser.addEventListener("load", function onload() {
tab.linkedBrowser.removeEventListener("load", onload, true);

View File

@ -6,6 +6,8 @@
Components.utils.import("resource:///modules/translation/Translation.jsm");
const kShowUIPref = "browser.translation.ui.show";
function waitForCondition(condition, nextTest, errorMsg) {
var tries = 0;
var interval = setInterval(function() {
@ -68,6 +70,7 @@ function hasTranslationInfoBar() {
function test() {
waitForExplicitFinish();
Services.prefs.setBoolPref(kShowUIPref, true);
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
tab.linkedBrowser.addEventListener("load", function onload() {
@ -75,6 +78,7 @@ function test() {
TranslationStub.browser = gBrowser.selectedBrowser;
registerCleanupFunction(function () {
gBrowser.removeTab(tab);
Services.prefs.clearUserPref(kShowUIPref);
});
run_tests(() => {
finish();

View File

@ -73,15 +73,17 @@ WebappsStore.prototype = {
_listTabs: function() {
this._connection.client.listTabs((resp) => {
this._webAppsActor = resp.webappsActor;
this._feedStore();
this._feedStore().then(() => {
this.emit("store-ready");
});
});
},
_feedStore: function(deviceFront, webAppsActor) {
_feedStore: function() {
this._listenToApps();
this._getAllApps()
.then(this._getRunningApps.bind(this))
.then(this._getAppsIcons.bind(this))
return this._getAllApps()
.then(this._getRunningApps.bind(this))
.then(this._getAppsIcons.bind(this))
},
_listenToApps: function() {

View File

@ -37,7 +37,6 @@ const SPECTRUM_FRAME = "chrome://browser/content/devtools/spectrum-frame.xhtml";
const ESCAPE_KEYCODE = Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE;
const RETURN_KEYCODE = Ci.nsIDOMKeyEvent.DOM_VK_RETURN;
const POPUP_EVENTS = ["shown", "hidden", "showing", "hiding"];
const FONT_FAMILY_PREVIEW_TEXT = "(ABCabc123&@%)";
/**
* Tooltip widget.
@ -636,7 +635,9 @@ Tooltip.prototype = {
* it was resized, if if was resized before this function was called.
* If not provided, will be measured on the loaded image.
* - maxDim : if the image should be resized before being shown, pass
* a number here
* a number here.
* - hideDimensionLabel : if the dimension label should be appended
* after the image.
*/
setImageContent: function(imageUrl, options={}) {
if (!imageUrl) {
@ -656,25 +657,28 @@ Tooltip.prototype = {
}
vbox.appendChild(image);
// Dimension label
let label = this.doc.createElement("label");
label.classList.add("devtools-tooltip-caption");
label.classList.add("theme-comment");
if (options.naturalWidth && options.naturalHeight) {
label.textContent = this._getImageDimensionLabel(options.naturalWidth,
options.naturalHeight);
} else {
// If no dimensions were provided, load the image to get them
label.textContent = l10n.strings.GetStringFromName("previewTooltip.image.brokenImage");
let imgObj = new this.doc.defaultView.Image();
imgObj.src = imageUrl;
imgObj.onload = () => {
imgObj.onload = null;
label.textContent = this._getImageDimensionLabel(imgObj.naturalWidth,
imgObj.naturalHeight);
if (!options.hideDimensionLabel) {
let label = this.doc.createElement("label");
label.classList.add("devtools-tooltip-caption");
label.classList.add("theme-comment");
if (options.naturalWidth && options.naturalHeight) {
label.textContent = this._getImageDimensionLabel(options.naturalWidth,
options.naturalHeight);
} else {
// If no dimensions were provided, load the image to get them
label.textContent = l10n.strings.GetStringFromName("previewTooltip.image.brokenImage");
let imgObj = new this.doc.defaultView.Image();
imgObj.src = imageUrl;
imgObj.onload = () => {
imgObj.onload = null;
label.textContent = this._getImageDimensionLabel(imgObj.naturalWidth,
imgObj.naturalHeight);
}
}
vbox.appendChild(label);
}
vbox.appendChild(label);
this.content = vbox;
},
@ -777,26 +781,30 @@ Tooltip.prototype = {
* This is based on Lea Verou's Dablet. See https://github.com/LeaVerou/dabblet
* for more info.
* @param {String} font The font family value.
* @param {object} nodeFront
* The NodeActor that will used to retrieve the dataURL for the font
* family tooltip contents.
* @return A promise that resolves when the font tooltip content is ready, or
* rejects if no font is provided
*/
setFontFamilyContent: function(font) {
if (!font) {
return;
setFontFamilyContent: Task.async(function*(font, nodeFront) {
if (!font || !nodeFront) {
throw "Missing font";
}
// Main container
let vbox = this.doc.createElement("vbox");
vbox.setAttribute("flex", "1");
if (typeof nodeFront.getFontFamilyDataURL === "function") {
font = font.replace(/"/g, "'");
font = font.replace("!important", "");
font = font.trim();
// Display the font family previewer
let previewer = this.doc.createElement("description");
previewer.setAttribute("flex", "1");
previewer.style.fontFamily = font;
previewer.classList.add("devtools-tooltip-font-previewer-text");
previewer.textContent = FONT_FAMILY_PREVIEW_TEXT;
vbox.appendChild(previewer);
let fillStyle = (Services.prefs.getCharPref("devtools.theme") === "light") ?
"black" : "white";
this.content = vbox;
}
let {data, size} = yield nodeFront.getFontFamilyDataURL(font, fillStyle);
let str = yield data.string();
this.setImageContent(str, { hideDimensionLabel: true, maxDim: size });
}
})
};
/**

View File

@ -545,8 +545,12 @@ CssHtmlTree.prototype = {
// Test for font family
if (propName.textContent === "font-family") {
this.tooltip.setFontFamilyContent(propValue.textContent);
return true;
let prop = propValue.textContent.toLowerCase();
if (prop !== "inherit" && prop !== "unset" && prop !== "initial") {
return this.tooltip.setFontFamilyContent(propValue.textContent,
inspector.selection.nodeFront);
}
}
}

View File

@ -1212,8 +1212,12 @@ CssRuleView.prototype = {
return this.previewTooltip.setRelativeImageContent(uri, this.inspector.inspector, dim);
}
if (tooltipType === "font") {
this.previewTooltip.setFontFamilyContent(target.textContent);
return true;
let prop = target.textContent.toLowerCase();
if (prop !== "inherit" && prop !== "unset" && prop !== "initial") {
return this.previewTooltip.setFontFamilyContent(target.textContent,
this.inspector.selection.nodeFront);
}
}
return false;

View File

@ -29,15 +29,15 @@ let test = asyncTest(function*() {
info("Selecting the test node");
yield selectNode("#testElement", inspector);
yield testRuleView(view);
yield testRuleView(view, inspector.selection.nodeFront);
info("Opening the computed view");
let {toolbox, inspector, view} = yield openComputedView();
yield testComputedView(view);
yield testComputedView(view, inspector.selection.nodeFront);
});
function* testRuleView(ruleView) {
function* testRuleView(ruleView, nodeFront) {
info("Testing font-family tooltips in the rule view");
let panel = ruleView.previewTooltip.panel;
@ -52,11 +52,15 @@ function* testRuleView(ruleView) {
// And verify that the tooltip gets shown on this property
yield assertHoverTooltipOn(ruleView.previewTooltip, valueSpan);
let description = panel.getElementsByTagName("description")[0];
is(description.style.fontFamily, "cursive", "Tooltips contains correct font-family style");
let images = panel.getElementsByTagName("image");
is(images.length, 1, "Tooltip contains an image");
ok(images[0].getAttribute("src").startsWith("data:"), "Tooltip contains a data-uri image as expected");
let dataURL = yield getFontFamilyDataURL(valueSpan.textContent, nodeFront);
is(images[0].getAttribute("src"), dataURL, "Tooltip contains the correct data-uri image");
}
function* testComputedView(computedView) {
function* testComputedView(computedView, nodeFront) {
info("Testing font-family tooltips in the computed view");
let panel = computedView.tooltip.panel;
@ -64,6 +68,10 @@ function* testComputedView(computedView) {
yield assertHoverTooltipOn(computedView.tooltip, valueSpan);
let description = panel.getElementsByTagName("description")[0];
is(description.style.fontFamily, "cursive", "Tooltips contains correct font-family style");
let images = panel.getElementsByTagName("image");
is(images.length, 1, "Tooltip contains an image");
ok(images[0].getAttribute("src").startsWith("data:"), "Tooltip contains a data-uri image as expected");
let dataURL = yield getFontFamilyDataURL(valueSpan.textContent, nodeFront);
is(images[0].getAttribute("src"), dataURL, "Tooltip contains the correct data-uri image");
}

View File

@ -27,15 +27,15 @@ let test = asyncTest(function*() {
info("Selecting the test node");
yield selectNode("#testElement", inspector);
yield testRuleView(view);
yield testRuleView(view, inspector.selection.nodeFront);
info("Opening the computed view");
let {toolbox, inspector, view} = yield openComputedView();
yield testComputedView(view);
yield testComputedView(view, inspector.selection.nodeFront);
});
function* testRuleView(ruleView) {
function* testRuleView(ruleView, nodeFront) {
info("Testing font-family tooltips in the rule view");
let panel = ruleView.previewTooltip.panel;
@ -55,11 +55,15 @@ function* testRuleView(ruleView) {
// And verify that the tooltip gets shown on this property
yield assertHoverTooltipOn(ruleView.previewTooltip, valueSpan);
let description = panel.getElementsByTagName("description")[0];
is(description.style.fontFamily, "Arial", "Tooltips contains correct font-family style");
let images = panel.getElementsByTagName("image");
is(images.length, 1, "Tooltip contains an image");
ok(images[0].getAttribute("src").startsWith("data:"), "Tooltip contains a data-uri image as expected");
let dataURL = yield getFontFamilyDataURL(valueSpan.textContent, nodeFront);
is(images[0].getAttribute("src"), dataURL, "Tooltip contains the correct data-uri image");
}
function* testComputedView(computedView) {
function* testComputedView(computedView, nodeFront) {
info("Testing font-family tooltips in the computed view");
let panel = computedView.tooltip.panel;
@ -67,6 +71,10 @@ function* testComputedView(computedView) {
yield assertHoverTooltipOn(computedView.tooltip, valueSpan);
let description = panel.getElementsByTagName("description")[0];
is(description.style.fontFamily, "Arial", "Tooltips contains correct font-family style");
let images = panel.getElementsByTagName("image");
is(images.length, 1, "Tooltip contains an image");
ok(images[0].getAttribute("src").startsWith("data:"), "Tooltip contains a data-uri image as expected");
let dataURL = yield getFontFamilyDataURL(valueSpan.textContent, nodeFront);
is(images[0].getAttribute("src"), dataURL, "Tooltip contains the correct data-uri image");
}

View File

@ -468,6 +468,22 @@ function hasSideBarTab(inspector, id) {
return !!inspector.sidebar.getWindowForTab(id);
}
/**
* Get the dataURL for the font family tooltip.
* @param {String} font The font family value.
* @param {object} nodeFront
* The NodeActor that will used to retrieve the dataURL for the
* font family tooltip contents.
*/
let getFontFamilyDataURL = Task.async(function*(font, nodeFront) {
let fillStyle = (Services.prefs.getCharPref("devtools.theme") === "light") ?
"black" : "white";
let {data} = yield nodeFront.getFontFamilyDataURL(font, fillStyle);
let dataURL = yield data.string();
return dataURL;
});
/* *********************************************
* RULE-VIEW
* *********************************************

Binary file not shown.

View File

@ -5,11 +5,14 @@ support-files =
doc_simple-context.html
doc_complex-context.html
doc_simple-node-creation.html
doc_buffer-and-array.html
440hz_sine.ogg
head.js
[browser_audionode-actor-get-set-param.js]
[browser_audionode-actor-get-type.js]
[browser_audionode-actor-get-params.js]
[browser_audionode-actor-get-params-01.js]
[browser_audionode-actor-get-params-02.js]
[browser_audionode-actor-get-param-flags.js]
[browser_audionode-actor-is-source.js]
[browser_webaudio-actor-simple.js]
@ -20,9 +23,13 @@ support-files =
[browser_wa_graph-render-01.js]
[browser_wa_graph-render-02.js]
[browser_wa_graph-markers.js]
[browser_wa_properties-view.js]
[browser_wa_properties-view-edit.js]
[browser_wa_graph-selected.js]
[browser_wa_inspector.js]
[browser_wa_inspector-toggle.js]
[browser_wa_properties-view.js]
# [browser_wa_properties-view-edit.js]
# Disabled for too many intermittents bug 1010423
[browser_wa_properties-view-params.js]
[browser_wa_properties-view-params-objects.js]

View File

@ -23,12 +23,7 @@ function spawnTest () {
nodeTypes.forEach((type, i) => {
let params = allNodeParams[i];
params.forEach(({param, value, flags}) => {
ok(~NODE_PROPERTIES[type].indexOf(param), "expected parameter for " + type);
// Skip over some properties that are undefined by default
if (!/buffer|loop|smoothing|curve|cone/.test(param)) {
ok(value != undefined, param + " is not undefined");
}
ok(param in NODE_DEFAULT_VALUES[type], "expected parameter for " + type);
ok(typeof flags === "object", type + " has a flags object");

View File

@ -0,0 +1,45 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that default properties are returned with the correct type
* from the AudioNode actors.
*/
function spawnTest() {
let [target, debuggee, front] = yield initBackend(SIMPLE_NODES_URL);
let [_, nodes] = yield Promise.all([
front.setup({ reload: true }),
getN(front, "create-node", 14)
]);
let allParams = yield Promise.all(nodes.map(node => node.getParams()));
let types = [
"AudioDestinationNode", "AudioBufferSourceNode", "ScriptProcessorNode",
"AnalyserNode", "GainNode", "DelayNode", "BiquadFilterNode", "WaveShaperNode",
"PannerNode", "ConvolverNode", "ChannelSplitterNode", "ChannelMergerNode",
"DynamicsCompressorNode", "OscillatorNode"
];
allParams.forEach((params, i) => {
compare(params, NODE_DEFAULT_VALUES[types[i]], types[i]);
});
yield removeTab(target.tab);
finish();
}
function compare (actual, expected, type) {
actual.forEach(({ value, param }) => {
value = getGripValue(value);
if (typeof expected[param] === "function") {
ok(expected[param](value), type + " has a passing value for " + param);
}
else {
ise(value, expected[param], type + " has correct default value and type for " + param);
}
});
is(actual.length, Object.keys(expected).length,
type + " has correct amount of properties.");
}

View File

@ -20,7 +20,8 @@ function spawnTest () {
ise(type, "sine", "AudioNode:getParam correctly fetches non-AudioParam");
let type = yield oscNode.getParam("not-a-valid-param");
is(type, undefined, "AudioNode:getParam correctly returns false for invalid param");
ok(type.type === "undefined",
"AudioNode:getParam correctly returns a grip value for `undefined` for an invalid param.");
let resSuccess = yield oscNode.setParam("frequency", 220);
let freq = yield oscNode.getParam("frequency");

View File

@ -52,21 +52,3 @@ function spawnTest() {
yield teardown(panel);
finish();
}
function clickGraphNode (panelWin, el, waitForToggle = false) {
let { promise, resolve } = Promise.defer();
let promises = [
once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET)
];
if (waitForToggle) {
promises.push(once(panelWin, EVENTS.UI_INSPECTOR_TOGGLED));
}
// Use `el` as the element if it is one, otherwise
// assume it's an ID and find the related graph node
let element = el.tagName ? el : findGraphNode(panelWin, el);
click(panelWin, element);
return Promise.all(promises);
}

View File

@ -0,0 +1,52 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that SVG nodes and edges were created for the Graph View.
*/
function spawnTest() {
let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS } = panelWin;
let started = once(gFront, "start-context");
reload(target);
let [actors] = yield Promise.all([
get3(gFront, "create-node"),
waitForGraphRendered(panelWin, 3, 2)
]);
let [destId, oscId, gainId] = actors.map(actor => actor.actorID);
ok(!findGraphNode(panelWin, destId).classList.contains("selected"),
"No nodes selected on start. (destination)");
ok(!findGraphNode(panelWin, oscId).classList.contains("selected"),
"No nodes selected on start. (oscillator)");
ok(!findGraphNode(panelWin, gainId).classList.contains("selected"),
"No nodes selected on start. (gain)");
yield clickGraphNode(panelWin, oscId);
ok(findGraphNode(panelWin, oscId).classList.contains("selected"),
"Selected node has class 'selected'.");
ok(!findGraphNode(panelWin, destId).classList.contains("selected"),
"Non-selected nodes do not have class 'selected'.");
ok(!findGraphNode(panelWin, gainId).classList.contains("selected"),
"Non-selected nodes do not have class 'selected'.");
yield clickGraphNode(panelWin, gainId);
ok(!findGraphNode(panelWin, oscId).classList.contains("selected"),
"Previously selected node no longer has class 'selected'.");
ok(!findGraphNode(panelWin, destId).classList.contains("selected"),
"Non-selected nodes do not have class 'selected'.");
ok(findGraphNode(panelWin, gainId).classList.contains("selected"),
"Newly selected node now has class 'selected'.");
yield teardown(panel);
finish();
}

View File

@ -31,7 +31,7 @@ function spawnTest() {
let setAndCheck = setAndCheckVariable(panelWin, gVars);
checkVariableView(gVars, 0, {
"type": "\"sine\"",
"type": "sine",
"frequency": 440,
"detune": 0
}, "default loaded string");
@ -44,7 +44,7 @@ function spawnTest() {
click(panelWin, findGraphNode(panelWin, nodeIds[1]));
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
yield setAndCheck(0, "type", "square", "\"square\"", "sets string as string");
yield setAndCheck(0, "type", "square", "square", "sets string as string");
click(panelWin, findGraphNode(panelWin, nodeIds[2]));
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);

View File

@ -0,0 +1,39 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that params view correctly displays non-primitive properties
* like AudioBuffer and Float32Array in properties of AudioNodes.
*/
function spawnTest() {
let [target, debuggee, panel] = yield initWebAudioEditor(BUFFER_AND_ARRAY_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, WebAudioInspectorView } = panelWin;
let gVars = WebAudioInspectorView._propsView;
let started = once(gFront, "start-context");
reload(target);
let [actors] = yield Promise.all([
getN(gFront, "create-node", 3),
waitForGraphRendered(panelWin, 3, 2)
]);
let nodeIds = actors.map(actor => actor.actorID);
click(panelWin, findGraphNode(panelWin, nodeIds[2]));
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
checkVariableView(gVars, 0, {
"curve": "Float32Array"
}, "WaveShaper's `curve` is listed as an `Float32Array`.");
click(panelWin, findGraphNode(panelWin, nodeIds[1]));
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
checkVariableView(gVars, 0, {
"buffer": "AudioBuffer"
}, "AudioBufferSourceNode's `buffer` is listed as an `AudioBuffer`.");
yield teardown(panel);
finish();
}

View File

@ -0,0 +1,39 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that params view correctly displays all properties for nodes
* correctly, with default values and correct types.
*/
function spawnTest() {
let [target, debuggee, panel] = yield initWebAudioEditor(SIMPLE_NODES_URL);
let { panelWin } = panel;
let { gFront, $, $$, EVENTS, WebAudioInspectorView } = panelWin;
let gVars = WebAudioInspectorView._propsView;
let started = once(gFront, "start-context");
reload(target);
let [actors] = yield Promise.all([
getN(gFront, "create-node", 14),
waitForGraphRendered(panelWin, 14, 0)
]);
let nodeIds = actors.map(actor => actor.actorID);
let types = [
"AudioDestinationNode", "AudioBufferSourceNode", "ScriptProcessorNode",
"AnalyserNode", "GainNode", "DelayNode", "BiquadFilterNode", "WaveShaperNode",
"PannerNode", "ConvolverNode", "ChannelSplitterNode", "ChannelMergerNode",
"DynamicsCompressorNode", "OscillatorNode"
];
for (let i = 0; i < types.length; i++) {
click(panelWin, findGraphNode(panelWin, nodeIds[i]));
yield once(panelWin, EVENTS.UI_INSPECTOR_NODE_SET);
checkVariableView(gVars, 0, NODE_DEFAULT_VALUES[types[i]], types[i]);
}
yield teardown(panel);
finish();
}

View File

@ -0,0 +1,56 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Web Audio Editor test page</title>
</head>
<body>
<script type="text/javascript;version=1.8">
"use strict";
let audioURL = "http://example.com/browser/browser/devtools/webaudioeditor/test/440hz_sine.ogg";
let ctx = new AudioContext();
let bufferNode = ctx.createBufferSource();
let shaperNode = ctx.createWaveShaper();
shaperNode.curve = generateWaveShapingCurve();
let xhr = getBuffer(audioURL, () => {
ctx.decodeAudioData(xhr.response, (buffer) => {
bufferNode.buffer = buffer;
bufferNode.connect(shaperNode);
shaperNode.connect(ctx.destination);
});
});
function generateWaveShapingCurve() {
let frames = 65536;
let curve = new Float32Array(frames);
let n = frames;
let n2 = n / 2;
for (let i = 0; i < n; ++i) {
let x = (i - n2) / n2;
let y = Math.atan(5 * x) / (0.5 * Math.PI);
}
return curve;
}
function getBuffer (url, callback) {
let xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.responseType = "arraybuffer";
xhr.onload = callback;
xhr.send();
return xhr;
}
</script>
</body>
</html>

View File

@ -24,6 +24,7 @@ const EXAMPLE_URL = "http://example.com/browser/browser/devtools/webaudioeditor/
const SIMPLE_CONTEXT_URL = EXAMPLE_URL + "doc_simple-context.html";
const COMPLEX_CONTEXT_URL = EXAMPLE_URL + "doc_complex-context.html";
const SIMPLE_NODES_URL = EXAMPLE_URL + "doc_simple-node-creation.html";
const BUFFER_AND_ARRAY_URL = EXAMPLE_URL + "doc_buffer-and-array.html";
// All tests are asynchronous.
waitForExplicitFinish();
@ -219,8 +220,23 @@ function checkVariableView (view, index, hash, description = "") {
let aVar = scope.get(variable);
is(aVar.target.querySelector(".name").getAttribute("value"), variable,
"Correct property name for " + variable);
is(aVar.target.querySelector(".value").getAttribute("value"), hash[variable],
"Correct property value of " + hash[variable] + " for " + variable + " " + description);
let value = aVar.target.querySelector(".value").getAttribute("value");
// Cast value with JSON.parse if possible;
// will fail when displaying Object types like "ArrayBuffer"
// and "Float32Array", but will match the original value.
try {
value = JSON.parse(value);
}
catch (e) {}
if (typeof hash[variable] === "function") {
ok(hash[variable](value),
"Passing property value of " + value + " for " + variable + " " + description);
}
else {
ise(value, hash[variable],
"Correct property value of " + hash[variable] + " for " + variable + " " + description);
}
});
}
@ -292,23 +308,116 @@ function wait (n) {
return promise;
}
/**
* Clicks a graph node based on actorID or passing in an element.
* Returns a promise that resolves once
* UI_INSPECTOR_NODE_SET is fired.
*/
function clickGraphNode (panelWin, el, waitForToggle = false) {
let { promise, resolve } = Promise.defer();
let promises = [
once(panelWin, panelWin.EVENTS.UI_INSPECTOR_NODE_SET)
];
if (waitForToggle) {
promises.push(once(panelWin, panelWin.EVENTS.UI_INSPECTOR_TOGGLED));
}
// Use `el` as the element if it is one, otherwise
// assume it's an ID and find the related graph node
let element = el.tagName ? el : findGraphNode(panelWin, el);
click(panelWin, element);
return Promise.all(promises);
}
/**
* Returns the primitive value of a grip's value, or the
* original form that the string grip.type comes from.
*/
function getGripValue (value) {
if (~["boolean", "string", "number"].indexOf(typeof value)) {
return value;
}
switch (value.type) {
case "undefined": return undefined;
case "Infinity": return Infinity;
case "-Infinity": return -Infinity;
case "NaN": return NaN;
case "-0": return -0;
case "null": return null;
default: return value;
}
}
/**
* List of audio node properties to test against expectations of the AudioNode actor
*/
const NODE_PROPERTIES = {
"OscillatorNode": ["type", "frequency", "detune"],
"GainNode": ["gain"],
"DelayNode": ["delayTime"],
"AudioBufferSourceNode": ["buffer", "playbackRate", "loop", "loopStart", "loopEnd"],
"ScriptProcessorNode": ["bufferSize"],
"PannerNode": ["panningModel", "distanceModel", "refDistance", "maxDistance", "rolloffFactor", "coneInnerAngle", "coneOuterAngle", "coneOuterGain"],
"ConvolverNode": ["buffer", "normalize"],
"DynamicsCompressorNode": ["threshold", "knee", "ratio", "reduction", "attack", "release"],
"BiquadFilterNode": ["type", "frequency", "Q", "detune", "gain"],
"WaveShaperNode": ["curve", "oversample"],
"AnalyserNode": ["fftSize", "minDecibels", "maxDecibels", "smoothingTimeConstraint", "frequencyBinCount"],
"AudioDestinationNode": [],
"ChannelSplitterNode": [],
"ChannelMergerNode": []
const NODE_DEFAULT_VALUES = {
"AudioDestinationNode": {},
"AudioBufferSourceNode": {
"playbackRate": 1,
"loop": false,
"loopStart": 0,
"loopEnd": 0,
"buffer": null
},
"ScriptProcessorNode": {
"bufferSize": 4096
},
"AnalyserNode": {
"fftSize": 2048,
"minDecibels": -100,
"maxDecibels": -30,
"smoothingTimeConstant": 0.8,
"frequencyBinCount": 1024
},
"GainNode": {
"gain": 1
},
"DelayNode": {
"delayTime": 0
},
"BiquadFilterNode": {
"type": "lowpass",
"frequency": 350,
"Q": 1,
"detune": 0,
"gain": 0
},
"WaveShaperNode": {
"curve": null,
"oversample": "none"
},
"PannerNode": {
"panningModel": "HRTF",
"distanceModel": "inverse",
"refDistance": 1,
"maxDistance": 10000,
"rolloffFactor": 1,
"coneInnerAngle": 360,
"coneOuterAngle": 360,
"coneOuterGain": 0
},
"ConvolverNode": {
"buffer": null,
"normalize": true
},
"ChannelSplitterNode": {},
"ChannelMergerNode": {},
"DynamicsCompressorNode": {
"threshold": -24,
"knee": 30,
"ratio": 12,
"reduction": 0,
"attack": 0.003000000026077032,
"release": 0.25
},
"OscillatorNode": {
"type": "sine",
"frequency": 440,
"detune": 0
}
};

View File

@ -52,11 +52,13 @@ let WebAudioGraphView = {
initialize: function() {
this._onGraphNodeClick = this._onGraphNodeClick.bind(this);
this._onThemeChange = this._onThemeChange.bind(this);
this._onNodeSelect = this._onNodeSelect.bind(this);
this.draw = debounce(this.draw.bind(this), GRAPH_DEBOUNCE_TIMER);
$('#graph-target').addEventListener('click', this._onGraphNodeClick, false);
window.on(EVENTS.THEME_CHANGE, this._onThemeChange);
window.on(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSelect);
},
/**
@ -68,6 +70,7 @@ let WebAudioGraphView = {
}
$('#graph-target').removeEventListener('click', this._onGraphNodeClick, false);
window.off(EVENTS.THEME_CHANGE, this._onThemeChange);
window.off(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSelect);
},
/**
@ -101,20 +104,18 @@ let WebAudioGraphView = {
},
/**
* Makes the corresponding graph node appear "focused", called from WebAudioParamView
* Makes the corresponding graph node appear "focused", removing
* focused styles from all other nodes. If no `actorID` specified,
* make all nodes appear unselected.
* Called from UI_INSPECTOR_NODE_SELECT.
*/
focusNode: function (actorID) {
// Remove class "selected" from all nodes
Array.prototype.forEach.call($$(".nodes > g"), $node => $node.classList.remove("selected"));
Array.forEach($$(".nodes > g"), $node => $node.classList.remove("selected"));
// Add to "selected"
this._getNodeByID(actorID).classList.add("selected");
},
/**
* Unfocuses the corresponding graph node, called from WebAudioParamView
*/
blurNode: function (actorID) {
this._getNodeByID(actorID).classList.remove("selected");
if (actorID) {
this._getNodeByID(actorID).classList.add("selected");
}
},
/**
@ -234,6 +235,10 @@ let WebAudioGraphView = {
* Event handlers
*/
_onNodeSelect: function (eventName, id) {
this.focusNode(id);
},
/**
* Fired when the devtools theme changes.
*/

View File

@ -0,0 +1,10 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXTRA_COMPONENTS += [
'webideCli.js',
'webideComponents.manifest',
]

View File

@ -0,0 +1,81 @@
/* 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/. */
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
/**
* Handles -webide command line option.
*
* See webide/content/cli.js for a complete description of the command line.
*/
function webideCli() { }
webideCli.prototype = {
handle: function(cmdLine) {
let param;
try {
// Returns null if -webide is not present
// Throws if -webide is present with no params
param = cmdLine.handleFlagWithParam("webide", false);
if (!param) {
return;
}
} catch(e) {
// -webide is present with no params
cmdLine.handleFlag("webide", false);
}
// If -webide is used remotely, we don't want to open
// a new tab.
//
// If -webide is used for a new Firefox instance, we
// want to open webide only.
cmdLine.preventDefault = true;
let win = Services.wm.getMostRecentWindow("devtools:webide");
if (win) {
win.focus();
if (param) {
win.handleCommandline(param);
}
return;
}
win = Services.ww.openWindow(null,
"chrome://webide/content/",
"webide",
"chrome,centerscreen,resizable,dialog=no",
null);
if (param) {
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad, true);
// next tick
win.setTimeout(() => win.handleCommandline(param), 0);
}, true);
}
if (cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
// If this is a new Firefox instance, and because we will only start
// webide, we need to notify "sessionstore-windows-restored" to trigger
// addons registration (for simulators and adb helper).
Services.obs.notifyObservers(null, "sessionstore-windows-restored", "");
}
},
helpInfo : "",
classID: Components.ID("{79b7b44e-de5e-4e4c-b7a2-044003c615d9}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([webideCli]);

View File

@ -0,0 +1,4 @@
# webide components
component {79b7b44e-de5e-4e4c-b7a2-044003c615d9} webideCli.js
contract @mozilla.org/browser/webide-clh;1 {79b7b44e-de5e-4e4c-b7a2-044003c615d9}
category command-line-handler a-webide @mozilla.org/browser/webide-clh;1

View File

@ -0,0 +1,198 @@
/* 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/. */
/**
*
* SYNOPSIS
* firefox --webide [OPTIONS]
*
* DESCRIPTION
* Starts WebIDE (aka App Manager). If Firefox has already started, opens
* the WebIDE window. If Firefox is not running, no browser window will
* be open.
*
* It's recommended to add the option `-jsconsole` to see potential errors
* occurring while processing the parameters.
*
* OPTIONS
* A set of "key=value" pairs, separated by '&'.
*
* actions=action1,action2,...,actionN
* Executed in order. actionN will be executed only if actionN-1 doesn't fail.
* Available actions:
* addPackagedApp: Import and select app ('location' parameter must be a directory)
* addHostedApp: Import and select app ('location' parameter must be a URL)
* connectToRuntime: Connect to runtime (require 'runtimeType')
* play: Start or reload selected app on connected runtime
* debug: Debug selected app or debug 'appID'
*
* location
* packaged app directory or hosted app manifest URL
*
* runtimeType
* Type of runtime to connect to. "usb" or "simulator"
*
* runtimeID
* Which runtime to use. By default, the most recent USB device or most recent simulator
*
* appID
* App on runtime
*
* EXAMPLES
*
* $ firefox --webide "actions=addPackagedApp,connectToRuntime,play,debug&location=/home/bob/Downloads/foobar/&runtimeType=usb"
* Select app located in /home/bob/Downloads/foobar, then
* Connect to USB device, then
* Install app, then
* Start developer tools connected to the running app
*
*/
window.handleCommandline = function(cmdline) {
console.log("External query", cmdline);
let params = new Map();
for (let token of cmdline.split("&")) {
token = token.split("=");
params.set(token[0], token[1]);
}
if (params.has("actions")) {
return UI.busyUntil(Task.spawn(function* () {
let actions = params.get("actions").split(",");
for (let action of actions) {
if (action in CliActions) {
console.log("External query - running action", action);
yield CliActions[action].call(window, params);
} else {
console.log("External query - unknown action", action);
}
}
}), "Computing command line");
} else {
return promise.reject("No actions provided");
}
}
let CliActions = {
addPackagedApp: function(params) {
return Task.spawn(function* () {
let location = params.get("location");
if (!location) {
throw new Error("No location parameter");
}
yield AppProjects.load();
// Normalize location
let directory = new FileUtils.File(location);
if (AppProjects.get(directory.path)) {
// Already imported
return;
}
yield Cmds.importPackagedApp(location);
})
},
addHostedApp: function(params) {
return Task.spawn(function* () {
let location = params.get("location");
if (!location) {
throw new Error("No location parameter");
}
yield AppProjects.load();
if (AppProjects.get(location)) {
// Already imported
return;
}
yield Cmds.importHostedApp(location);
})
},
debug: function(params) {
return Task.spawn(function* () {
let appID = params.get("appID");
if (appID) {
let appToSelect;
for (let i = 0; i < AppManager.webAppsStore.object.all.length; i++) {
let app = AppManager.webAppsStore.object.all[i];
if (app.manifestURL == appID) {
appToSelect = app;
break;
}
}
if (!appToSelect) {
throw new Error("App not found on device");
}
AppManager.selectedProject = {
type: "runtimeApp",
app: appToSelect,
icon: appToSelect.iconURL,
name: appToSelect.name
};
}
UI.closeToolbox();
yield Cmds.toggleToolbox();
});
},
connectToRuntime: function(params) {
return Task.spawn(function* () {
let type = params.get("runtimeType");
if (type != "usb" && type != "simulator") {
return promise.reject("Unkown runtime type");
}
yield Cmds.disconnectRuntime();
if (AppManager.runtimeList[type].length == 0) {
let deferred = promise.defer();
function onRuntimeListUpdate(event, what) {
if (AppManager.runtimeList[type].length > 0) {
deferred.resolve();
}
}
let timeout = setTimeout(deferred.resolve, 3000);
AppManager.on("app-manager-update", onRuntimeListUpdate);
yield deferred.promise;
AppManager.off("app-manager-update", onRuntimeListUpdate);
clearTimeout(timeout);
}
let runtime;
let runtimeID = params.get("runtimeID");
if (runtimeID) {
for (let r of AppManager.runtimeList[type]) {
if (r.getID() == runtimeID) {
runtime = r;
break;
}
}
} else {
let list = AppManager.runtimeList[type];
runtime = list[list.length - 1];
}
if (!runtime) {
return promise.reject("Can't find any runtime to connect to");
}
let deferred = promise.defer();
// store-ready is fired when the list of installed apps has been
// received by the webAppsStore.
AppManager.webAppsStore.once("store-ready", deferred.resolve);
UI.connectToRuntime(runtime).then(null, deferred.reject);
return deferred.promise;
})
},
play: function(params) {
return Task.spawn(function* () {
yield Cmds.play();
})
},
}

View File

@ -83,7 +83,10 @@ function updateUI() {
document.querySelector("#type").classList.remove("hidden");
if (project.type == "runtimeApp") {
let manifest = AppManager.getProjectManifestURL(project);
document.querySelector("#type").textContent = manifest.type || "web";
document.querySelector("#manifestURLHeader").classList.remove("hidden");
document.querySelector("#manifestURL").textContent = manifest;
} else {
document.querySelector("#type").textContent = project.type + " " + (manifest.type || "web");
}

View File

@ -3,13 +3,14 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
webide.jar:
% content webide %content/
% content webide %content/
content/webide.xul (webide.xul)
content/webide.js (webide.js)
content/newapp.xul (newapp.xul)
content/newapp.js (newapp.js)
content/details.xhtml (details.xhtml)
content/details.js (details.js)
content/cli.js (cli.js)
# Temporarily include locales in content, until we're ready
# to localize webide

View File

@ -20,6 +20,11 @@ const {AppManager} = require("devtools/app-manager");
let gTemplateList = null;
// See bug 989619
console.log = console.log.bind(console);
console.warn = console.warn.bind(console);
console.error = console.error.bind(console);
window.addEventListener("load", function onLoad() {
window.removeEventListener("load", onLoad);
let projectNameNode = document.querySelector("#project-name");

View File

@ -24,6 +24,11 @@ const Strings = Services.strings.createBundle("chrome://webide/content/webide.pr
const HTML = "http://www.w3.org/1999/xhtml";
const HELP_URL = "https://developer.mozilla.org/Firefox_OS/Using_the_App_Manager#Troubleshooting";
// See bug 989619
console.log = console.log.bind(console);
console.warn = console.warn.bind(console);
console.error = console.error.bind(console);
window.addEventListener("load", function onLoad() {
window.removeEventListener("load", onLoad);
UI.init();
@ -50,19 +55,19 @@ let UI = {
this.onfocus = this.onfocus.bind(this);
window.addEventListener("focus", this.onfocus, true);
try {
AppProjects.load().then(() => {
let lastProjectLocation = Services.prefs.getCharPref("devtools.webide.lastprojectlocation");
AppProjects.load().then(() => {
if (lastProjectLocation) {
let lastProject = AppProjects.get(lastProjectLocation);
if (lastProject) {
AppManager.selectedProject = lastProject;
} else {
AppManager.selectedProject = null;
}
});
} catch(e) {
AppManager.selectedProject = null;
}
} else {
AppManager.selectedProject = null;
}
});
},
uninit: function() {
@ -144,6 +149,7 @@ let UI = {
},
busy: function() {
this.hidePanels();
document.querySelector("window").classList.add("busy")
this.updateCommands();
},
@ -157,7 +163,6 @@ let UI = {
// Freeze the UI until the promise is resolved. A 30s timeout
// will unfreeze the UI, just in case the promise never gets
// resolved.
this.hidePanels();
let timeout = setTimeout(() => {
this.unbusy();
UI.reportError("error_operationTimeout", operationDescription);

View File

@ -24,6 +24,7 @@
persist="screenX screenY width height">
<script type="application/javascript" src="webide.js"></script>
<script type="application/javascript" src="cli.js"></script>
<commandset id="mainCommandSet">
<commandset id="editMenuCommands"/>

View File

@ -18,6 +18,8 @@ const {AppValidator} = require("devtools/app-manager/app-validator");
const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
const AppActorFront = require("devtools/app-actor-front");
const {getDeviceFront} = require("devtools/server/actors/device");
const {setTimeout} = require("sdk/timers");
const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
const Strings = Services.strings.createBundle("chrome://webide/content/webide.properties");
@ -34,7 +36,10 @@ exports.AppManager = AppManager = {
this.connection = ConnectionManager.createConnection("localhost", port);
this.onConnectionChanged = this.onConnectionChanged.bind(this);
this.connection.on(Connection.Events.STATUS_CHANGED, this.onConnectionChanged);
this.onWebAppsStoreready = this.onWebAppsStoreready.bind(this);
this.webAppsStore = new WebappsStore(this.connection);
this.webAppsStore.on("store-ready", this.onWebAppsStoreready);
this.runtimeList = {usb: [], simulator: []};
this.trackUSBRuntimes();
@ -49,9 +54,10 @@ exports.AppManager = AppManager = {
this.untrackSimulatorRuntimes();
this._runningApps.clear();
this.runtimeList = null;
this.connection.off(Connection.Events.STATUS_CHANGED, this.onConnectionChanged);
this.webAppsStore.off("store-ready", this.onWebAppsStoreready);
this.webAppsStore.destroy();
this.webAppsStore = null;
this.connection.off(Connection.Events.STATUS_CHANGED, this.onConnectionChanged);
this._listTabsResponse = null;
this.connection.disconnect();
this.connection = null;
@ -92,12 +98,17 @@ exports.AppManager = AppManager = {
this._listenToApps();
this._listTabsResponse = response;
this._getRunningApps();
this.update("list-tabs-response");
});
}
this.update("connection");
},
onWebAppsStoreready: function() {
this.update("runtime-apps-found");
},
_runningApps: new Set(),
_getRunningApps: function() {
let client = this.connection.client;
@ -158,22 +169,32 @@ exports.AppManager = AppManager = {
getTarget: function() {
let manifest = this.getProjectManifestURL(this.selectedProject);
let name = this.selectedProject.name;
if (manifest) {
let client = this.connection.client;
let actor = this._listTabsResponse.webappsActor;
let promise = AppActorFront.getTargetForApp(client, actor, manifest);
promise.then(( ) => { },
(e) => {
this.reportError("error_cantConnectToApp", manifestURL);
console.error("Can't connect to app: " + e)
});
return promise;
if (!manifest) {
console.error("Can't find manifestURL for selected project");
return promise.reject();
}
console.error("Can't find manifestURL for selected project");
return promise.reject();
let client = this.connection.client;
let actor = this._listTabsResponse.webappsActor;
return Task.spawn(function* () {
// Once we asked the app to launch, the app isn't necessary completely loaded.
// launch request only ask the app to launch and immediatly returns.
// We have to keep trying to get app tab actors required to create its target.
for (let i = 0; i < 10; i++) {
try {
let target = yield AppActorFront.getTargetForApp(client, actor, manifest);
// Success
return target;
} catch(e) {}
let deferred = promise.defer();
setTimeout(deferred.resolve, 500);
yield deferred.promise;
}
AppManager.reportError("error_cantConnectToApp", manifest);
throw new Error("can't connect to app");
});
},
@ -271,7 +292,7 @@ exports.AppManager = AppManager = {
disconnectRuntime: function() {
if (this.connection.status != Connection.Status.CONNECTED) {
return promise.reject("Already disconnected");
return promise.resolve();
}
let deferred = promise.defer();
this.connection.once(Connection.Events.DISCONNECTED, () => deferred.resolve());
@ -302,26 +323,34 @@ exports.AppManager = AppManager = {
return promise.reject("Can't install");
}
return this.validateProject(project).then(() => {
return Task.spawn(function* () {
let self = AppManager;
yield self.validateProject(project);
if (project.errorsCount > 0) {
this.reportError("error_cantInstallValidationErrors");
self.reportError("error_cantInstallValidationErrors");
return;
}
let client = this.connection.client;
let actor = this._listTabsResponse.webappsActor;
let client = self.connection.client;
let actor = self._listTabsResponse.webappsActor;
let installPromise;
if (project.type != "packaged" && project.type != "hosted") {
return promise.reject("Don't know how to install project");
}
if (project.type == "packaged") {
installPromise = AppActorFront.installPackaged(client, actor, project.location, project.packagedAppOrigin)
.then(({ appId }) => {
// If the packaged app specified a custom origin override,
// we need to update the local project origin
project.packagedAppOrigin = appId;
// And ensure the indexed db on disk is also updated
AppProjects.update(project);
});
let {appId} = yield AppActorFront.installPackaged(client,
actor,
project.location,
project.packagedAppOrigin);
// If the packaged app specified a custom origin override,
// we need to update the local project origin
project.packagedAppOrigin = appId;
// And ensure the indexed db on disk is also updated
AppProjects.update(project);
}
if (project.type == "hosted") {
@ -332,25 +361,33 @@ exports.AppManager = AppManager = {
origin: origin.spec,
manifestURL: project.location
};
installPromise = AppActorFront.installHosted(client, actor, appId, metadata, project.manifest);
yield AppActorFront.installHosted(client,
actor,
appId,
metadata,
project.manifest);
}
if (!installPromise) {
return promise.reject("Can't install");
function waitUntilProjectRuns() {
let deferred = promise.defer();
self.on("app-manager-update", function onUpdate(event, what) {
if (what == "project-is-running") {
self.off("app-manager-update", onUpdate);
deferred.resolve();
}
});
return deferred.promise;
}
return installPromise.then(() => {
let manifest = this.getProjectManifestURL(project);
if (!this._runningApps.has(manifest)) {
console.log("Launching app: " + project.name);
AppActorFront.launchApp(client, actor, manifest);
} else {
console.log("Reloading app: " + project.name);
AppActorFront.reloadApp(client, actor, manifest);
}
});
let manifest = self.getProjectManifestURL(project);
if (!self._runningApps.has(manifest)) {
yield AppActorFront.launchApp(client, actor, manifest);
yield waitUntilProjectRuns();
}, console.error);
} else {
yield AppActorFront.reloadApp(client, actor, manifest);
}
});
},
stopRunningApp: function() {
@ -367,78 +404,78 @@ exports.AppManager = AppManager = {
return promise.reject();
}
let validation = new AppValidator(project);
return validation.validate()
.then(() => {
if (validation.manifest) {
let manifest = validation.manifest;
let iconPath;
if (manifest.icons) {
let size = Object.keys(manifest.icons).sort(function(a, b) b - a)[0];
if (size) {
iconPath = manifest.icons[size];
}
}
if (!iconPath) {
project.icon = AppManager.DEFAULT_PROJECT_ICON;
} else {
if (project.type == "hosted") {
let manifestURL = Services.io.newURI(project.location, null, null);
let origin = Services.io.newURI(manifestURL.prePath, null, null);
project.icon = Services.io.newURI(iconPath, null, origin).spec;
} else if (project.type == "packaged") {
let projectFolder = FileUtils.File(project.location);
let folderURI = Services.io.newFileURI(projectFolder).spec;
project.icon = folderURI + iconPath.replace(/^\/|\\/, "");
}
}
project.manifest = validation.manifest;
return Task.spawn(function* () {
if ("name" in project.manifest) {
project.name = project.manifest.name;
} else {
project.name = AppManager.DEFAULT_PROJECT_NAME;
let validation = new AppValidator(project);
yield validation.validate();
if (validation.manifest) {
let manifest = validation.manifest;
let iconPath;
if (manifest.icons) {
let size = Object.keys(manifest.icons).sort(function(a, b) b - a)[0];
if (size) {
iconPath = manifest.icons[size];
}
} else {
project.manifest = null;
}
if (!iconPath) {
project.icon = AppManager.DEFAULT_PROJECT_ICON;
} else {
if (project.type == "hosted") {
let manifestURL = Services.io.newURI(project.location, null, null);
let origin = Services.io.newURI(manifestURL.prePath, null, null);
project.icon = Services.io.newURI(iconPath, null, origin).spec;
} else if (project.type == "packaged") {
let projectFolder = FileUtils.File(project.location);
let folderURI = Services.io.newFileURI(projectFolder).spec;
project.icon = folderURI + iconPath.replace(/^\/|\\/, "");
}
}
project.manifest = validation.manifest;
if ("name" in project.manifest) {
project.name = project.manifest.name;
} else {
project.name = AppManager.DEFAULT_PROJECT_NAME;
}
} else {
project.manifest = null;
project.icon = AppManager.DEFAULT_PROJECT_ICON;
project.name = AppManager.DEFAULT_PROJECT_NAME;
}
project.validationStatus = "valid";
project.validationStatus = "valid";
if (validation.warnings.length > 0) {
project.warningsCount = validation.warnings.length;
project.warnings = validation.warnings;
project.validationStatus = "warning";
} else {
project.warnings = "";
project.warningsCount = 0;
}
if (validation.warnings.length > 0) {
project.warningsCount = validation.warnings.length;
project.warnings = validation.warnings;
project.validationStatus = "warning";
} else {
project.warnings = "";
project.warningsCount = 0;
}
if (validation.errors.length > 0) {
project.errorsCount = validation.errors.length;
project.errors = validation.errors;
project.validationStatus = "error";
} else {
project.errors = "";
project.errorsCount = 0;
}
if (validation.errors.length > 0) {
project.errorsCount = validation.errors.length;
project.errors = validation.errors;
project.validationStatus = "error";
} else {
project.errors = "";
project.errorsCount = 0;
}
if (project.warningsCount && project.errorsCount) {
project.validationStatus = "error warning";
}
if (project.warningsCount && project.errorsCount) {
project.validationStatus = "error warning";
}
if (this.selectedProject === project) {
this.update("project-validated");
}
if (AppProjects.get(project.location)) {
yield AppProjects.update(project);
}
if (AppProjects.get(project.location)) {
AppProjects.update(project);
}
return project;
}, console.error);
if (this.selectedProject === project) {
this.update("project-validated");
}
});
},
/* RUNTIME LIST */

View File

@ -6,6 +6,7 @@
PARALLEL_DIRS += [
'content',
'components',
'themes',
]

View File

@ -11,3 +11,4 @@ support-files =
[test_newapp.html]
[test_import.html]
[test_runtime.html]
[test_cli.html]

View File

@ -0,0 +1,67 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
<title></title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script>
<script type="application/javascript;version=1.8" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<script type="application/javascript;version=1.8">
window.onload = function() {
SimpleTest.waitForExplicitFinish();
Task.spawn(function* () {
let clClass = Components.classes["@mozilla.org/toolkit/command-line;1"].createInstance();
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
DebuggerServer.init(function () { return true; });
DebuggerServer.addBrowserActors();
let win = yield openWebIDE();
let packagedAppLocation = getTestFilePath("app");
let cli = "actions=addPackagedApp&location=" + packagedAppLocation;
yield win.handleCommandline(cli);
let project = win.AppManager.selectedProject;
is(project.location, packagedAppLocation, "Project imported");
win.AppManager.runtimeList.usb.push({
connect: function(connection) {
ok(connection, win.AppManager.connection, "connection is valid");
connection.host = null; // force connectPipe
connection.connect();
return promise.resolve();
},
getName: function() {
return "fakeRuntime";
}
});
yield win.handleCommandline("actions=connectToRuntime&runtimeType=usb");
is(win.AppManager.connection.status, "connected", "connected");
is(win.AppManager.selectedProject.name, "A name (in app directory)", "project imported");
yield removeAllProjects();
yield closeWebIDE(win);
DebuggerServer.destroy();
SimpleTest.finish();
});
}
</script>
</body>
</html>

View File

@ -5,3 +5,4 @@
pref("devtools.webide.showProjectEditor", true);
pref("devtools.webide.templatesURL", "http://fixme/");
pref("devtools.webide.lastprojectlocation", "");

View File

@ -1,4 +1,4 @@
This is the pdf.js project output, https://github.com/mozilla/pdf.js
Current extension version is: 1.0.162
Current extension version is: 1.0.248

View File

@ -131,3 +131,4 @@ PdfRedirector.prototype = {
// Do nothing
}
};

View File

@ -977,3 +977,4 @@ PdfStreamConverter.prototype = {
delete this.binaryStream;
}
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -477,9 +477,10 @@ html[dir='rtl'] .splitToolbarButton > .toolbarButton {
}
.toolbarButton,
.secondaryToolbarButton {
.secondaryToolbarButton,
.overlayButton {
border: 0 none;
background-color: rgba(0, 0, 0, 0);
background: none;
width: 32px;
height: 25px;
}
@ -492,7 +493,8 @@ html[dir='rtl'] .splitToolbarButton > .toolbarButton {
}
.toolbarButton[disabled],
.secondaryToolbarButton[disabled] {
.secondaryToolbarButton[disabled],
.overlayButton[disabled] {
opacity: .5;
}
@ -581,8 +583,8 @@ html[dir='rtl'] .splitToolbarButtonSeparator {
.toolbarButton,
.dropdownToolbarButton,
.overlayButton,
.secondaryToolbarButton {
.secondaryToolbarButton,
.overlayButton {
min-width: 16px;
padding: 2px 6px 0;
border: 1px solid transparent;
@ -693,12 +695,6 @@ html[dir='rtl'] .dropdownToolbarButton {
background: hsl(0,0%,24%);
}
.overlayButton {
margin: 3px 2px 4px 5px !important;
line-height: 16px;
padding: 2px 6px 3px 6px;
}
#customScaleOption {
display: none;
}
@ -1329,80 +1325,37 @@ canvas {
width: 98%;
}
.overlayButton {
width: auto;
margin: 3px 4px 2px 4px !important;
padding: 2px 6px 3px 6px;
}
#overlayContainer {
display: table;
position: absolute;
width: 100%;
height: 100%;
background-color: hsla(0,0%,0%,.2);
z-index: 10000;
z-index: 40000;
}
#overlayContainer > * {
overflow: auto;
}
#promptContainer {
#overlayContainer > .container {
display: table-cell;
vertical-align: middle;
text-align: center;
}
#promptContainer > * {
display: inline-block;
}
.prompt {
display: table;
padding: 15px;
border-spacing: 4px;
color: hsl(0,0%,85%);
line-height: 14px;
text-align: center;
background-color: #474747; /* fallback */
background-image: url(images/texture.png),
linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95));
box-shadow: inset 1px 0 0 hsla(0,0%,100%,.08),
inset 0 1px 1px hsla(0,0%,0%,.15),
inset 0 -1px 0 hsla(0,0%,100%,.05),
0 1px 0 hsla(0,0%,0%,.15),
0 1px 1px hsla(0,0%,0%,.1);
}
.prompt > .row {
display: table-row;
}
.prompt > .row > * {
display: table-cell;
}
.prompt .toolbarField {
margin: 5px 0;
width: 200px;
}
.prompt .toolbarField:hover,
.prompt .toolbarField:focus {
border-color: hsla(0,0%,0%,.32) hsla(0,0%,0%,.38) hsla(0,0%,0%,.42);
}
#documentPropertiesContainer {
display: table-cell;
vertical-align: middle;
text-align: center;
}
#documentPropertiesContainer > * {
#overlayContainer > .container > .dialog {
display: inline-block;
padding: 15px;
border-spacing: 4px;
max-width: 350px;
max-height: 350px;
color: hsl(0,0%,85%);
font-size: 12px;
line-height: 14px;
text-align: left;
cursor: default;
background-color: #474747; /* fallback */
background-image: url(images/texture.png),
linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95));
@ -1411,9 +1364,28 @@ canvas {
inset 0 -1px 0 hsla(0,0%,100%,.05),
0 1px 0 hsla(0,0%,0%,.15),
0 1px 1px hsla(0,0%,0%,.1);
border: 1px solid hsla(0,0%,0%,.5);
border-radius: 4px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}
#documentPropertiesContainer .separator {
.dialog > .row {
display: table-row;
}
.dialog > .row > * {
display: table-cell;
}
.dialog .toolbarField {
margin: 5px 0;
}
.dialog .toolbarField:hover,
.dialog .toolbarField:focus {
border-color: hsla(0,0%,0%,.32) hsla(0,0%,0%,.38) hsla(0,0%,0%,.42);
}
.dialog .separator {
display: block;
margin: 4px 0 4px 0;
height: 1px;
@ -1422,35 +1394,40 @@ canvas {
box-shadow: 0 0 0 1px hsla(0,0%,100%,.08);
}
#documentPropertiesContainer .row {
display: table-row;
.dialog .buttonRow {
text-align: center;
vertical-align: middle;
}
html[dir='ltr'] #documentPropertiesContainer .row > * {
display: table-cell;
min-width: 100px;
#passwordOverlay > .dialog {
text-align: center;
}
#passwordOverlay .toolbarField {
width: 200px;
}
html[dir='rtl'] #documentPropertiesContainer .row > * {
display: table-cell;
#documentPropertiesOverlay > .dialog {
text-align: left;
}
#documentPropertiesOverlay .row > * {
min-width: 100px;
}
html[dir='ltr'] #documentPropertiesOverlay .row > * {
text-align: left;
}
html[dir='rtl'] #documentPropertiesOverlay .row > * {
text-align: right;
}
#documentPropertiesContainer .row span {
#documentPropertiesOverlay .row > span {
width: 125px;
word-wrap: break-word;
}
#documentPropertiesContainer .row p {
#documentPropertiesOverlay .row > p {
max-width: 225px;
word-wrap: break-word;
}
#documentPropertiesContainer .buttonRow {
#documentPropertiesOverlay .buttonRow {
margin-top: 10px;
text-align: center;
vertical-align: middle;
}
.clearBoth {

View File

@ -25,8 +25,8 @@ http://sourceforge.net/adobe/cmap/wiki/License/
<!-- This snippet is used in the Firefox extension (included from viewer.html) -->
<base href="resource://pdf.js/web/" />
<script type="text/javascript" src="l10n.js"></script>
<script type="text/javascript" src="../build/pdf.js"></script>
<script src="l10n.js"></script>
<script src="../build/pdf.js"></script>
<link rel="stylesheet" href="viewer.css"/>
@ -36,8 +36,8 @@ http://sourceforge.net/adobe/cmap/wiki/License/
<script type="text/javascript" src="debugger.js"></script>
<script type="text/javascript" src="viewer.js"></script>
<script src="debugger.js"></script>
<script src="viewer.js"></script>
</head>
@ -267,8 +267,8 @@ http://sourceforge.net/adobe/cmap/wiki/License/
</div> <!-- mainContainer -->
<div id="overlayContainer" class="hidden">
<div id="promptContainer" class="hidden">
<div id="passwordContainer" class="prompt doorHanger">
<div id="passwordOverlay" class="container hidden">
<div class="dialog">
<div class="row">
<p id="passwordText" data-l10n-id="password_label">Enter the password to open this PDF file:</p>
</div>
@ -281,8 +281,8 @@ http://sourceforge.net/adobe/cmap/wiki/License/
</div>
</div>
</div>
<div id="documentPropertiesContainer" class="hidden">
<div class="doorHanger">
<div id="documentPropertiesOverlay" class="container hidden">
<div class="dialog">
<div class="row">
<span data-l10n-id="document_properties_file_name">File name:</span> <p id="fileNameField">-</p>
</div>
@ -322,7 +322,7 @@ http://sourceforge.net/adobe/cmap/wiki/License/
<span data-l10n-id="document_properties_page_count">Page Count:</span> <p id="pageCountField">-</p>
</div>
<div class="buttonRow">
<button id="documentPropertiesClose" class="overlayButton"><span data-l10n-id="document_properties_close">Close</span></button>
<button id="documentPropertiesClose" class="overlayButton"><span data-l10n-id="document_properties_close">Close</span></button>
</div>
</div>
</div>

View File

@ -648,7 +648,7 @@ var ViewHistory = (function ViewHistoryClosure() {
return;
}
this.file[name] = val;
this._writeToStorage();
return this._writeToStorage();
},
setMultiple: function ViewHistory_setMultiple(properties) {
@ -658,7 +658,7 @@ var ViewHistory = (function ViewHistoryClosure() {
for (var name in properties) {
this.file[name] = properties[name];
}
this._writeToStorage();
return this._writeToStorage();
},
get: function ViewHistory_get(name, defaultValue) {
@ -3446,14 +3446,15 @@ var PDFView = {
}
},
renderHighestPriority: function pdfViewRenderHighestPriority() {
renderHighestPriority:
function pdfViewRenderHighestPriority(currentlyVisiblePages) {
if (PDFView.idleTimeout) {
clearTimeout(PDFView.idleTimeout);
PDFView.idleTimeout = null;
}
// Pages have a higher priority than thumbnails, so check them first.
var visiblePages = this.getVisiblePages();
var visiblePages = currentlyVisiblePages || this.getVisiblePages();
var pageView = this.getHighestPriority(visiblePages, this.pages,
this.pageViewScroll.down);
if (pageView) {
@ -4018,12 +4019,13 @@ var PageView = function pageView(container, id, scale,
// the text layer are rotated.
// TODO: This could probably be simplified by drawing the text layer in
// one orientation then rotating overall.
var textLayerViewport = this.textLayer.viewport;
var textRelativeRotation = this.viewport.rotation -
this.textLayer.viewport.rotation;
textLayerViewport.rotation;
var textAbsRotation = Math.abs(textRelativeRotation);
var scale = (width / canvas.width);
var scale = width / textLayerViewport.width;
if (textAbsRotation === 90 || textAbsRotation === 270) {
scale = width / canvas.height;
scale = width / textLayerViewport.height;
}
var textLayerDiv = this.textLayer.textLayerDiv;
var transX, transY;
@ -5373,7 +5375,7 @@ function updateViewarea() {
return;
}
PDFView.renderHighestPriority();
PDFView.renderHighestPriority(visible);
var currentId = PDFView.page;
var firstPage = visible.first;
@ -5430,6 +5432,8 @@ function updateViewarea() {
'zoom': normalizedScaleValue,
'scrollLeft': intLeft,
'scrollTop': intTop
}).catch(function() {
// unable to write to storage
});
});
var href = PDFView.getAnchorUrl(pdfOpenParams);
@ -5809,16 +5813,8 @@ window.addEventListener('afterprint', function afterPrint(evt) {
(function animationStartedClosure() {
// The offsetParent is not set until the pdf.js iframe or object is visible.
// Waiting for first animation.
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function startAtOnce(callback) { callback(); };
PDFView.animationStartedPromise = new Promise(function (resolve) {
requestAnimationFrame(function onAnimationFrame() {
resolve();
});
window.requestAnimationFrame(resolve);
});
})();

View File

@ -376,7 +376,7 @@ BookmarksObserver.prototype = {
this._rootEvents.removeListener(aEvent, aListener);
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarksObserver,
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver,
Ci.nsISupportsWeakReference])
};
@ -495,7 +495,7 @@ Bookmark.prototype = {
},
QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmark,
Ci.nsINavBookmarksObserver,
Ci.nsINavBookmarkObserver,
Ci.nsISupportsWeakReference])
};
@ -667,7 +667,7 @@ BookmarkFolder.prototype = {
},
QueryInterface: XPCOMUtils.generateQI([Ci.fuelIBookmarkFolder,
Ci.nsINavBookmarksObserver,
Ci.nsINavBookmarkObserver,
Ci.nsISupportsWeakReference])
};

View File

@ -866,5 +866,7 @@ bin/libfreebl_32int64_3.so
#ifdef MOZ_ASAN
#ifdef CLANG_CXX
@BINPATH@/llvm-symbolizer
#endif
#endif

View File

@ -320,9 +320,9 @@ HiddenBrowser.prototype = {
// Swap docShells.
tabbrowser.swapNewTabWithBrowser(aTab, this._browser);
// Load all default frame scripts attached to the target window.
// Load all delayed frame scripts attached to the "browers" message manager.
let mm = aTab.linkedBrowser.messageManager;
let scripts = win.messageManager.getDelayedFrameScripts();
let scripts = win.getGroupMessageManager("browsers").getDelayedFrameScripts();
Array.forEach(scripts, ([script, runGlobal]) => mm.loadFrameScript(script, true, runGlobal));
// Remove the browser, it will be recreated by a timer.

View File

@ -1,26 +0,0 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
#inspector-searchbox {
transition-property: max-width, -moz-padding-end, -moz-padding-start;
transition-duration: 250ms;
transition-timing-function: ease;
}
#inspector-searchbox:not([focused]):not([filled]) > .textbox-input-box {
overflow: hidden;
}
#inspector-searchbox:not([focused]):not([filled]) {
max-width: 20px !important;
-moz-padding-end: 5px;
-moz-padding-start: 22px;
background-position: 8px center, top left, top left;
}
#inspector-searchbox[focused],
#inspector-searchbox[filled] {
max-width: 200px !important;
}

View File

@ -182,7 +182,9 @@ browser.jar:
skin/classic/browser/tabview/tabview.png (tabview/tabview.png)
skin/classic/browser/tabview/tabview.css (tabview/tabview.css)
skin/classic/browser/translating-16.png (../shared/translation/translating-16.png)
skin/classic/browser/translating-16@2x.png (../shared/translation/translating-16@2x.png)
skin/classic/browser/translation-16.png (../shared/translation/translation-16.png)
skin/classic/browser/translation-16@2x.png (../shared/translation/translation-16@2x.png)
* skin/classic/browser/devtools/common.css (../shared/devtools/common.css)
* skin/classic/browser/devtools/dark-theme.css (../shared/devtools/dark-theme.css)
* skin/classic/browser/devtools/light-theme.css (../shared/devtools/light-theme.css)
@ -277,7 +279,7 @@ browser.jar:
skin/classic/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
skin/classic/browser/devtools/floating-scrollbars.css (devtools/floating-scrollbars.css)
skin/classic/browser/devtools/floating-scrollbars-light.css (devtools/floating-scrollbars-light.css)
skin/classic/browser/devtools/inspector.css (devtools/inspector.css)
* skin/classic/browser/devtools/inspector.css (../shared/devtools/inspector.css)
skin/classic/browser/devtools/profiler-stopwatch.svg (../shared/devtools/images/profiler-stopwatch.svg)
skin/classic/browser/devtools/profiler-stopwatch-checked.svg (../shared/devtools/images/profiler-stopwatch-checked.svg)
skin/classic/browser/devtools/tool-options.svg (../shared/devtools/images/tool-options.svg)

View File

@ -397,7 +397,7 @@ browser.jar:
skin/classic/browser/devtools/toggle-tools.png (../shared/devtools/images/toggle-tools.png)
skin/classic/browser/devtools/dock-bottom@2x.png (../shared/devtools/images/dock-bottom@2x.png)
skin/classic/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
* skin/classic/browser/devtools/inspector.css (devtools/inspector.css)
* skin/classic/browser/devtools/inspector.css (../shared/devtools/inspector.css)
skin/classic/browser/devtools/profiler-stopwatch.svg (../shared/devtools/images/profiler-stopwatch.svg)
skin/classic/browser/devtools/profiler-stopwatch-checked.svg (../shared/devtools/images/profiler-stopwatch-checked.svg)
skin/classic/browser/devtools/tool-options.svg (../shared/devtools/images/tool-options.svg)

View File

@ -184,14 +184,6 @@
margin-bottom: -4px;
}
/* Tooltip: Font Family Previewer Text */
.devtools-tooltip-font-previewer-text {
max-width: 400px;
line-height: 1.5;
font-size: 150%;
text-align: center;
}
/* Tooltip: Alert Icon */
.devtools-tooltip-alert-icon {

View File

@ -349,10 +349,6 @@ div.CodeMirror span.eval-text {
border-bottom: 1px solid #434850;
}
.theme-tooltip-panel .devtools-tooltip-font-previewer-text {
color: white;
}
.theme-tooltip-panel .devtools-tooltip-simple-text:last-child {
border-bottom: 0;
}

View File

@ -3,8 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
%include ../shared.inc
%filter substitution
%ifdef XP_MACOSX
#inspector-toolbar {
padding-top: 4px;
@ -21,6 +20,8 @@
padding-right: 18px; /* use -moz-padding-end when/if bug 631729 gets fixed */
}
%endif
#inspector-searchbox {
transition-property: max-width, -moz-padding-end, -moz-padding-start;
transition-duration: 250ms;

View File

@ -358,10 +358,6 @@ div.CodeMirror span.eval-text {
border-bottom: 1px solid #d9e1e8;
}
.theme-tooltip-panel .devtools-tooltip-font-previewer-text {
color: black;
}
.theme-tooltip-panel .devtools-tooltip-simple-text:last-child {
border-bottom: 0;
}

View File

@ -1,26 +0,0 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
#inspector-searchbox {
transition-property: max-width, -moz-padding-end, -moz-padding-start;
transition-duration: 250ms;
transition-timing-function: ease;
}
#inspector-searchbox:not([focused]):not([filled]) > .textbox-input-box {
overflow: hidden;
}
#inspector-searchbox:not([focused]):not([filled]) {
max-width: 20px !important;
-moz-padding-end: 6px;
-moz-padding-start: 22px;
background-position: 8px center, top left, top left;
}
#inspector-searchbox[focused],
#inspector-searchbox[filled] {
max-width: 200px !important;
}

View File

@ -219,7 +219,9 @@ browser.jar:
skin/classic/browser/tabview/tabview-inverted.png (tabview/tabview-inverted.png)
skin/classic/browser/tabview/tabview.css (tabview/tabview.css)
skin/classic/browser/translating-16.png (../shared/translation/translating-16.png)
skin/classic/browser/translating-16@2x.png (../shared/translation/translating-16@2x.png)
skin/classic/browser/translation-16.png (../shared/translation/translation-16.png)
skin/classic/browser/translation-16@2x.png (../shared/translation/translation-16@2x.png)
* skin/classic/browser/devtools/common.css (../shared/devtools/common.css)
* skin/classic/browser/devtools/dark-theme.css (../shared/devtools/dark-theme.css)
* skin/classic/browser/devtools/light-theme.css (../shared/devtools/light-theme.css)
@ -315,7 +317,7 @@ browser.jar:
skin/classic/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
skin/classic/browser/devtools/floating-scrollbars.css (devtools/floating-scrollbars.css)
skin/classic/browser/devtools/floating-scrollbars-light.css (devtools/floating-scrollbars-light.css)
skin/classic/browser/devtools/inspector.css (devtools/inspector.css)
* skin/classic/browser/devtools/inspector.css (../shared/devtools/inspector.css)
skin/classic/browser/devtools/profiler-stopwatch.svg (../shared/devtools/images/profiler-stopwatch.svg)
skin/classic/browser/devtools/profiler-stopwatch-checked.svg (../shared/devtools/images/profiler-stopwatch-checked.svg)
skin/classic/browser/devtools/tool-options.svg (../shared/devtools/images/tool-options.svg)
@ -610,7 +612,9 @@ browser.jar:
skin/classic/aero/browser/tabview/tabview-inverted.png (tabview/tabview-inverted.png)
skin/classic/aero/browser/tabview/tabview.css (tabview/tabview.css)
skin/classic/aero/browser/translating-16.png (../shared/translation/translating-16.png)
skin/classic/aero/browser/translating-16@2x.png (../shared/translation/translating-16@2x.png)
skin/classic/aero/browser/translation-16.png (../shared/translation/translation-16.png)
skin/classic/aero/browser/translation-16@2x.png (../shared/translation/translation-16@2x.png)
* skin/classic/aero/browser/devtools/common.css (../shared/devtools/common.css)
* skin/classic/aero/browser/devtools/dark-theme.css (../shared/devtools/dark-theme.css)
* skin/classic/aero/browser/devtools/light-theme.css (../shared/devtools/light-theme.css)
@ -706,7 +710,7 @@ browser.jar:
skin/classic/aero/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
skin/classic/aero/browser/devtools/floating-scrollbars.css (devtools/floating-scrollbars.css)
skin/classic/aero/browser/devtools/floating-scrollbars-light.css (devtools/floating-scrollbars-light.css)
skin/classic/aero/browser/devtools/inspector.css (devtools/inspector.css)
* skin/classic/aero/browser/devtools/inspector.css (../shared/devtools/inspector.css)
skin/classic/aero/browser/devtools/profiler-stopwatch.svg (../shared/devtools/images/profiler-stopwatch.svg)
skin/classic/aero/browser/devtools/profiler-stopwatch-checked.svg (../shared/devtools/images/profiler-stopwatch-checked.svg)
skin/classic/aero/browser/devtools/tool-options.svg (../shared/devtools/images/tool-options.svg)

View File

@ -64,7 +64,7 @@ case "$target" in
kernel_name=`uname -s | tr "[[:upper:]]" "[[:lower:]]"`
for version in $android_gnu_compiler_version 4.7 4.6 4.4.3 ; do
for version in $android_gnu_compiler_version 4.8 4.7 4.6 4.4.3; do
case "$target_cpu" in
arm)
target_name=arm-linux-androideabi-$version

View File

@ -39,6 +39,8 @@ interface nsIPrincipal;
* - "frame message managers" which correspond to frame elements
* - "window message managers" which correspond to top-level chrome
* windows
* - "group message managers" which correspond to named message
* managers with a specific window MM as the parent
* - the "global message manager", on the parent side. See below.
*
* The DOM-realm message managers can communicate in the ways shown by
@ -54,6 +56,16 @@ interface nsIPrincipal;
* | +-->frame MMp1_1<------------>frame MMc1_1
* | |
* | +-->frame MMp1_2<------------>frame MMc1_2
* | |
* | +-->group MMgr1
* | | |
* | | +-->frame MMp2_1<------->frame MMc2_1
* | | |
* | | +-->frame MMp2_2<------->frame MMc2_2
* | |
* | +-->group MMgr2
* | | ...
* | |
* | ...
* |
* +-->window MMw2
@ -64,17 +76,23 @@ interface nsIPrincipal;
* message managers in the hierarchy above MMp1_1, in this diagram
* MMw1 and MMg, will also notify their message listeners when the
* message arrives.
*
* A message sent from MMc2_1 will be sent to MMp2_1 and also notify
* all message managers in the hierarchy above that, including the
* group message manager MMgr1.
* For example: a message broadcast through the global MMg on the
* parent side would be broadcast to MMw1, which would transitively
* broadcast it to MMp1_1, MM1p_2". The message would next be
* broadcast to MMw2, and so on down the hierarchy.
* broadcast it to MMp1_1, MM1p_2. The message would next be
* broadcast to MMgr1, which would broadcast it to MMp2_1 and MMp2_2.
* After that it would broadcast to MMgr2 and then to MMw2, and so
* on down the hierarchy.
*
* ***** PERFORMANCE AND SECURITY WARNING *****
* Messages broadcast through the global MM and window MMs can result
* in messages being dispatched across many OS processes, and to many
* processes with different permissions. Great care should be taken
* when broadcasting.
* Messages broadcast through the global MM and window or group MMs
* can result in messages being dispatched across many OS processes,
* and to many processes with different permissions. Great care
* should be taken when broadcasting.
*
* Interfaces
* ----------

View File

@ -143,6 +143,8 @@ already_AddRefed<nsIDocument>
DOMParser::ParseFromBuffer(const Uint8Array& aBuf, uint32_t aBufLen,
SupportedType aType, ErrorResult& rv)
{
aBuf.ComputeLengthAndData();
if (aBufLen > aBuf.Length()) {
rv.Throw(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY);
return nullptr;

View File

@ -1212,7 +1212,10 @@ WebSocket::Send(const ArrayBuffer& aData,
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
MOZ_ASSERT(sizeof(*aData.Data()) == 1);
aData.ComputeLengthAndData();
static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
uint32_t len = aData.Length();
char* data = reinterpret_cast<char*>(aData.Data());
@ -1226,7 +1229,10 @@ WebSocket::Send(const ArrayBufferView& aData,
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
MOZ_ASSERT(sizeof(*aData.Data()) == 1);
aData.ComputeLengthAndData();
static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
uint32_t len = aData.Length();
char* data = reinterpret_cast<char*>(aData.Data());

View File

@ -303,7 +303,10 @@ nsDOMDataChannel::Send(const ArrayBuffer& aData, ErrorResult& aRv)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
MOZ_ASSERT(sizeof(*aData.Data()) == 1);
aData.ComputeLengthAndData();
static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
uint32_t len = aData.Length();
char* data = reinterpret_cast<char*>(aData.Data());
@ -316,7 +319,10 @@ nsDOMDataChannel::Send(const ArrayBufferView& aData, ErrorResult& aRv)
{
NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
MOZ_ASSERT(sizeof(*aData.Data()) == 1);
aData.ComputeLengthAndData();
static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
uint32_t len = aData.Length();
char* data = reinterpret_cast<char*>(aData.Data());

View File

@ -2529,6 +2529,7 @@ GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult, uint64_t* aContentLe
JS::Rooted<JSObject*> obj(cx, realVal.toObjectOrNull());
if (JS_IsArrayBufferObject(obj)) {
ArrayBuffer buf(obj);
buf.ComputeLengthAndData();
return GetRequestBody(buf.Data(), buf.Length(), aResult,
aContentLength, aContentType, aCharset);
}
@ -2572,14 +2573,16 @@ nsXMLHttpRequest::GetRequestBody(nsIVariant* aVariant,
switch (body.GetType()) {
case nsXMLHttpRequest::RequestBody::ArrayBuffer:
{
return ::GetRequestBody(value.mArrayBuffer->Data(),
value.mArrayBuffer->Length(), aResult,
const ArrayBuffer* buffer = value.mArrayBuffer;
buffer->ComputeLengthAndData();
return ::GetRequestBody(buffer->Data(), buffer->Length(), aResult,
aContentLength, aContentType, aCharset);
}
case nsXMLHttpRequest::RequestBody::ArrayBufferView:
{
return ::GetRequestBody(value.mArrayBufferView->Data(),
value.mArrayBufferView->Length(), aResult,
const ArrayBufferView* view = value.mArrayBufferView;
view->ComputeLengthAndData();
return ::GetRequestBody(view->Data(), view->Length(), aResult,
aContentLength, aContentType, aCharset);
}
case nsXMLHttpRequest::RequestBody::Blob:
@ -3583,15 +3586,11 @@ nsXMLHttpRequest::OnProgress(nsIRequest *aRequest, nsISupports *aContext, uint64
// So, try to remove the headers, if possible.
bool lengthComputable = (aProgressMax != UINT64_MAX);
if (upload) {
uint64_t loaded = aProgress;
uint64_t total = aProgressMax;
mUploadTransferred = aProgress;
if (lengthComputable) {
uint64_t headerSize = aProgressMax - mUploadTotal;
loaded -= headerSize;
total -= headerSize;
mUploadTransferred = aProgressMax - mUploadTotal;
}
mUploadLengthComputable = lengthComputable;
mUploadTransferred = loaded;
mProgressSinceLastProgressEvent = true;
MaybeDispatchProgressEvents(false);

View File

@ -4010,7 +4010,7 @@ CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
imageData.Width(), imageData.Height(),
arr.Data(), arr.Length(), false, 0, 0, 0, 0);
&arr, false, 0, 0, 0, 0);
}
void
@ -4024,7 +4024,7 @@ CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
imageData.Width(), imageData.Height(),
arr.Data(), arr.Length(), true,
&arr, true,
JS_DoubleToInt32(dirtyX),
JS_DoubleToInt32(dirtyY),
JS_DoubleToInt32(dirtyWidth),
@ -4036,7 +4036,7 @@ CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
nsresult
CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
unsigned char *aData, uint32_t aDataLen,
dom::Uint8ClampedArray* aArray,
bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
int32_t dirtyWidth, int32_t dirtyHeight)
{
@ -4089,8 +4089,12 @@ CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w
return NS_OK;
}
aArray->ComputeLengthAndData();
uint32_t dataLen = aArray->Length();
uint32_t len = w * h * 4;
if (aDataLen != len) {
if (dataLen != len) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
@ -4101,7 +4105,7 @@ CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w
return NS_ERROR_FAILURE;
}
uint8_t *src = aData;
uint8_t *src = aArray->Data();
uint8_t *dst = imgsurf->Data();
for (uint32_t j = 0; j < h; j++) {

View File

@ -551,7 +551,7 @@ protected:
JSObject** aRetval);
nsresult PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
unsigned char *aData, uint32_t aDataLen,
dom::Uint8ClampedArray* aArray,
bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
int32_t dirtyWidth, int32_t dirtyHeight);

View File

@ -70,6 +70,8 @@ ImageData::Constructor(const GlobalObject& aGlobal,
const Optional<uint32_t>& aHeight,
ErrorResult& aRv)
{
aData.ComputeLengthAndData();
uint32_t length = aData.Length();
if (length == 0 || length % 4) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);

View File

@ -83,18 +83,21 @@ WebGLMemoryPressureObserver::Observe(nsISupports* aSubject,
if (!mContext->mCanLoseContextInForeground &&
ProcessPriorityManager::CurrentProcessIsForeground())
{
wantToLoseContext = false;
else if (!nsCRT::strcmp(aSomeData,
MOZ_UTF16("heap-minimize")))
} else if (!nsCRT::strcmp(aSomeData,
MOZ_UTF16("heap-minimize")))
{
wantToLoseContext = mContext->mLoseContextOnHeapMinimize;
}
if (wantToLoseContext)
if (wantToLoseContext) {
mContext->ForceLoseContext();
}
return NS_OK;
}
WebGLContextOptions::WebGLContextOptions()
: alpha(true), depth(true), stencil(false),
premultipliedAlpha(true), antialias(true),
@ -168,12 +171,12 @@ WebGLContext::WebGLContext()
WebGLMemoryTracker::AddWebGLContext(this);
mAllowRestore = true;
mAllowContextRestore = true;
mLastLossWasSimulated = false;
mContextLossTimerRunning = false;
mDrawSinceContextLossTimerSet = false;
mRunContextLossTimerAgain = false;
mContextRestorer = do_CreateInstance("@mozilla.org/timer;1");
mContextStatus = ContextNotLost;
mContextLostErrorSet = false;
mLoseContextOnHeapMinimize = false;
mCanLoseContextInForeground = true;
@ -571,11 +574,8 @@ WebGLContext::SetDimensions(int32_t width, int32_t height)
mResetLayer = true;
mOptionsFrozen = true;
mHasRobustness = gl->HasRobustness();
// increment the generation number
++mGeneration;
#if 0
if (mGeneration > 0) {
// XXX dispatch context lost event
@ -1110,6 +1110,96 @@ WebGLContext::DummyFramebufferOperation(const char *info)
ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
}
static bool
CheckContextLost(GLContext* gl, bool* out_isGuilty)
{
MOZ_ASSERT(gl);
MOZ_ASSERT(out_isGuilty);
bool isEGL = gl->GetContextType() == gl::GLContextType::EGL;
GLenum resetStatus = LOCAL_GL_NO_ERROR;
if (gl->HasRobustness()) {
gl->MakeCurrent();
resetStatus = gl->fGetGraphicsResetStatus();
} else if (isEGL) {
// Simulate a ARB_robustness guilty context loss for when we
// get an EGL_CONTEXT_LOST error. It may not actually be guilty,
// but we can't make any distinction.
if (!gl->MakeCurrent(true) && gl->IsContextLost()) {
resetStatus = LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB;
}
}
if (resetStatus == LOCAL_GL_NO_ERROR) {
*out_isGuilty = false;
return false;
}
// Assume guilty unless we find otherwise!
bool isGuilty = true;
switch (resetStatus) {
case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB:
// Either nothing wrong, or not our fault.
isGuilty = false;
break;
case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB:
NS_WARNING("WebGL content on the page definitely caused the graphics"
" card to reset.");
break;
case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB:
NS_WARNING("WebGL content on the page might have caused the graphics"
" card to reset");
// If we can't tell, assume guilty.
break;
default:
MOZ_ASSERT(false, "Unreachable.");
// If we do get here, let's pretend to be guilty as an escape plan.
break;
}
if (isGuilty) {
NS_WARNING("WebGL context on this page is considered guilty, and will"
" not be restored.");
}
*out_isGuilty = isGuilty;
return true;
}
bool
WebGLContext::TryToRestoreContext()
{
if (NS_FAILED(SetDimensions(mWidth, mHeight)))
return false;
return true;
}
class UpdateContextLossStatusTask : public nsRunnable
{
WebGLContext* const mContext;
public:
UpdateContextLossStatusTask(WebGLContext* context)
: mContext(context)
{
}
NS_IMETHOD Run() {
mContext->UpdateContextLossStatus();
return NS_OK;
}
};
void
WebGLContext::EnqueueUpdateContextLossStatus()
{
nsCOMPtr<nsIRunnable> task = new UpdateContextLossStatusTask(this);
NS_DispatchToCurrentThread(task);
}
// We use this timer for many things. Here are the things that it is activated for:
// 1) If a script is using the MOZ_WEBGL_lose_context extension.
// 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
@ -1125,137 +1215,123 @@ WebGLContext::DummyFramebufferOperation(const char *info)
// At a bare minimum, from context lost to context restores, it would take 3
// full timer iterations: detection, webglcontextlost, webglcontextrestored.
void
WebGLContext::RobustnessTimerCallback(nsITimer* timer)
WebGLContext::UpdateContextLossStatus()
{
TerminateContextLossTimer();
if (!mCanvasElement) {
// the canvas is gone. That happens when the page was closed before we got
// this timer event. In this case, there's nothing to do here, just don't crash.
return;
}
if (mContextStatus == ContextNotLost) {
// We don't know that we're lost, but we might be, so we need to
// check. If we're guilty, don't allow restores, though.
bool isGuilty = true;
bool isContextLost = CheckContextLost(gl, &isGuilty);
if (isContextLost) {
if (isGuilty)
mAllowContextRestore = false;
ForceLoseContext();
}
// Fall through.
}
// If the context has been lost and we're waiting for it to be restored, do
// that now.
if (mContextStatus == ContextLostAwaitingEvent) {
bool defaultAction;
// The context has been lost and we haven't yet triggered the
// callback, so do that now.
bool useDefaultHandler;
nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
NS_LITERAL_STRING("webglcontextlost"),
true,
true,
&defaultAction);
&useDefaultHandler);
// We sent the callback, so we're just 'regular lost' now.
mContextStatus = ContextLost;
// If we're told to use the default handler, it means the script
// didn't bother to handle the event. In this case, we shouldn't
// auto-restore the context.
if (useDefaultHandler)
mAllowContextRestore = false;
// If the script didn't handle the event, we don't allow restores.
if (defaultAction)
mAllowRestore = false;
// Fall through.
}
// If the script handled the event and we are allowing restores, then
// mark it to be restored. Otherwise, leave it as context lost
// (unusable).
if (!defaultAction && mAllowRestore) {
ForceRestoreContext();
// Restart the timer so that it will be restored on the next
// callback.
SetupContextLossTimer();
} else {
if (mContextStatus == ContextLost) {
// Context is lost, and we've already sent the callback. We
// should try to restore the context if we're both allowed to,
// and supposed to.
// Are we allowed to restore the context?
if (!mAllowContextRestore)
return;
// If we're only simulated-lost, we shouldn't auto-restore, and
// instead we should wait for restoreContext() to be called.
if (mLastLossWasSimulated)
return;
ForceRestoreContext();
return;
}
if (mContextStatus == ContextLostAwaitingRestore) {
// Context is lost, but we should try to restore it.
if (!mAllowContextRestore) {
// We might decide this after thinking we'd be OK restoring
// the context, so downgrade.
mContextStatus = ContextLost;
}
} else if (mContextStatus == ContextLostAwaitingRestore) {
// Try to restore the context. If it fails, try again later.
if (NS_FAILED(SetDimensions(mWidth, mHeight))) {
SetupContextLossTimer();
return;
}
if (!TryToRestoreContext()) {
// Failed to restore. Try again later.
RunContextLossTimer();
return;
}
// Revival!
mContextStatus = ContextNotLost;
nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
NS_LITERAL_STRING("webglcontextrestored"),
true,
true);
// Set all flags back to the state they were in before the context was
// lost.
mEmitContextLostErrorOnce = true;
mAllowRestore = true;
}
MaybeRestoreContext();
return;
}
void
WebGLContext::MaybeRestoreContext()
{
// Don't try to handle it if we already know it's busted.
if (mContextStatus != ContextNotLost || gl == nullptr)
return;
bool isEGL = gl->GetContextType() == gl::GLContextType::EGL,
isANGLE = gl->IsANGLE();
GLContext::ContextResetARB resetStatus = GLContext::CONTEXT_NO_ERROR;
if (mHasRobustness) {
gl->MakeCurrent();
resetStatus = (GLContext::ContextResetARB) gl->fGetGraphicsResetStatus();
} else if (isEGL) {
// Simulate a ARB_robustness guilty context loss for when we
// get an EGL_CONTEXT_LOST error. It may not actually be guilty,
// but we can't make any distinction, so we must assume the worst
// case.
if (!gl->MakeCurrent(true) && gl->IsContextLost()) {
resetStatus = GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB;
}
}
if (resetStatus != GLContext::CONTEXT_NO_ERROR) {
// It's already lost, but clean up after it and signal to JS that it is
// lost.
ForceLoseContext();
}
switch (resetStatus) {
case GLContext::CONTEXT_NO_ERROR:
// If there has been activity since the timer was set, it's possible
// that we did or are going to miss something, so clear this flag and
// run it again some time later.
if (mDrawSinceContextLossTimerSet)
SetupContextLossTimer();
break;
case GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB:
NS_WARNING("WebGL content on the page caused the graphics card to reset; not restoring the context");
mAllowRestore = false;
break;
case GLContext::CONTEXT_INNOCENT_CONTEXT_RESET_ARB:
break;
case GLContext::CONTEXT_UNKNOWN_CONTEXT_RESET_ARB:
NS_WARNING("WebGL content on the page might have caused the graphics card to reset");
if (isEGL && isANGLE) {
// If we're using ANGLE, we ONLY get back UNKNOWN context resets, including for guilty contexts.
// This means that we can't restore it or risk restoring a guilty context. Should this ever change,
// we can get rid of the whole IsANGLE() junk from GLContext.h since, as of writing, this is the
// only use for it. See ANGLE issue 261.
mAllowRestore = false;
}
break;
}
}
void
WebGLContext::ForceLoseContext()
{
if (mContextStatus == ContextLostAwaitingEvent)
return;
printf_stderr("WebGL(%p)::ForceLoseContext\n", this);
MOZ_ASSERT(!IsContextLost());
mContextStatus = ContextLostAwaitingEvent;
// Queue up a task to restore the event.
SetupContextLossTimer();
mContextLostErrorSet = false;
mLastLossWasSimulated = false;
// Burn it all!
DestroyResourcesAndContext();
// Queue up a task, since we know the status changed.
EnqueueUpdateContextLossStatus();
}
void
WebGLContext::ForceRestoreContext()
{
printf_stderr("WebGL(%p)::ForceRestoreContext\n", this);
mContextStatus = ContextLostAwaitingRestore;
mAllowContextRestore = true; // Hey, you did say 'force'.
// Queue up a task, since we know the status changed.
EnqueueUpdateContextLossStatus();
}
void

View File

@ -202,9 +202,6 @@ public:
int32_t x, int32_t y, int32_t w, int32_t h)
{ return NS_ERROR_NOT_IMPLEMENTED; }
bool LoseContext();
bool RestoreContext();
void SynthesizeGLError(GLenum err);
void SynthesizeGLError(GLenum err, const char *fmt, ...);
@ -261,11 +258,14 @@ public:
bool MinCapabilityMode() const { return mMinCapability; }
void RobustnessTimerCallback(nsITimer* timer);
static void RobustnessTimerCallbackStatic(nsITimer* timer, void *thisPointer);
void SetupContextLossTimer();
void UpdateContextLossStatus();
void EnqueueUpdateContextLossStatus();
static void ContextLossCallbackStatic(nsITimer* timer, void* thisPointer);
void RunContextLossTimer();
void TerminateContextLossTimer();
bool TryToRestoreContext();
void AssertCachedBindings();
void AssertCachedState();
@ -511,6 +511,7 @@ public:
void Uniform1iv(WebGLUniformLocation* location,
const dom::Int32Array& arr) {
arr.ComputeLengthAndData();
Uniform1iv_base(location, arr.Length(), arr.Data());
}
void Uniform1iv(WebGLUniformLocation* location,
@ -522,6 +523,7 @@ public:
void Uniform2iv(WebGLUniformLocation* location,
const dom::Int32Array& arr) {
arr.ComputeLengthAndData();
Uniform2iv_base(location, arr.Length(), arr.Data());
}
void Uniform2iv(WebGLUniformLocation* location,
@ -533,6 +535,7 @@ public:
void Uniform3iv(WebGLUniformLocation* location,
const dom::Int32Array& arr) {
arr.ComputeLengthAndData();
Uniform3iv_base(location, arr.Length(), arr.Data());
}
void Uniform3iv(WebGLUniformLocation* location,
@ -544,6 +547,7 @@ public:
void Uniform4iv(WebGLUniformLocation* location,
const dom::Int32Array& arr) {
arr.ComputeLengthAndData();
Uniform4iv_base(location, arr.Length(), arr.Data());
}
void Uniform4iv(WebGLUniformLocation* location,
@ -555,6 +559,7 @@ public:
void Uniform1fv(WebGLUniformLocation* location,
const dom::Float32Array& arr) {
arr.ComputeLengthAndData();
Uniform1fv_base(location, arr.Length(), arr.Data());
}
void Uniform1fv(WebGLUniformLocation* location,
@ -566,6 +571,7 @@ public:
void Uniform2fv(WebGLUniformLocation* location,
const dom::Float32Array& arr) {
arr.ComputeLengthAndData();
Uniform2fv_base(location, arr.Length(), arr.Data());
}
void Uniform2fv(WebGLUniformLocation* location,
@ -577,6 +583,7 @@ public:
void Uniform3fv(WebGLUniformLocation* location,
const dom::Float32Array& arr) {
arr.ComputeLengthAndData();
Uniform3fv_base(location, arr.Length(), arr.Data());
}
void Uniform3fv(WebGLUniformLocation* location,
@ -588,6 +595,7 @@ public:
void Uniform4fv(WebGLUniformLocation* location,
const dom::Float32Array& arr) {
arr.ComputeLengthAndData();
Uniform4fv_base(location, arr.Length(), arr.Data());
}
void Uniform4fv(WebGLUniformLocation* location,
@ -600,6 +608,7 @@ public:
void UniformMatrix2fv(WebGLUniformLocation* location,
WebGLboolean transpose,
const dom::Float32Array &value) {
value.ComputeLengthAndData();
UniformMatrix2fv_base(location, transpose, value.Length(), value.Data());
}
void UniformMatrix2fv(WebGLUniformLocation* location,
@ -615,6 +624,7 @@ public:
void UniformMatrix3fv(WebGLUniformLocation* location,
WebGLboolean transpose,
const dom::Float32Array &value) {
value.ComputeLengthAndData();
UniformMatrix3fv_base(location, transpose, value.Length(), value.Data());
}
void UniformMatrix3fv(WebGLUniformLocation* location,
@ -630,6 +640,7 @@ public:
void UniformMatrix4fv(WebGLUniformLocation* location,
WebGLboolean transpose,
const dom::Float32Array &value) {
value.ComputeLengthAndData();
UniformMatrix4fv_base(location, transpose, value.Length(), value.Data());
}
void UniformMatrix4fv(WebGLUniformLocation* location,
@ -655,8 +666,12 @@ public:
bool ValidateSamplerUniformSetter(const char* info,
WebGLUniformLocation *location,
GLint value);
void Viewport(GLint x, GLint y, GLsizei width, GLsizei height);
// -----------------------------------------------------------------------------
// WEBGL_lose_context
public:
void LoseContext();
void RestoreContext();
// -----------------------------------------------------------------------------
// Asynchronous Queries (WebGLContextAsyncQueries.cpp)
@ -753,6 +768,7 @@ public:
GLfloat x2, GLfloat x3);
void VertexAttrib1fv(GLuint idx, const dom::Float32Array &arr) {
arr.ComputeLengthAndData();
VertexAttrib1fv_base(idx, arr.Length(), arr.Data());
}
void VertexAttrib1fv(GLuint idx, const dom::Sequence<GLfloat>& arr) {
@ -760,6 +776,7 @@ public:
}
void VertexAttrib2fv(GLuint idx, const dom::Float32Array &arr) {
arr.ComputeLengthAndData();
VertexAttrib2fv_base(idx, arr.Length(), arr.Data());
}
void VertexAttrib2fv(GLuint idx, const dom::Sequence<GLfloat>& arr) {
@ -767,6 +784,7 @@ public:
}
void VertexAttrib3fv(GLuint idx, const dom::Float32Array &arr) {
arr.ComputeLengthAndData();
VertexAttrib3fv_base(idx, arr.Length(), arr.Data());
}
void VertexAttrib3fv(GLuint idx, const dom::Sequence<GLfloat>& arr) {
@ -774,6 +792,7 @@ public:
}
void VertexAttrib4fv(GLuint idx, const dom::Float32Array &arr) {
arr.ComputeLengthAndData();
VertexAttrib4fv_base(idx, arr.Length(), arr.Data());
}
void VertexAttrib4fv(GLuint idx, const dom::Sequence<GLfloat>& arr) {
@ -854,7 +873,6 @@ protected:
bool mOptionsFrozen;
bool mMinCapability;
bool mDisableExtensions;
bool mHasRobustness;
bool mIsMesa;
bool mLoseContextOnHeapMinimize;
bool mCanLoseContextInForeground;
@ -1101,7 +1119,6 @@ protected:
GLenum type,
const GLvoid *data);
void MaybeRestoreContext();
void ForceLoseContext();
void ForceRestoreContext();
@ -1163,7 +1180,7 @@ protected:
GLint mStencilRefFront, mStencilRefBack;
GLuint mStencilValueMaskFront, mStencilValueMaskBack,
mStencilWriteMaskFront, mStencilWriteMaskBack;
mStencilWriteMaskFront, mStencilWriteMaskBack;
realGLboolean mColorWriteMask[4];
realGLboolean mDepthWriteMask;
GLfloat mColorClearValue[4];
@ -1177,9 +1194,10 @@ protected:
bool mAlreadyWarnedAboutViewportLargerThanDest;
nsCOMPtr<nsITimer> mContextRestorer;
bool mAllowRestore;
bool mAllowContextRestore;
bool mLastLossWasSimulated;
bool mContextLossTimerRunning;
bool mDrawSinceContextLossTimerSet;
bool mRunContextLossTimerAgain;
ContextStatus mContextStatus;
bool mContextLostErrorSet;

View File

@ -203,8 +203,10 @@ WebGLContext::BufferData(GLenum target,
}
const ArrayBuffer& data = maybeData.Value();
data.ComputeLengthAndData();
// careful: data.Length() could conceivably be any size_t, but GLsizeiptr is like intptr_t.
// Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
// is like intptr_t.
if (!CheckedInt<GLsizeiptr>(data.Length()).isValid())
return ErrorOutOfMemory("bufferData: bad size");
@ -253,7 +255,10 @@ WebGLContext::BufferData(GLenum target, const ArrayBufferView& data,
if (!boundBuffer)
return ErrorInvalidOperation("bufferData: no buffer bound!");
// careful: data.Length() could conceivably be any size_t, but GLsizeiptr is like intptr_t.
data.ComputeLengthAndData();
// Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
// is like intptr_t.
if (!CheckedInt<GLsizeiptr>(data.Length()).isValid())
return ErrorOutOfMemory("bufferData: bad size");
@ -290,8 +295,6 @@ WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset,
return;
}
const ArrayBuffer& data = maybeData.Value();
if (byteOffset < 0)
return ErrorInvalidValue("bufferSubData: negative offset");
@ -300,6 +303,9 @@ WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset,
if (!boundBuffer)
return ErrorInvalidOperation("bufferData: no buffer bound!");
const ArrayBuffer& data = maybeData.Value();
data.ComputeLengthAndData();
CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(byteOffset) + data.Length();
if (!checked_neededByteLength.isValid())
return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length");
@ -336,6 +342,8 @@ WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr byteOffset,
if (!boundBuffer)
return ErrorInvalidOperation("bufferSubData: no buffer bound!");
data.ComputeLengthAndData();
CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(byteOffset) + data.Length();
if (!checked_neededByteLength.isValid())
return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length");

View File

@ -128,7 +128,7 @@ WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei count)
if (!DrawArrays_check(first, count, 1, "drawArrays"))
return;
SetupContextLossTimer();
RunContextLossTimer();
gl->fDrawArrays(mode, first, count);
Draw_cleanup();
@ -149,7 +149,7 @@ WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsiz
if (!DrawInstanced_check("drawArraysInstanced"))
return;
SetupContextLossTimer();
RunContextLossTimer();
gl->fDrawArraysInstanced(mode, first, count, primcount);
Draw_cleanup();
@ -296,7 +296,7 @@ WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type,
return;
}
SetupContextLossTimer();
RunContextLossTimer();
if (gl->IsSupported(gl::GLFeature::draw_range_elements)) {
gl->fDrawRangeElements(mode, 0, upperBound,
@ -324,7 +324,7 @@ WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
if (!DrawInstanced_check("drawElementsInstanced"))
return;
SetupContextLossTimer();
RunContextLossTimer();
gl->fDrawElementsInstanced(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset), primcount);
Draw_cleanup();

View File

@ -2126,7 +2126,8 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
if (!isReadTypeValid)
return ErrorInvalidEnum("readPixels: Bad type", type);
int dataType = JS_GetArrayBufferViewType(pixels.Value().Obj());
const ArrayBufferView& pixbuf = pixels.Value();
int dataType = JS_GetArrayBufferViewType(pixbuf.Obj());
// Check the pixels param type
if (dataType != requiredDataType)
@ -2144,11 +2145,15 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
if (!checked_neededByteLength.isValid())
return ErrorInvalidOperation("readPixels: integer overflow computing the needed buffer size");
uint32_t dataByteLen = JS_GetTypedArrayByteLength(pixels.Value().Obj());
// Compute length and data. Don't reenter after this point, lest the
// precomputed go out of sync with the instant length/data.
pixbuf.ComputeLengthAndData();
uint32_t dataByteLen = pixbuf.Length();
if (checked_neededByteLength.value() > dataByteLen)
return ErrorInvalidOperation("readPixels: buffer too small");
void* data = pixels.Value().Data();
void* data = pixbuf.Data();
if (!data) {
ErrorOutOfMemory("readPixels: buffer storage is null. Did we run out of memory?");
return rv.Throw(NS_ERROR_OUT_OF_MEMORY);
@ -3261,6 +3266,8 @@ WebGLContext::CompressedTexImage2D(GLenum target, GLint level, GLenum internalfo
return;
}
view.ComputeLengthAndData();
uint32_t byteLength = view.Length();
if (!ValidateCompTexImageDataSize(target, internalformat, width, height, byteLength, func)) {
return;
@ -3304,6 +3311,8 @@ WebGLContext::CompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset,
MOZ_ASSERT(tex);
WebGLTexture::ImageInfo& levelInfo = tex->ImageInfoAt(target, level);
view.ComputeLengthAndData();
uint32_t byteLength = view.Length();
if (!ValidateCompTexImageDataSize(target, format, width, height, byteLength, func))
return;
@ -3670,10 +3679,24 @@ WebGLContext::TexImage2D(GLenum target, GLint level,
if (IsContextLost())
return;
void* data;
uint32_t length;
int jsArrayType;
if (pixels.IsNull()) {
data = nullptr;
length = 0;
jsArrayType = -1;
} else {
const ArrayBufferView& view = pixels.Value();
view.ComputeLengthAndData();
data = view.Data();
length = view.Length();
jsArrayType = int(JS_GetArrayBufferViewType(view.Obj()));
}
return TexImage2D_base(target, level, internalformat, width, height, 0, border, format, type,
pixels.IsNull() ? 0 : pixels.Value().Data(),
pixels.IsNull() ? 0 : pixels.Value().Length(),
pixels.IsNull() ? -1 : (int)JS_GetArrayBufferViewType(pixels.Value().Obj()),
data, length, jsArrayType,
WebGLTexelFormat::Auto, false);
}
@ -3691,6 +3714,8 @@ WebGLContext::TexImage2D(GLenum target, GLint level,
}
Uint8ClampedArray arr(pixels->GetDataObject());
arr.ComputeLengthAndData();
return TexImage2D_base(target, level, internalformat, pixels->Width(),
pixels->Height(), 4*pixels->Width(), 0,
format, type, arr.Data(), arr.Length(), -1,
@ -3799,10 +3824,13 @@ WebGLContext::TexSubImage2D(GLenum target, GLint level,
if (pixels.IsNull())
return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
const ArrayBufferView& view = pixels.Value();
view.ComputeLengthAndData();
return TexSubImage2D_base(target, level, xoffset, yoffset,
width, height, 0, format, type,
pixels.Value().Data(), pixels.Value().Length(),
JS_GetArrayBufferViewType(pixels.Value().Obj()),
view.Data(), view.Length(),
JS_GetArrayBufferViewType(view.Obj()),
WebGLTexelFormat::Auto, false);
}
@ -3819,6 +3847,8 @@ WebGLContext::TexSubImage2D(GLenum target, GLint level,
return ErrorInvalidValue("texSubImage2D: pixels must not be null!");
Uint8ClampedArray arr(pixels->GetDataObject());
arr.ComputeLengthAndData();
return TexSubImage2D_base(target, level, xoffset, yoffset,
pixels->Width(), pixels->Height(),
4*pixels->Width(), format, type,
@ -3827,27 +3857,34 @@ WebGLContext::TexSubImage2D(GLenum target, GLint level,
WebGLTexelFormat::RGBA8, false);
}
bool
void
WebGLContext::LoseContext()
{
if (IsContextLost())
return false;
return ErrorInvalidOperation("loseContext: Context is already lost.");
ForceLoseContext();
return true;
mLastLossWasSimulated = true;
}
bool
void
WebGLContext::RestoreContext()
{
if (!IsContextLost() || !mAllowRestore) {
return false;
if (!IsContextLost())
return ErrorInvalidOperation("restoreContext: Context is not lost.");
if (!mLastLossWasSimulated) {
return ErrorInvalidOperation("restoreContext: Context loss was not simulated."
" Cannot simulate restore.");
}
// If we're currently lost, and the last loss was simulated, then
// we're currently only simulated-lost, allowing us to call
// restoreContext().
if (!mAllowContextRestore)
return ErrorInvalidOperation("restoreContext: Context cannot be restored.");
ForceRestoreContext();
return true;
}
bool

View File

@ -8,33 +8,45 @@
using namespace mozilla;
/* static */ void
WebGLContext::RobustnessTimerCallbackStatic(nsITimer* timer, void *thisPointer) {
static_cast<WebGLContext*>(thisPointer)->RobustnessTimerCallback(timer);
WebGLContext::ContextLossCallbackStatic(nsITimer* timer, void* thisPointer)
{
(void)timer;
WebGLContext* context = static_cast<WebGLContext*>(thisPointer);
context->TerminateContextLossTimer();
context->UpdateContextLossStatus();
}
void
WebGLContext::SetupContextLossTimer() {
WebGLContext::RunContextLossTimer()
{
// If the timer was already running, don't restart it here. Instead,
// wait until the previous call is done, then fire it one more time.
// This is an optimization to prevent unnecessary cross-communication
// between threads.
// This is an optimization to prevent unnecessary
// cross-communication between threads.
if (mContextLossTimerRunning) {
mDrawSinceContextLossTimerSet = true;
mRunContextLossTimerAgain = true;
return;
}
mContextRestorer->InitWithFuncCallback(RobustnessTimerCallbackStatic,
static_cast<void*>(this),
1000,
nsITimer::TYPE_ONE_SHOT);
mContextRestorer->InitWithFuncCallback(ContextLossCallbackStatic,
static_cast<void*>(this),
1000,
nsITimer::TYPE_ONE_SHOT);
mContextLossTimerRunning = true;
mDrawSinceContextLossTimerSet = false;
mRunContextLossTimerAgain = false;
}
void
WebGLContext::TerminateContextLossTimer() {
if (mContextLossTimerRunning) {
mContextRestorer->Cancel();
mContextLossTimerRunning = false;
WebGLContext::TerminateContextLossTimer()
{
if (!mContextLossTimerRunning)
return;
mContextRestorer->Cancel();
mContextLossTimerRunning = false;
if (mRunContextLossTimerAgain) {
RunContextLossTimer();
}
}

View File

@ -21,15 +21,13 @@ WebGLExtensionLoseContext::~WebGLExtensionLoseContext()
void
WebGLExtensionLoseContext::LoseContext()
{
if (!mContext->LoseContext())
mContext->mWebGLError = LOCAL_GL_INVALID_OPERATION;
mContext->LoseContext();
}
void
void
WebGLExtensionLoseContext::RestoreContext()
{
if (!mContext->RestoreContext())
mContext->mWebGLError = LOCAL_GL_INVALID_OPERATION;
mContext->RestoreContext();
}
IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionLoseContext)

View File

@ -99,7 +99,7 @@ function testLosingAndRestoringContext()
// restore the context after this event has exited.
setTimeout(function() {
shouldGenerateGLError(gl, gl.NO_ERROR, "extension.restoreContext()");
// The context should still be lost. It will not get restored until the
// The context should still be lost. It will not get restored until the
// webglrestorecontext event is fired.
shouldBeTrue("gl.isContextLost()");
shouldBe("gl.getError()", "gl.NO_ERROR");

View File

@ -250,12 +250,16 @@ SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv)
void
SourceBuffer::AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv)
{
aData.ComputeLengthAndData();
AppendData(aData.Data(), aData.Length(), aRv);
}
void
SourceBuffer::AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv)
{
aData.ComputeLengthAndData();
AppendData(aData.Data(), aData.Length(), aRv);
}

View File

@ -171,6 +171,8 @@ AnalyserNode::GetFloatFrequencyData(const Float32Array& aArray)
return;
}
aArray.ComputeLengthAndData();
float* buffer = aArray.Data();
size_t length = std::min(size_t(aArray.Length()), mOutputBuffer.Length());
@ -189,6 +191,8 @@ AnalyserNode::GetByteFrequencyData(const Uint8Array& aArray)
const double rangeScaleFactor = 1.0 / (mMaxDecibels - mMinDecibels);
aArray.ComputeLengthAndData();
unsigned char* buffer = aArray.Data();
size_t length = std::min(size_t(aArray.Length()), mOutputBuffer.Length());
@ -204,6 +208,8 @@ AnalyserNode::GetByteFrequencyData(const Uint8Array& aArray)
void
AnalyserNode::GetFloatTimeDomainData(const Float32Array& aArray)
{
aArray.ComputeLengthAndData();
float* buffer = aArray.Data();
size_t length = std::min(size_t(aArray.Length()), mBuffer.Length());
@ -215,6 +221,8 @@ AnalyserNode::GetFloatTimeDomainData(const Float32Array& aArray)
void
AnalyserNode::GetByteTimeDomainData(const Uint8Array& aArray)
{
aArray.ComputeLengthAndData();
unsigned char* buffer = aArray.Data();
size_t length = std::min(size_t(aArray.Length()), mBuffer.Length());

View File

@ -130,6 +130,8 @@ void
AudioBuffer::CopyFromChannel(const Float32Array& aDestination, uint32_t aChannelNumber,
uint32_t aStartInChannel, ErrorResult& aRv)
{
aDestination.ComputeLengthAndData();
uint32_t length = aDestination.Length();
CheckedInt<uint32_t> end = aStartInChannel;
end += length;
@ -156,6 +158,8 @@ AudioBuffer::CopyToChannel(JSContext* aJSContext, const Float32Array& aSource,
uint32_t aChannelNumber, uint32_t aStartInChannel,
ErrorResult& aRv)
{
aSource.ComputeLengthAndData();
uint32_t length = aSource.Length();
CheckedInt<uint32_t> end = aStartInChannel;
end += length;

View File

@ -400,6 +400,9 @@ AudioContext::CreatePeriodicWave(const Float32Array& aRealData,
const Float32Array& aImagData,
ErrorResult& aRv)
{
aRealData.ComputeLengthAndData();
aImagData.ComputeLengthAndData();
if (aRealData.Length() != aImagData.Length() ||
aRealData.Length() == 0 ||
aRealData.Length() > 4096) {
@ -434,6 +437,8 @@ AudioContext::DecodeAudioData(const ArrayBuffer& aBuffer,
JSContext* cx = jsapi.cx();
JSAutoCompartment ac(cx, aBuffer.Obj());
aBuffer.ComputeLengthAndData();
// Neuter the array buffer
size_t length = aBuffer.Length();
JS::RootedObject obj(cx, aBuffer.Obj());

View File

@ -55,6 +55,7 @@ public:
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return;
}
aValues.ComputeLengthAndData();
AudioParamTimeline::SetValueCurveAtTime(aValues.Data(), aValues.Length(),
DOMTimeToStreamTime(aStartTime), aDuration, aRv);
mCallback(mNode);

View File

@ -308,6 +308,10 @@ BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
const Float32Array& aMagResponse,
const Float32Array& aPhaseResponse)
{
aFrequencyHz.ComputeLengthAndData();
aMagResponse.ComputeLengthAndData();
aPhaseResponse.ComputeLengthAndData();
uint32_t length = std::min(std::min(aFrequencyHz.Length(), aMagResponse.Length()),
aPhaseResponse.Length());
if (!length) {

View File

@ -313,10 +313,14 @@ WaveShaperNode::SetCurve(const Nullable<Float32Array>& aCurve)
{
nsTArray<float> curve;
if (!aCurve.IsNull()) {
mCurve = aCurve.Value().Obj();
const Float32Array& floats = aCurve.Value();
curve.SetLength(aCurve.Value().Length());
PodCopy(curve.Elements(), aCurve.Value().Data(), aCurve.Value().Length());
mCurve = floats.Obj();
floats.ComputeLengthAndData();
curve.SetLength(floats.Length());
PodCopy(curve.Elements(), floats.Data(), floats.Length());
} else {
mCurve = nullptr;
}

View File

@ -109,7 +109,7 @@ MediaEngineTabVideoSource::InitRunnable::Run()
void
MediaEngineTabVideoSource::GetName(nsAString_internal& aName)
{
aName.AssignLiteral(MOZ_UTF16("&getUserMedia.videoDevice.tabShare;"));
aName.AssignLiteral(MOZ_UTF16("&getUserMedia.videoSource.tabShare;"));
}
void

View File

@ -27,6 +27,15 @@ GetUserMediaLog()
}
#endif
static PRLogModuleInfo*
GetWebrtcTraceLog()
{
static PRLogModuleInfo *sLog;
if (!sLog)
sLog = PR_NewLogModule("webrtc_trace");
return sLog;
}
#include "MediaEngineWebRTC.h"
#include "ImageContainer.h"
#include "nsIComponentRegistrar.h"
@ -65,6 +74,14 @@ MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
gFarendObserver = new AudioOutputObserver();
}
void
MediaEngineWebRTC::Print(webrtc::TraceLevel level, const char* message, int length)
{
PRLogModuleInfo *log = GetWebrtcTraceLog();
// XXX look at log level?
PR_LOG(log, PR_LOG_DEBUG, ("%s", message));
}
void
MediaEngineWebRTC::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources)
{
@ -130,7 +147,7 @@ MediaEngineWebRTC::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSourc
}
}
PRLogModuleInfo *logs = GetWebRTCLogInfo();
PRLogModuleInfo *logs = GetWebrtcTraceLog();
if (!gWebrtcTraceLoggingOn && logs && logs->level > 0) {
// no need to a critical section or lock here
gWebrtcTraceLoggingOn = 1;
@ -143,7 +160,11 @@ MediaEngineWebRTC::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSourc
LOG(("%s Logging webrtc to %s level %d", __FUNCTION__, file, logs->level));
mVideoEngine->SetTraceFilter(logs->level);
mVideoEngine->SetTraceFile(file);
if (strcmp(file, "nspr") == 0) {
mVideoEngine->SetTraceCallback(this);
} else {
mVideoEngine->SetTraceFile(file);
}
}
ptrViEBase = webrtc::ViEBase::GetInterface(mVideoEngine);
@ -263,7 +284,7 @@ MediaEngineWebRTC::EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSourc
}
}
PRLogModuleInfo *logs = GetWebRTCLogInfo();
PRLogModuleInfo *logs = GetWebrtcTraceLog();
if (!gWebrtcTraceLoggingOn && logs && logs->level > 0) {
// no need to a critical section or lock here
gWebrtcTraceLoggingOn = 1;
@ -276,7 +297,11 @@ MediaEngineWebRTC::EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSourc
LOG(("Logging webrtc to %s level %d", __FUNCTION__, file, logs->level));
mVoiceEngine->SetTraceFilter(logs->level);
mVoiceEngine->SetTraceFile(file);
if (strcmp(file, "nspr") == 0) {
mVoiceEngine->SetTraceCallback(this);
} else {
mVoiceEngine->SetTraceFile(file);
}
}
ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
@ -340,13 +365,16 @@ MediaEngineWebRTC::Shutdown()
// This is likely paranoia
MutexAutoLock lock(mMutex);
// Clear callbacks before we go away since the engines may outlive us
if (mVideoEngine) {
mVideoSources.Clear();
mVideoEngine->SetTraceCallback(nullptr);
webrtc::VideoEngine::Delete(mVideoEngine);
}
if (mVoiceEngine) {
mAudioSources.Clear();
mVoiceEngine->SetTraceCallback(nullptr);
webrtc::VoiceEngine::Delete(mVoiceEngine);
}

View File

@ -356,7 +356,8 @@ private:
NullTransport *mNullTransport;
};
class MediaEngineWebRTC : public MediaEngine
class MediaEngineWebRTC : public MediaEngine,
public webrtc::TraceCallback
{
public:
MediaEngineWebRTC(MediaEnginePrefs &aPrefs);
@ -368,6 +369,9 @@ public:
virtual void EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
virtual void EnumerateAudioDevices(nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
// Webrtc trace callbacks for proxying to NSPR
virtual void Print(webrtc::TraceLevel level, const char* message, int length);
private:
~MediaEngineWebRTC() {
Shutdown();

View File

@ -64,12 +64,6 @@ SVGAnimationElement::Init()
//----------------------------------------------------------------------
bool
SVGAnimationElement::PassesConditionalProcessingTests()
{
return SVGTests::PassesConditionalProcessingTests();
}
const nsAttrValue*
SVGAnimationElement::GetAnimAttr(nsIAtom* aName) const
{
@ -313,6 +307,13 @@ SVGAnimationElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
SVGAnimationElementBase::AfterSetAttr(aNamespaceID, aName, aValue,
aNotify);
if (SVGTests::IsConditionalProcessingAttribute(aName)) {
bool isDisabled = !SVGTests::PassesConditionalProcessingTests();
if (mTimedElement.SetIsDisabled(isDisabled)) {
AnimationNeedsResample();
}
}
if (aNamespaceID != kNameSpaceID_XLink || aName != nsGkAtoms::href)
return rv;

View File

@ -58,7 +58,6 @@ public:
virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
const nsAttrValue* aValue, bool aNotify) MOZ_OVERRIDE;
bool PassesConditionalProcessingTests();
const nsAttrValue* GetAnimAttr(nsIAtom* aName) const;
bool GetAnimAttr(nsIAtom* aAttName, nsAString& aResult) const;
bool HasAnimAttr(nsIAtom* aAttName) const;

Some files were not shown because too many files have changed in this diff Show More