зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to b2g-inbound
This commit is contained in:
Коммит
fe50f7afa0
|
@ -24,48 +24,53 @@ XPCOMUtils.defineLazyModuleGetter(this, "PanelFrame", "resource:///modules/Panel
|
|||
* the panel to the button which triggers it.
|
||||
* @param {String} [tabId] Identifier of the tab to select when the panel is
|
||||
* opened. Example: 'rooms', 'contacts', etc.
|
||||
* @return {Promise}
|
||||
*/
|
||||
openCallPanel: function(event, tabId = null) {
|
||||
let callback = iframe => {
|
||||
// Helper function to show a specific tab view in the panel.
|
||||
function showTab() {
|
||||
if (!tabId) {
|
||||
return new Promise((resolve) => {
|
||||
let callback = iframe => {
|
||||
// Helper function to show a specific tab view in the panel.
|
||||
function showTab() {
|
||||
if (!tabId) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
let win = iframe.contentWindow;
|
||||
let ev = new win.CustomEvent("UIAction", Cu.cloneInto({
|
||||
detail: {
|
||||
action: "selectTab",
|
||||
tab: tabId
|
||||
}
|
||||
}, win));
|
||||
win.dispatchEvent(ev);
|
||||
resolve();
|
||||
}
|
||||
|
||||
// If the panel has been opened and initialized before, we can skip waiting
|
||||
// for the content to load - because it's already there.
|
||||
if (("contentWindow" in iframe) && iframe.contentWindow.document.readyState == "complete") {
|
||||
showTab();
|
||||
return;
|
||||
}
|
||||
|
||||
let win = iframe.contentWindow;
|
||||
let ev = new win.CustomEvent("UIAction", Cu.cloneInto({
|
||||
detail: {
|
||||
action: "selectTab",
|
||||
tab: tabId
|
||||
}
|
||||
}, win));
|
||||
win.dispatchEvent(ev);
|
||||
}
|
||||
iframe.addEventListener("DOMContentLoaded", function documentDOMLoaded() {
|
||||
iframe.removeEventListener("DOMContentLoaded", documentDOMLoaded, true);
|
||||
injectLoopAPI(iframe.contentWindow);
|
||||
iframe.contentWindow.addEventListener("loopPanelInitialized", function loopPanelInitialized() {
|
||||
iframe.contentWindow.removeEventListener("loopPanelInitialized",
|
||||
loopPanelInitialized);
|
||||
showTab();
|
||||
});
|
||||
}, true);
|
||||
};
|
||||
|
||||
// If the panel has been opened and initialized before, we can skip waiting
|
||||
// for the content to load - because it's already there.
|
||||
if (("contentWindow" in iframe) && iframe.contentWindow.document.readyState == "complete") {
|
||||
showTab();
|
||||
return;
|
||||
}
|
||||
// Used to clear the temporary "login" state from the button.
|
||||
Services.obs.notifyObservers(null, "loop-status-changed", null);
|
||||
|
||||
iframe.addEventListener("DOMContentLoaded", function documentDOMLoaded() {
|
||||
iframe.removeEventListener("DOMContentLoaded", documentDOMLoaded, true);
|
||||
injectLoopAPI(iframe.contentWindow);
|
||||
iframe.contentWindow.addEventListener("loopPanelInitialized", function loopPanelInitialized() {
|
||||
iframe.contentWindow.removeEventListener("loopPanelInitialized",
|
||||
loopPanelInitialized);
|
||||
showTab();
|
||||
});
|
||||
}, true);
|
||||
};
|
||||
|
||||
// Used to clear the temporary "login" state from the button.
|
||||
Services.obs.notifyObservers(null, "loop-status-changed", null);
|
||||
|
||||
PanelFrame.showPopup(window, event ? event.target : this.toolbarButton.node,
|
||||
"loop", null, "about:looppanel", null, callback);
|
||||
PanelFrame.showPopup(window, event ? event.target : this.toolbarButton.node,
|
||||
"loop", null, "about:looppanel", null, callback);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -64,7 +64,7 @@ function promiseGetMozLoopAPI() {
|
|||
let frameId = btn.getAttribute("notificationFrameId");
|
||||
let frame = document.getElementById(frameId);
|
||||
if (frame) {
|
||||
loopPanel.removeChild(frame);
|
||||
frame.remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -111,15 +111,15 @@ this.UITour = {
|
|||
},
|
||||
widgetName: "PanelUI-customize",
|
||||
}],
|
||||
["devtools", {query: "#developer-button"}],
|
||||
["help", {query: "#PanelUI-help"}],
|
||||
["home", {query: "#home-button"}],
|
||||
["loop", {query: "#loop-button"}],
|
||||
["devtools", {query: "#developer-button"}],
|
||||
["webide", {query: "#webide-button"}],
|
||||
["forget", {
|
||||
query: "#panic-button",
|
||||
widgetName: "panic-button",
|
||||
allowAdd: true }],
|
||||
allowAdd: true,
|
||||
}],
|
||||
["loop", {query: "#loop-button"}],
|
||||
["privateWindow", {query: "#privatebrowsing-button"}],
|
||||
["quit", {query: "#PanelUI-quit"}],
|
||||
["search", {
|
||||
|
@ -188,6 +188,7 @@ this.UITour = {
|
|||
query: "#urlbar",
|
||||
widgetName: "urlbar-container",
|
||||
}],
|
||||
["webide", {query: "#webide-button"}],
|
||||
]),
|
||||
|
||||
init: function() {
|
||||
|
@ -714,8 +715,6 @@ this.UITour = {
|
|||
teardownTour: function(aWindow, aWindowClosing = false) {
|
||||
log.debug("teardownTour: aWindowClosing = " + aWindowClosing);
|
||||
aWindow.gBrowser.tabContainer.removeEventListener("TabSelect", this);
|
||||
aWindow.PanelUI.panel.removeEventListener("popuphiding", this.hidePanelAnnotations);
|
||||
aWindow.PanelUI.panel.removeEventListener("ViewShowing", this.hidePanelAnnotations);
|
||||
aWindow.removeEventListener("SSWindowClosing", this);
|
||||
|
||||
let originTabs = this.originTabs.get(aWindow);
|
||||
|
@ -732,8 +731,15 @@ this.UITour = {
|
|||
this.hideInfo(aWindow);
|
||||
// Ensure the menu panel is hidden before calling recreatePopup so popup events occur.
|
||||
this.hideMenu(aWindow, "appMenu");
|
||||
this.hideMenu(aWindow, "loop");
|
||||
}
|
||||
|
||||
// Clean up panel listeners after we may have called hideMenu above.
|
||||
aWindow.PanelUI.panel.removeEventListener("popuphiding", this.hidePanelAnnotations);
|
||||
aWindow.PanelUI.panel.removeEventListener("ViewShowing", this.hidePanelAnnotations);
|
||||
let loopPanel = aWindow.document.getElementById("loop-notification-panel");
|
||||
loopPanel.removeEventListener("popuphidden", this.onLoopPanelHidden);
|
||||
|
||||
this.endUrlbarCapture(aWindow);
|
||||
this.removePinnedTab(aWindow);
|
||||
this.resetTheme();
|
||||
|
@ -867,7 +873,7 @@ this.UITour = {
|
|||
*/
|
||||
_setAppMenuStateForAnnotation: function(aWindow, aAnnotationType, aShouldOpenForHighlight, aCallback = null) {
|
||||
log.debug("_setAppMenuStateForAnnotation:", aAnnotationType);
|
||||
log.debug("_setAppMenuStateForAnnotation: Menu is exptected to be:", aShouldOpenForHighlight ? "open" : "closed");
|
||||
log.debug("_setAppMenuStateForAnnotation: Menu is expected to be:", aShouldOpenForHighlight ? "open" : "closed");
|
||||
|
||||
// If the panel is in the desired state, we're done.
|
||||
let panelIsOpen = aWindow.PanelUI.panel.state != "closed";
|
||||
|
@ -1238,6 +1244,26 @@ this.UITour = {
|
|||
} else if (aMenuName == "bookmarks") {
|
||||
let menuBtn = aWindow.document.getElementById("bookmarks-menu-button");
|
||||
openMenuButton(menuBtn);
|
||||
} else if (aMenuName == "loop") {
|
||||
let toolbarButton = aWindow.LoopUI.toolbarButton;
|
||||
if (!toolbarButton || !toolbarButton.node) {
|
||||
return;
|
||||
}
|
||||
|
||||
let panel = aWindow.document.getElementById("loop-notification-panel");
|
||||
panel.setAttribute("noautohide", true);
|
||||
if (panel.state != "open") {
|
||||
this.recreatePopup(panel);
|
||||
}
|
||||
|
||||
// An event object is expected but we don't want to toggle the panel with a click if the panel
|
||||
// is already open.
|
||||
aWindow.LoopUI.openCallPanel({ target: toolbarButton.node, }).then(() => {
|
||||
if (aOpenCallback) {
|
||||
aOpenCallback();
|
||||
}
|
||||
});
|
||||
panel.addEventListener("popuphidden", this.onLoopPanelHidden);
|
||||
} else if (aMenuName == "searchEngines") {
|
||||
this.getTarget(aWindow, "searchProvider").then(target => {
|
||||
openMenuButton(target.node);
|
||||
|
@ -1258,6 +1284,9 @@ this.UITour = {
|
|||
} else if (aMenuName == "bookmarks") {
|
||||
let menuBtn = aWindow.document.getElementById("bookmarks-menu-button");
|
||||
closeMenuButton(menuBtn);
|
||||
} else if (aMenuName == "loop") {
|
||||
let panel = aWindow.document.getElementById("loop-notification-panel");
|
||||
panel.hidePopup();
|
||||
} else if (aMenuName == "searchEngines") {
|
||||
let menuBtn = this.targets.get("searchProvider").query(aWindow.document);
|
||||
closeMenuButton(menuBtn);
|
||||
|
@ -1289,6 +1318,11 @@ this.UITour = {
|
|||
UITour.appMenuOpenForAnnotation.clear();
|
||||
},
|
||||
|
||||
onLoopPanelHidden: function(aEvent) {
|
||||
aEvent.target.removeAttribute("noautohide");
|
||||
UITour.recreatePopup(aEvent.target);
|
||||
},
|
||||
|
||||
recreatePopup: function(aPanel) {
|
||||
// After changing popup attributes that relate to how the native widget is created
|
||||
// (e.g. @noautohide) we need to re-create the frame/widget for it to take effect.
|
||||
|
|
|
@ -28,6 +28,8 @@ skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
|
|||
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
|
||||
[browser_UITour_annotation_size_attributes.js]
|
||||
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
|
||||
[browser_UITour_loop.js]
|
||||
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
|
||||
[browser_UITour_modalDialog.js]
|
||||
run-if = os == "mac" # modal dialog disabling only working on OS X
|
||||
skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
|
||||
|
|
|
@ -89,7 +89,7 @@ let tests = [
|
|||
yield addPinnedTabPromise();
|
||||
is(gBrowser.tabs[1].pinned, false, "After multiple calls of addPinnedTab, should still only have one pinned tab");
|
||||
}),
|
||||
taskify(function* test_menu() {
|
||||
taskify(function* test_bookmarks_menu() {
|
||||
let bookmarksMenuButton = document.getElementById("bookmarks-menu-button");
|
||||
|
||||
ise(bookmarksMenuButton.open, false, "Menu should initially be closed");
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
let gTestTab;
|
||||
let gContentAPI;
|
||||
let gContentWindow;
|
||||
let loopButton;
|
||||
let loopPanel = document.getElementById("loop-notification-panel");
|
||||
|
||||
Components.utils.import("resource:///modules/UITour.jsm");
|
||||
|
||||
function test() {
|
||||
UITourTest();
|
||||
}
|
||||
|
||||
let tests = [
|
||||
taskify(function* test_menu_show_hide() {
|
||||
ise(loopButton.open, false, "Menu should initially be closed");
|
||||
gContentAPI.showMenu("loop");
|
||||
|
||||
yield waitForConditionPromise(() => {
|
||||
return loopButton.open;
|
||||
}, "Menu should be visible after showMenu()");
|
||||
|
||||
ok(loopPanel.hasAttribute("noautohide"), "@noautohide should be on the loop panel");
|
||||
ok(loopPanel.hasAttribute("panelopen"), "The panel should have @panelopen");
|
||||
is(loopPanel.state, "open", "The panel should be open");
|
||||
ok(loopButton.hasAttribute("open"), "Loop button should know that the menu is open");
|
||||
|
||||
gContentAPI.hideMenu("loop");
|
||||
yield waitForConditionPromise(() => {
|
||||
return !loopButton.open;
|
||||
}, "Menu should be hidden after hideMenu()");
|
||||
|
||||
checkLoopPanelIsHidden();
|
||||
}),
|
||||
// Test the menu was cleaned up in teardown.
|
||||
taskify(function* setup_menu_cleanup() {
|
||||
gContentAPI.showMenu("loop");
|
||||
|
||||
yield waitForConditionPromise(() => {
|
||||
return loopButton.open;
|
||||
}, "Menu should be visible after showMenu()");
|
||||
|
||||
// Leave it open so it gets torn down and we can test below that teardown was succesful.
|
||||
}),
|
||||
taskify(function* test_menu_cleanup() {
|
||||
// Test that the open menu from above was torn down fully.
|
||||
checkLoopPanelIsHidden();
|
||||
}),
|
||||
];
|
||||
|
||||
function checkLoopPanelIsHidden() {
|
||||
ok(!loopPanel.hasAttribute("noautohide"), "@noautohide on the loop panel should have been cleaned up");
|
||||
ok(!loopPanel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen");
|
||||
isnot(loopPanel.state, "open", "The panel shouldn't be open");
|
||||
is(loopButton.hasAttribute("open"), false, "Loop button should know that the panel is closed");
|
||||
}
|
||||
|
||||
if (Services.prefs.getBoolPref("loop.enabled")) {
|
||||
loopButton = window.LoopUI.toolbarButton.node;
|
||||
registerCleanupFunction(() => {
|
||||
// Copied from browser/components/loop/test/mochitest/head.js
|
||||
// Remove the iframe after each test. This also avoids mochitest complaining
|
||||
// about leaks on shutdown as we intentionally hold the iframe open for the
|
||||
// life of the application.
|
||||
let frameId = loopButton.getAttribute("notificationFrameId");
|
||||
let frame = document.getElementById(frameId);
|
||||
if (frame) {
|
||||
frame.remove();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ok(true, "Loop is disabled so skip the UITour Loop tests");
|
||||
tests = [];
|
||||
}
|
|
@ -34,7 +34,7 @@ treecol {
|
|||
}
|
||||
|
||||
#category-search > .category-icon {
|
||||
-moz-image-region: rect(0, 194px, 24px, 168px);
|
||||
-moz-image-region: rect(0, 192px, 24px, 168px);
|
||||
}
|
||||
|
||||
#category-content > .category-icon {
|
||||
|
|
|
@ -747,6 +747,7 @@ browser.jar:
|
|||
skin/classic/aero/browser/devtools/eyedropper.css (../shared/devtools/eyedropper.css)
|
||||
* skin/classic/aero/browser/devtools/netmonitor.css (devtools/netmonitor.css)
|
||||
* skin/classic/aero/browser/devtools/profiler.css (devtools/profiler.css)
|
||||
* skin/classic/aero/browser/devtools/performance.css (devtools/performance.css)
|
||||
* skin/classic/aero/browser/devtools/timeline.css (devtools/timeline.css)
|
||||
* skin/classic/aero/browser/devtools/scratchpad.css (devtools/scratchpad.css)
|
||||
* skin/classic/aero/browser/devtools/shadereditor.css (devtools/shadereditor.css)
|
||||
|
|
|
@ -25,9 +25,6 @@ if CONFIG['MOZ_VORBIS']:
|
|||
if CONFIG['MOZ_TREMOR']:
|
||||
external_dirs += ['media/libtremor']
|
||||
|
||||
if CONFIG['MOZ_OPUS']:
|
||||
external_dirs += ['media/libopus']
|
||||
|
||||
if CONFIG['MOZ_WEBM']:
|
||||
external_dirs += ['media/libnestegg']
|
||||
|
||||
|
@ -44,6 +41,7 @@ external_dirs += [
|
|||
'media/kiss_fft',
|
||||
'media/libcubeb',
|
||||
'media/libogg',
|
||||
'media/libopus',
|
||||
'media/libtheora',
|
||||
'media/libspeex_resampler',
|
||||
'media/libstagefright',
|
||||
|
|
18
configure.in
18
configure.in
|
@ -3852,7 +3852,6 @@ MOZ_TREMOR=
|
|||
MOZ_WAVE=1
|
||||
MOZ_SAMPLE_TYPE_FLOAT32=
|
||||
MOZ_SAMPLE_TYPE_S16=
|
||||
MOZ_OPUS=1
|
||||
MOZ_WEBM=1
|
||||
MOZ_GSTREAMER=
|
||||
MOZ_DIRECTSHOW=
|
||||
|
@ -5224,14 +5223,6 @@ if test "${ac_cv_c_attribute_aligned}" != "0"; then
|
|||
[${ac_cv_c_attribute_aligned}],[Maximum supported data alignment])
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Disable Opus audio codec support
|
||||
dnl ========================================================
|
||||
MOZ_ARG_DISABLE_BOOL(opus,
|
||||
[ --disable-opus Disable support for Opus audio],
|
||||
MOZ_OPUS=,
|
||||
MOZ_OPUS=1)
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Disable VP8 decoder support
|
||||
dnl ========================================================
|
||||
|
@ -5567,10 +5558,6 @@ if test -n "$MOZ_VORBIS" -a -n "$MOZ_TREMOR"; then
|
|||
AC_MSG_ERROR([MOZ_VORBIS and MOZ_TREMOR are mutually exclusive! The build system should not allow them both to be set, but they are. Please file a bug at https://bugzilla.mozilla.org/])
|
||||
fi
|
||||
|
||||
if test -n "$MOZ_WEBRTC" -a -z "$MOZ_OPUS"; then
|
||||
AC_MSG_ERROR([MOZ_WEBRTC requires MOZ_OPUS which is disabled.])
|
||||
fi
|
||||
|
||||
if test -n "$MOZ_VORBIS"; then
|
||||
AC_DEFINE(MOZ_VORBIS)
|
||||
fi
|
||||
|
@ -5581,10 +5568,6 @@ if test -n "$MOZ_TREMOR"; then
|
|||
MOZ_WEBM_ENCODER=
|
||||
fi
|
||||
|
||||
if test -n "$MOZ_OPUS"; then
|
||||
AC_DEFINE(MOZ_OPUS)
|
||||
fi
|
||||
|
||||
if test -n "$MOZ_WEBM_ENCODER"; then
|
||||
AC_DEFINE(MOZ_WEBM_ENCODER)
|
||||
fi
|
||||
|
@ -8877,7 +8860,6 @@ AC_SUBST(WIN32_GUI_EXE_LDFLAGS)
|
|||
AC_SUBST(MOZ_WAVE)
|
||||
AC_SUBST(MOZ_VORBIS)
|
||||
AC_SUBST(MOZ_TREMOR)
|
||||
AC_SUBST(MOZ_OPUS)
|
||||
AC_SUBST(MOZ_WEBM)
|
||||
AC_SUBST(MOZ_WMF)
|
||||
AC_SUBST(MOZ_FFMPEG)
|
||||
|
|
|
@ -373,7 +373,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec
|
|||
[test_bug338583.html]
|
||||
# b2g(https not working, bug 907770) b2g-debug(https not working, bug 907770) b2g-desktop(43 total - bug 901343, specialpowers.wrap issue createsystemxhr)
|
||||
# e10s - bug 970589, bug 1091934
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || (os == 'linux' && debug && e10s)
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android'
|
||||
[test_bug338679.html]
|
||||
[test_bug339494.html]
|
||||
[test_bug339494.xhtml]
|
||||
|
|
|
@ -644,7 +644,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=338583
|
|||
setTimeout(f, 10 + 10*f.i);
|
||||
} else {
|
||||
stress_factor = ((new Date()).getTime()-begin_time)*1/589;
|
||||
stress_factor *= 1.10; // also, a margin of 10%
|
||||
stress_factor *= 1.50; // also, a margin of 50%
|
||||
|
||||
runAllTests();
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ public:
|
|||
|
||||
// nsWrapperCache
|
||||
virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "WebGL1Context.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
bool
|
||||
WebGL1Context::ValidateAttribPointerType(bool /*integerMode*/, GLenum type, GLsizei* out_alignment, const char* info)
|
||||
{
|
||||
MOZ_ASSERT(out_alignment);
|
||||
if (!out_alignment)
|
||||
return false;
|
||||
|
||||
switch (type) {
|
||||
case LOCAL_GL_BYTE:
|
||||
case LOCAL_GL_UNSIGNED_BYTE:
|
||||
*out_alignment = 1;
|
||||
return true;
|
||||
|
||||
case LOCAL_GL_SHORT:
|
||||
case LOCAL_GL_UNSIGNED_SHORT:
|
||||
*out_alignment = 2;
|
||||
return true;
|
||||
// XXX case LOCAL_GL_FIXED:
|
||||
case LOCAL_GL_FLOAT:
|
||||
*out_alignment = 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
ErrorInvalidEnumInfo(info, type);
|
||||
return false;
|
||||
}
|
|
@ -35,7 +35,6 @@ public:
|
|||
|
||||
virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Buffer objects - WebGL2ContextBuffers.cpp
|
||||
|
||||
|
@ -126,6 +125,12 @@ public:
|
|||
void UniformMatrix4x3fv(WebGLUniformLocation* location, bool transpose, const dom::Float32Array& value);
|
||||
void UniformMatrix4x3fv(WebGLUniformLocation* location, bool transpose, const dom::Sequence<GLfloat>& value);
|
||||
|
||||
private:
|
||||
void VertexAttribI4iv(GLuint index, size_t length, const GLint* v);
|
||||
void VertexAttribI4uiv(GLuint index, size_t length, const GLuint* v);
|
||||
|
||||
public:
|
||||
// GL 3.0 & ES 3.0
|
||||
void VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w);
|
||||
void VertexAttribI4iv(GLuint index, const dom::Sequence<GLint>& v);
|
||||
void VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);
|
||||
|
@ -250,11 +255,14 @@ public:
|
|||
private:
|
||||
WebGL2Context();
|
||||
|
||||
JS::Value GetTexParameterInternal(const TexTarget& target, GLenum pname) MOZ_OVERRIDE;
|
||||
|
||||
bool ValidateSizedInternalFormat(GLenum internalFormat, const char* info);
|
||||
bool ValidateTexStorage(GLenum target, GLsizei levels, GLenum internalformat,
|
||||
GLsizei width, GLsizei height, GLsizei depth,
|
||||
const char* info);
|
||||
JS::Value GetTexParameterInternal(const TexTarget& target, GLenum pname) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -56,7 +56,7 @@ SimulateOcclusionQueryTarget(const gl::GLContext* gl, GLenum target)
|
|||
return LOCAL_GL_SAMPLES_PASSED;
|
||||
}
|
||||
|
||||
WebGLQueryRefPtr*
|
||||
WebGLRefPtr<WebGLQuery>*
|
||||
WebGLContext::GetQueryTargetSlot(GLenum target)
|
||||
{
|
||||
switch (target) {
|
||||
|
|
|
@ -5,17 +5,103 @@
|
|||
|
||||
#include "WebGL2Context.h"
|
||||
#include "GLContext.h"
|
||||
#include "WebGLVertexArray.h"
|
||||
#include "WebGLVertexAttribData.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
typedef union { GLint i; GLfloat f; GLuint u; } fi_t;
|
||||
|
||||
static inline
|
||||
GLfloat PuntToFloat(GLint i)
|
||||
{
|
||||
fi_t tmp;
|
||||
tmp.i = i;
|
||||
return tmp.f;
|
||||
}
|
||||
|
||||
static inline
|
||||
GLfloat PuntToFloat(GLuint u)
|
||||
{
|
||||
fi_t tmp;
|
||||
tmp.u = u;
|
||||
return tmp.f;
|
||||
}
|
||||
|
||||
bool
|
||||
WebGL2Context::ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info)
|
||||
{
|
||||
MOZ_ASSERT(alignment);
|
||||
|
||||
switch (type) {
|
||||
case LOCAL_GL_BYTE:
|
||||
case LOCAL_GL_UNSIGNED_BYTE:
|
||||
*alignment = 1;
|
||||
return true;
|
||||
|
||||
case LOCAL_GL_SHORT:
|
||||
case LOCAL_GL_UNSIGNED_SHORT:
|
||||
*alignment = 2;
|
||||
return true;
|
||||
|
||||
case LOCAL_GL_INT:
|
||||
case LOCAL_GL_UNSIGNED_INT:
|
||||
*alignment = 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!integerMode) {
|
||||
switch (type) {
|
||||
case LOCAL_GL_HALF_FLOAT:
|
||||
*alignment = 2;
|
||||
return true;
|
||||
|
||||
case LOCAL_GL_FLOAT:
|
||||
case LOCAL_GL_FIXED:
|
||||
case LOCAL_GL_INT_2_10_10_10_REV:
|
||||
case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
|
||||
*alignment = 4;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorInvalidEnum("%s: invalid enum value 0x%x", info, type);
|
||||
return false;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Uniforms and attributes
|
||||
|
||||
void
|
||||
WebGL2Context::VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset)
|
||||
{
|
||||
MOZ_CRASH("Not Implemented.");
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateAttribIndex(index, "vertexAttribIPointer"))
|
||||
return;
|
||||
|
||||
if (!ValidateAttribPointer(true, index, size, type, LOCAL_GL_FALSE, stride, offset, "vertexAttribIPointer"))
|
||||
return;
|
||||
|
||||
MOZ_ASSERT(mBoundVertexArray);
|
||||
mBoundVertexArray->EnsureAttrib(index);
|
||||
|
||||
InvalidateBufferFetching();
|
||||
|
||||
WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
|
||||
|
||||
vd.buf = mBoundArrayBuffer;
|
||||
vd.stride = stride;
|
||||
vd.size = size;
|
||||
vd.byteOffset = offset;
|
||||
vd.type = type;
|
||||
vd.normalized = false;
|
||||
vd.integer = true;
|
||||
|
||||
MakeContextCurrent();
|
||||
gl->fVertexAttribIPointer(index, size, type, stride, reinterpret_cast<void*>(offset));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -141,25 +227,81 @@ WebGL2Context::UniformMatrix4x3fv(WebGLUniformLocation* location, bool transpose
|
|||
void
|
||||
WebGL2Context::VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w)
|
||||
{
|
||||
MOZ_CRASH("Not Implemented.");
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (index || gl->IsGLES()) {
|
||||
MakeContextCurrent();
|
||||
gl->fVertexAttribI4i(index, x, y, z, w);
|
||||
} else {
|
||||
mVertexAttrib0Vector[0] = PuntToFloat(x);
|
||||
mVertexAttrib0Vector[1] = PuntToFloat(y);
|
||||
mVertexAttrib0Vector[2] = PuntToFloat(z);
|
||||
mVertexAttrib0Vector[3] = PuntToFloat(w);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGL2Context::VertexAttribI4iv(GLuint index, size_t length, const GLint* v)
|
||||
{
|
||||
if (!ValidateAttribArraySetter("vertexAttribI4iv", 4, length))
|
||||
return;
|
||||
|
||||
if (index || gl->IsGLES()) {
|
||||
MakeContextCurrent();
|
||||
gl->fVertexAttribI4iv(index, v);
|
||||
} else {
|
||||
mVertexAttrib0Vector[0] = PuntToFloat(v[0]);
|
||||
mVertexAttrib0Vector[1] = PuntToFloat(v[1]);
|
||||
mVertexAttrib0Vector[2] = PuntToFloat(v[2]);
|
||||
mVertexAttrib0Vector[3] = PuntToFloat(v[3]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGL2Context::VertexAttribI4iv(GLuint index, const dom::Sequence<GLint>& v)
|
||||
{
|
||||
MOZ_CRASH("Not Implemented.");
|
||||
VertexAttribI4iv(index, v.Length(), v.Elements());
|
||||
}
|
||||
|
||||
void
|
||||
WebGL2Context::VertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w)
|
||||
{
|
||||
MOZ_CRASH("Not Implemented.");
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (index || gl->IsGLES()) {
|
||||
MakeContextCurrent();
|
||||
gl->fVertexAttribI4ui(index, x, y, z, w);
|
||||
} else {
|
||||
mVertexAttrib0Vector[0] = PuntToFloat(x);
|
||||
mVertexAttrib0Vector[1] = PuntToFloat(y);
|
||||
mVertexAttrib0Vector[2] = PuntToFloat(z);
|
||||
mVertexAttrib0Vector[3] = PuntToFloat(w);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGL2Context::VertexAttribI4uiv(GLuint index, size_t length, const GLuint* v)
|
||||
{
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (index || gl->IsGLES()) {
|
||||
MakeContextCurrent();
|
||||
gl->fVertexAttribI4uiv(index, v);
|
||||
} else {
|
||||
mVertexAttrib0Vector[0] = PuntToFloat(v[0]);
|
||||
mVertexAttrib0Vector[1] = PuntToFloat(v[1]);
|
||||
mVertexAttrib0Vector[2] = PuntToFloat(v[2]);
|
||||
mVertexAttrib0Vector[3] = PuntToFloat(v[3]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGL2Context::VertexAttribI4uiv(GLuint index, const dom::Sequence<GLuint>& v)
|
||||
{
|
||||
MOZ_CRASH("Not Implemented.");
|
||||
VertexAttribI4uiv(index, v.Length(), v.Elements());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
|
@ -52,7 +52,7 @@ class nsIDocShell;
|
|||
*
|
||||
* Exceptions: some of the following values are set to higher values than in the spec because
|
||||
* the values in the spec are ridiculously low. They are explicitly marked below
|
||||
*/
|
||||
*/
|
||||
#define MINVALUE_GL_MAX_TEXTURE_SIZE 1024 // Different from the spec, which sets it to 64 on page 162
|
||||
#define MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE 512 // Different from the spec, which sets it to 16 on page 162
|
||||
#define MINVALUE_GL_MAX_VERTEX_ATTRIBS 8 // Page 164
|
||||
|
@ -95,8 +95,6 @@ namespace gfx {
|
|||
class SourceSurface;
|
||||
}
|
||||
|
||||
typedef WebGLRefPtr<WebGLQuery> WebGLQueryRefPtr;
|
||||
|
||||
WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format);
|
||||
|
||||
void AssertUintParamCorrect(gl::GLContext* gl, GLenum pname, GLuint shadow);
|
||||
|
@ -134,11 +132,13 @@ TexTarget TexImageTargetToTexTarget(TexImageTarget texImageTarget);
|
|||
class WebGLIntOrFloat {
|
||||
enum {
|
||||
Int,
|
||||
Float
|
||||
Float,
|
||||
Uint
|
||||
} mType;
|
||||
union {
|
||||
GLint i;
|
||||
GLfloat f;
|
||||
GLuint u;
|
||||
} mValue;
|
||||
|
||||
public:
|
||||
|
@ -158,6 +158,7 @@ class WebGLContext
|
|||
, public nsWrapperCache
|
||||
, public SupportsWeakPtr<WebGLContext>
|
||||
{
|
||||
friend class WebGL2Context;
|
||||
friend class WebGLContextUserData;
|
||||
friend class WebGLExtensionCompressedTextureATC;
|
||||
friend class WebGLExtensionCompressedTextureETC1;
|
||||
|
@ -898,10 +899,10 @@ private:
|
|||
// -----------------------------------------------------------------------------
|
||||
// Queries (WebGL2ContextQueries.cpp)
|
||||
protected:
|
||||
WebGLQueryRefPtr* GetQueryTargetSlot(GLenum target);
|
||||
WebGLRefPtr<WebGLQuery>* GetQueryTargetSlot(GLenum target);
|
||||
|
||||
WebGLQueryRefPtr mActiveOcclusionQuery;
|
||||
WebGLQueryRefPtr mActiveTransformFeedbackQuery;
|
||||
WebGLRefPtr<WebGLQuery> mActiveOcclusionQuery;
|
||||
WebGLRefPtr<WebGLQuery> mActiveTransformFeedbackQuery;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// State and State Requests (WebGLContextState.cpp)
|
||||
|
@ -1006,6 +1007,7 @@ private:
|
|||
uint32_t mMaxFetchedVertices;
|
||||
uint32_t mMaxFetchedInstances;
|
||||
|
||||
protected:
|
||||
inline void InvalidateBufferFetching() {
|
||||
mBufferFetchingIsVerified = false;
|
||||
mBufferFetchingHasPerVertex = false;
|
||||
|
@ -1192,6 +1194,10 @@ protected:
|
|||
bool ValidateTexInputData(GLenum type, js::Scalar::Type jsArrayType,
|
||||
WebGLTexImageFunc func, WebGLTexDimensions dims);
|
||||
bool ValidateDrawModeEnum(GLenum mode, const char* info);
|
||||
bool ValidateAttribIndex(GLuint index, const char* info);
|
||||
bool ValidateAttribPointer(bool integerMode, GLuint index, GLint size, GLenum type,
|
||||
WebGLboolean normalized, GLsizei stride,
|
||||
WebGLintptr byteOffset, const char* info);
|
||||
bool ValidateStencilParamsForDrawCall();
|
||||
|
||||
bool ValidateGLSLVariableName(const nsAString& name, const char* info);
|
||||
|
@ -1329,6 +1335,11 @@ private:
|
|||
template<class ObjectType>
|
||||
bool ValidateObjectAssumeNonNull(const char* info, ObjectType* object);
|
||||
|
||||
private:
|
||||
// -------------------------------------------------------------------------
|
||||
// Context customization points
|
||||
virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) = 0;
|
||||
|
||||
protected:
|
||||
int32_t MaxTextureSizeForTarget(TexTarget target) const {
|
||||
return (target == LOCAL_GL_TEXTURE_2D) ? mGLMaxTextureSize
|
||||
|
|
|
@ -621,12 +621,20 @@ WebGLContext::UndoFakeVertexAttrib0()
|
|||
if (mBoundVertexArray->HasAttrib(0) && mBoundVertexArray->mAttribs[0].buf) {
|
||||
const WebGLVertexAttribData& attrib0 = mBoundVertexArray->mAttribs[0];
|
||||
gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, attrib0.buf->GLName());
|
||||
gl->fVertexAttribPointer(0,
|
||||
attrib0.size,
|
||||
attrib0.type,
|
||||
attrib0.normalized,
|
||||
attrib0.stride,
|
||||
reinterpret_cast<const GLvoid *>(attrib0.byteOffset));
|
||||
if (attrib0.integer) {
|
||||
gl->fVertexAttribIPointer(0,
|
||||
attrib0.size,
|
||||
attrib0.type,
|
||||
attrib0.stride,
|
||||
reinterpret_cast<const GLvoid*>(attrib0.byteOffset));
|
||||
} else {
|
||||
gl->fVertexAttribPointer(0,
|
||||
attrib0.size,
|
||||
attrib0.type,
|
||||
attrib0.normalized,
|
||||
attrib0.stride,
|
||||
reinterpret_cast<const GLvoid*>(attrib0.byteOffset));
|
||||
}
|
||||
} else {
|
||||
gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
|
|
@ -1718,6 +1718,78 @@ WebGLContext::ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLContext::ValidateAttribIndex(GLuint index, const char* info)
|
||||
{
|
||||
bool valid = (index < MaxVertexAttribs());
|
||||
|
||||
if (!valid) {
|
||||
if (index == GLuint(-1)) {
|
||||
ErrorInvalidValue("%s: -1 is not a valid `index`. This value"
|
||||
" probably comes from a getAttribLocation()"
|
||||
" call, where this return value -1 means"
|
||||
" that the passed name didn't correspond to"
|
||||
" an active attribute in the specified"
|
||||
" program.", info);
|
||||
} else {
|
||||
ErrorInvalidValue("%s: `index` must be less than"
|
||||
" MAX_VERTEX_ATTRIBS.", info);
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLContext::ValidateAttribPointer(bool integerMode, GLuint index, GLint size, GLenum type,
|
||||
WebGLboolean normalized, GLsizei stride,
|
||||
WebGLintptr byteOffset, const char* info)
|
||||
{
|
||||
WebGLBuffer* buffer = mBoundArrayBuffer;
|
||||
if (!buffer) {
|
||||
ErrorInvalidOperation("%s: must have valid GL_ARRAY_BUFFER binding", info);
|
||||
return false;
|
||||
}
|
||||
|
||||
GLsizei requiredAlignment = 0;
|
||||
if (!ValidateAttribPointerType(integerMode, type, &requiredAlignment, info))
|
||||
return false;
|
||||
|
||||
// requiredAlignment should always be a power of two
|
||||
MOZ_ASSERT(IsPOTAssumingNonnegative(requiredAlignment));
|
||||
GLsizei requiredAlignmentMask = requiredAlignment - 1;
|
||||
|
||||
if (size < 1 || size > 4) {
|
||||
ErrorInvalidValue("%s: invalid element size", info);
|
||||
return false;
|
||||
}
|
||||
|
||||
// see WebGL spec section 6.6 "Vertex Attribute Data Stride"
|
||||
if (stride < 0 || stride > 255) {
|
||||
ErrorInvalidValue("%s: negative or too large stride", info);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (byteOffset < 0) {
|
||||
ErrorInvalidValue("%s: negative offset", info);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stride & requiredAlignmentMask) {
|
||||
ErrorInvalidOperation("%s: stride doesn't satisfy the alignment "
|
||||
"requirement of given type", info);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (byteOffset & requiredAlignmentMask) {
|
||||
ErrorInvalidOperation("%s: byteOffset doesn't satisfy the alignment "
|
||||
"requirement of given type", info);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLContext::ValidateStencilParamsForDrawCall()
|
||||
{
|
||||
|
|
|
@ -20,34 +20,13 @@
|
|||
using namespace mozilla;
|
||||
using namespace dom;
|
||||
|
||||
static bool
|
||||
CheckAttribIndex(WebGLContext& webgl, GLuint index, const char* info)
|
||||
{
|
||||
if (index >= webgl.MaxVertexAttribs()) {
|
||||
if (index == GLuint(-1)) {
|
||||
webgl.ErrorInvalidValue("%s: -1 is not a valid `index`. This value"
|
||||
" probably comes from a getAttribLocation()"
|
||||
" call, where this return value -1 means"
|
||||
" that the passed name didn't correspond to"
|
||||
" an active attribute in the specified"
|
||||
" program.", info);
|
||||
} else {
|
||||
webgl.ErrorInvalidValue("%s: `index` must be less than"
|
||||
" MAX_VERTEX_ATTRIBS.", info);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::VertexAttrib1f(GLuint index, GLfloat x0)
|
||||
{
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!CheckAttribIndex(*this, index, "vertexAttrib1f"))
|
||||
if (!ValidateAttribIndex(index, "vertexAttrib1f"))
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
@ -70,7 +49,7 @@ WebGLContext::VertexAttrib2f(GLuint index, GLfloat x0, GLfloat x1)
|
|||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!CheckAttribIndex(*this, index, "vertexAttrib2f"))
|
||||
if (!ValidateAttribIndex(index, "vertexAttrib2f"))
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
@ -93,7 +72,7 @@ WebGLContext::VertexAttrib3f(GLuint index, GLfloat x0, GLfloat x1, GLfloat x2)
|
|||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!CheckAttribIndex(*this, index, "vertexAttrib3f"))
|
||||
if (!ValidateAttribIndex(index, "vertexAttrib3f"))
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
@ -117,7 +96,7 @@ WebGLContext::VertexAttrib4f(GLuint index, GLfloat x0, GLfloat x1,
|
|||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!CheckAttribIndex(*this, index, "vertexAttrib4f"))
|
||||
if (!ValidateAttribIndex(index, "vertexAttrib4f"))
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
@ -142,7 +121,7 @@ WebGLContext::VertexAttrib1fv_base(GLuint index, uint32_t arrayLength,
|
|||
if (!ValidateAttribArraySetter("VertexAttrib1fv", 1, arrayLength))
|
||||
return;
|
||||
|
||||
if (!CheckAttribIndex(*this, index, "vertexAttrib1fv"))
|
||||
if (!ValidateAttribIndex(index, "vertexAttrib1fv"))
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
@ -165,7 +144,7 @@ WebGLContext::VertexAttrib2fv_base(GLuint index, uint32_t arrayLength,
|
|||
if (!ValidateAttribArraySetter("VertexAttrib2fv", 2, arrayLength))
|
||||
return;
|
||||
|
||||
if (!CheckAttribIndex(*this, index, "vertexAttrib2fv"))
|
||||
if (!ValidateAttribIndex(index, "vertexAttrib2fv"))
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
@ -188,7 +167,7 @@ WebGLContext::VertexAttrib3fv_base(GLuint index, uint32_t arrayLength,
|
|||
if (!ValidateAttribArraySetter("VertexAttrib3fv", 3, arrayLength))
|
||||
return;
|
||||
|
||||
if (!CheckAttribIndex(*this, index, "vertexAttrib3fv"))
|
||||
if (!ValidateAttribIndex(index, "vertexAttrib3fv"))
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
@ -211,7 +190,7 @@ WebGLContext::VertexAttrib4fv_base(GLuint index, uint32_t arrayLength,
|
|||
if (!ValidateAttribArraySetter("VertexAttrib4fv", 4, arrayLength))
|
||||
return;
|
||||
|
||||
if (!CheckAttribIndex(*this, index, "vertexAttrib4fv"))
|
||||
if (!ValidateAttribIndex(index, "vertexAttrib4fv"))
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
@ -233,7 +212,7 @@ WebGLContext::EnableVertexAttribArray(GLuint index)
|
|||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!CheckAttribIndex(*this, index, "enableVertexAttribArray"))
|
||||
if (!ValidateAttribIndex(index, "enableVertexAttribArray"))
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
@ -252,7 +231,7 @@ WebGLContext::DisableVertexAttribArray(GLuint index)
|
|||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!CheckAttribIndex(*this, index, "disableVertexAttribArray"))
|
||||
if (!ValidateAttribIndex(index, "disableVertexAttribArray"))
|
||||
return;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
@ -273,7 +252,7 @@ WebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
|
|||
if (IsContextLost())
|
||||
return JS::NullValue();
|
||||
|
||||
if (!CheckAttribIndex(*this, index, "getVertexAttrib"))
|
||||
if (!ValidateAttribIndex(index, "getVertexAttrib"))
|
||||
return JS::NullValue();
|
||||
|
||||
MOZ_ASSERT(mBoundVertexArray);
|
||||
|
@ -359,7 +338,7 @@ WebGLContext::GetVertexAttribOffset(GLuint index, GLenum pname)
|
|||
if (IsContextLost())
|
||||
return 0;
|
||||
|
||||
if (!CheckAttribIndex(*this, index, "getVertexAttribOffset"))
|
||||
if (!ValidateAttribIndex(index, "getVertexAttribOffset"))
|
||||
return 0;
|
||||
|
||||
if (pname != LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER) {
|
||||
|
@ -380,56 +359,15 @@ WebGLContext::VertexAttribPointer(GLuint index, GLint size, GLenum type,
|
|||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!CheckAttribIndex(*this, index, "vertexAttribPointer"))
|
||||
if (!ValidateAttribIndex(index, "vertexAttribPointer"))
|
||||
return;
|
||||
|
||||
if (mBoundArrayBuffer == nullptr)
|
||||
return ErrorInvalidOperation("vertexAttribPointer: must have valid GL_ARRAY_BUFFER binding");
|
||||
|
||||
GLsizei requiredAlignment = 1;
|
||||
switch (type) {
|
||||
case LOCAL_GL_BYTE:
|
||||
case LOCAL_GL_UNSIGNED_BYTE:
|
||||
requiredAlignment = 1;
|
||||
break;
|
||||
case LOCAL_GL_SHORT:
|
||||
case LOCAL_GL_UNSIGNED_SHORT:
|
||||
requiredAlignment = 2;
|
||||
break;
|
||||
// XXX case LOCAL_GL_FIXED:
|
||||
case LOCAL_GL_FLOAT:
|
||||
requiredAlignment = 4;
|
||||
break;
|
||||
default:
|
||||
return ErrorInvalidEnumInfo("vertexAttribPointer: type", type);
|
||||
}
|
||||
|
||||
// requiredAlignment should always be a power of two.
|
||||
GLsizei requiredAlignmentMask = requiredAlignment - 1;
|
||||
if (!ValidateAttribPointer(false, index, size, type, normalized, stride, byteOffset, "vertexAttribPointer"))
|
||||
return;
|
||||
|
||||
MOZ_ASSERT(mBoundVertexArray);
|
||||
mBoundVertexArray->EnsureAttrib(index);
|
||||
|
||||
if (size < 1 || size > 4)
|
||||
return ErrorInvalidValue("vertexAttribPointer: invalid element size");
|
||||
|
||||
if (stride < 0 || stride > 255) // see WebGL spec section 6.6 "Vertex Attribute Data Stride"
|
||||
return ErrorInvalidValue("vertexAttribPointer: negative or too large stride");
|
||||
|
||||
if (byteOffset < 0)
|
||||
return ErrorInvalidValue("vertexAttribPointer: negative offset");
|
||||
|
||||
if (stride & requiredAlignmentMask) {
|
||||
return ErrorInvalidOperation("vertexAttribPointer: stride doesn't satisfy the alignment "
|
||||
"requirement of given type");
|
||||
}
|
||||
|
||||
if (byteOffset & requiredAlignmentMask) {
|
||||
return ErrorInvalidOperation("vertexAttribPointer: byteOffset doesn't satisfy the alignment "
|
||||
"requirement of given type");
|
||||
|
||||
}
|
||||
|
||||
InvalidateBufferFetching();
|
||||
|
||||
/* XXX make work with bufferSubData & heterogeneous types
|
||||
|
@ -445,11 +383,10 @@ WebGLContext::VertexAttribPointer(GLuint index, GLint size, GLenum type,
|
|||
vd.byteOffset = byteOffset;
|
||||
vd.type = type;
|
||||
vd.normalized = normalized;
|
||||
vd.integer = false;
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
gl->fVertexAttribPointer(index, size, type, normalized,
|
||||
stride,
|
||||
gl->fVertexAttribPointer(index, size, type, normalized, stride,
|
||||
reinterpret_cast<void*>(byteOffset));
|
||||
}
|
||||
|
||||
|
@ -459,7 +396,7 @@ WebGLContext::VertexAttribDivisor(GLuint index, GLuint divisor)
|
|||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!CheckAttribIndex(*this, index, "vertexAttribDivisor"))
|
||||
if (!ValidateAttribIndex(index, "vertexAttribDivisor"))
|
||||
return;
|
||||
|
||||
MOZ_ASSERT(mBoundVertexArray);
|
||||
|
|
|
@ -74,6 +74,7 @@ protected:
|
|||
|
||||
friend class WebGLContext;
|
||||
friend class WebGLVertexArrayFake;
|
||||
friend class WebGL2Context;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -29,8 +29,13 @@ WebGLVertexArrayFake::BindVertexArrayImpl()
|
|||
|
||||
mContext->BindBuffer(LOCAL_GL_ARRAY_BUFFER, vd.buf);
|
||||
|
||||
gl->fVertexAttribPointer(i, vd.size, vd.type, vd.normalized, vd.stride,
|
||||
reinterpret_cast<void*>(vd.byteOffset));
|
||||
if (vd.integer) {
|
||||
gl->fVertexAttribIPointer(i, vd.size, vd.type, vd.stride,
|
||||
reinterpret_cast<const GLvoid*>(vd.byteOffset));
|
||||
} else {
|
||||
gl->fVertexAttribPointer(i, vd.size, vd.type, vd.normalized, vd.stride,
|
||||
reinterpret_cast<const GLvoid*>(vd.byteOffset));
|
||||
}
|
||||
|
||||
if (vd.enabled)
|
||||
gl->fEnableVertexAttribArray(i);
|
||||
|
|
|
@ -25,6 +25,7 @@ struct WebGLVertexAttribData
|
|||
, type(LOCAL_GL_FLOAT)
|
||||
, enabled(false)
|
||||
, normalized(false)
|
||||
, integer(false)
|
||||
{}
|
||||
|
||||
WebGLRefPtr<WebGLBuffer> buf;
|
||||
|
@ -35,6 +36,7 @@ struct WebGLVertexAttribData
|
|||
GLenum type;
|
||||
bool enabled;
|
||||
bool normalized;
|
||||
bool integer;
|
||||
|
||||
GLuint componentSize() const {
|
||||
switch(type) {
|
||||
|
|
|
@ -42,6 +42,7 @@ UNIFIED_SOURCES += [
|
|||
UNIFIED_SOURCES += [
|
||||
'MurmurHash3.cpp',
|
||||
'WebGL1Context.cpp',
|
||||
'WebGL1ContextUniforms.cpp',
|
||||
'WebGL2Context.cpp',
|
||||
'WebGL2ContextBuffers.cpp',
|
||||
'WebGL2ContextDraw.cpp',
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
setup({allow_uncaught_exception:true});
|
||||
var errorEvent;
|
||||
var file;
|
||||
var line;
|
||||
|
@ -31,8 +32,8 @@
|
|||
generate_tests(assert_equals, [
|
||||
[ "Event filename", errorEvent.filename, location.href ],
|
||||
[ "Callback filename", file, location.href ],
|
||||
[ "Event line number", errorEvent.lineno, 27 ],
|
||||
[ "Callback line number", line, 27 ],
|
||||
[ "Event line number", errorEvent.lineno, 28 ],
|
||||
[ "Callback line number", line, 28 ],
|
||||
[ "Event message", errorEvent.message, "Error: hello" ],
|
||||
[ "Callback message", msg, "Error: hello" ],
|
||||
[ "Event error-object", errorEvent.error, thrown],
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
return tokens;
|
||||
};
|
||||
|
||||
var parse = function (tokens) {
|
||||
var parse = function (tokens, opt) {
|
||||
var line = 1;
|
||||
tokens = tokens.slice();
|
||||
|
||||
|
@ -82,14 +82,41 @@
|
|||
}
|
||||
};
|
||||
|
||||
var all_ws = function () {
|
||||
var all_ws = function (store, pea) { // pea == post extended attribute, tpea = same for types
|
||||
var t = { type: "whitespace", value: "" };
|
||||
while (true) {
|
||||
var w = ws();
|
||||
if (!w) break;
|
||||
t.value += w.value;
|
||||
}
|
||||
if (t.value.length > 0) return t;
|
||||
if (t.value.length > 0) {
|
||||
if (store) {
|
||||
var w = t.value
|
||||
, re = {
|
||||
"ws": /^([\t\n\r ]+)/
|
||||
, "line-comment": /^\/\/(.*)\n?/m
|
||||
, "multiline-comment": /^\/\*((?:.|\n|\r)*?)\*\//
|
||||
}
|
||||
, wsTypes = []
|
||||
;
|
||||
for (var k in re) wsTypes.push(k);
|
||||
while (w.length) {
|
||||
var matched = false;
|
||||
for (var i = 0, n = wsTypes.length; i < n; i++) {
|
||||
var type = wsTypes[i];
|
||||
w = w.replace(re[type], function (tok, m1) {
|
||||
store.push({ type: type + (pea ? ("-" + pea) : ""), value: m1 });
|
||||
matched = true;
|
||||
return "";
|
||||
});
|
||||
if (matched) break;
|
||||
}
|
||||
if (matched) continue;
|
||||
throw new Error("Surprising white space construct."); // this shouldn't happen
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
var integer_type = function () {
|
||||
|
@ -151,8 +178,15 @@
|
|||
else if (consume(OTHER, "[")) {
|
||||
all_ws();
|
||||
consume(OTHER, "]") || error("Unterminated array type");
|
||||
if (!obj.array) obj.array = 1;
|
||||
else obj.array++;
|
||||
if (!obj.array) {
|
||||
obj.array = 1;
|
||||
obj.nullableArray = [obj.nullable];
|
||||
}
|
||||
else {
|
||||
obj.array++;
|
||||
obj.nullableArray.push(obj.nullable);
|
||||
}
|
||||
obj.nullable = false;
|
||||
}
|
||||
else return;
|
||||
}
|
||||
|
@ -160,40 +194,46 @@
|
|||
|
||||
var single_type = function () {
|
||||
var prim = primitive_type()
|
||||
, ret = { sequence: false, nullable: false, array: false, union: false }
|
||||
, ret = { sequence: false, generic: null, nullable: false, array: false, union: false }
|
||||
, name
|
||||
, value
|
||||
;
|
||||
if (prim) {
|
||||
ret.idlType = prim;
|
||||
}
|
||||
else if (consume(ID, "sequence")) {
|
||||
else if (name = consume(ID)) {
|
||||
value = name.value;
|
||||
all_ws();
|
||||
if (!consume(OTHER, "<")) {
|
||||
ret.idlType = "sequence";
|
||||
}
|
||||
else {
|
||||
ret.sequence = true;
|
||||
ret.idlType = type() || error("Error parsing sequence type");
|
||||
// Generic types
|
||||
if (consume(OTHER, "<")) {
|
||||
// backwards compat
|
||||
if (value === "sequence") {
|
||||
ret.sequence = true;
|
||||
}
|
||||
ret.generic = value;
|
||||
ret.idlType = type() || error("Error parsing generic type " + value);
|
||||
all_ws();
|
||||
if (!consume(OTHER, ">")) error("Unterminated sequence");
|
||||
if (!consume(OTHER, ">")) error("Unterminated generic type " + value);
|
||||
all_ws();
|
||||
if (consume(OTHER, "?")) ret.nullable = true;
|
||||
return ret;
|
||||
}
|
||||
else {
|
||||
ret.idlType = value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
var name = consume(ID);
|
||||
if (!name) return;
|
||||
ret.idlType = name.value;
|
||||
return;
|
||||
}
|
||||
type_suffix(ret);
|
||||
if (ret.nullable && ret.idlType === "any") error("Type any cannot be made nullable");
|
||||
if (ret.nullable && !ret.array && ret.idlType === "any") error("Type any cannot be made nullable");
|
||||
return ret;
|
||||
};
|
||||
|
||||
var union_type = function () {
|
||||
all_ws();
|
||||
if (!consume(OTHER, "(")) return;
|
||||
var ret = { sequence: false, nullable: false, array: false, union: true, idlType: [] };
|
||||
var ret = { sequence: false, generic: null, nullable: false, array: false, union: true, idlType: [] };
|
||||
var fst = type() || error("Union type with no content");
|
||||
ret.idlType.push(fst);
|
||||
while (true) {
|
||||
|
@ -211,16 +251,21 @@
|
|||
return single_type() || union_type();
|
||||
};
|
||||
|
||||
var argument = function () {
|
||||
var argument = function (store) {
|
||||
var ret = { optional: false, variadic: false };
|
||||
ret.extAttrs = extended_attrs();
|
||||
all_ws();
|
||||
if (consume(ID, "optional")) {
|
||||
ret.extAttrs = extended_attrs(store);
|
||||
all_ws(store, "pea");
|
||||
var opt_token = consume(ID, "optional");
|
||||
if (opt_token) {
|
||||
ret.optional = true;
|
||||
all_ws();
|
||||
}
|
||||
ret.idlType = type();
|
||||
if (!ret.idlType) return;
|
||||
if (!ret.idlType) {
|
||||
if (opt_token) tokens.unshift(opt_token);
|
||||
return;
|
||||
}
|
||||
var type_token = last_token;
|
||||
if (!ret.optional) {
|
||||
all_ws();
|
||||
if (tokens.length >= 3 &&
|
||||
|
@ -235,7 +280,12 @@
|
|||
}
|
||||
}
|
||||
all_ws();
|
||||
var name = consume(ID) || error("No name in argument");
|
||||
var name = consume(ID);
|
||||
if (!name) {
|
||||
if (opt_token) tokens.unshift(opt_token);
|
||||
tokens.unshift(type_token);
|
||||
return;
|
||||
}
|
||||
ret.name = name.value;
|
||||
if (ret.optional) {
|
||||
all_ws();
|
||||
|
@ -244,20 +294,33 @@
|
|||
return ret;
|
||||
};
|
||||
|
||||
var argument_list = function () {
|
||||
var arg = argument(), ret = [];
|
||||
if (!arg) return ret;
|
||||
var argument_list = function (store) {
|
||||
var ret = []
|
||||
, arg = argument(store ? ret : null)
|
||||
;
|
||||
if (!arg) return;
|
||||
ret.push(arg);
|
||||
while (true) {
|
||||
all_ws();
|
||||
all_ws(store ? ret : null);
|
||||
if (!consume(OTHER, ",")) return ret;
|
||||
all_ws();
|
||||
var nxt = argument() || error("Trailing comma in arguments list");
|
||||
var nxt = argument(store ? ret : null) || error("Trailing comma in arguments list");
|
||||
ret.push(nxt);
|
||||
}
|
||||
};
|
||||
|
||||
var simple_extended_attr = function () {
|
||||
var type_pair = function () {
|
||||
all_ws();
|
||||
var k = type();
|
||||
if (!k) return;
|
||||
all_ws()
|
||||
if (!consume(OTHER, ",")) return;
|
||||
all_ws();
|
||||
var v = type();
|
||||
if (!v) return;
|
||||
return [k, v];
|
||||
};
|
||||
|
||||
var simple_extended_attr = function (store) {
|
||||
all_ws();
|
||||
var name = consume(ID);
|
||||
if (!name) return;
|
||||
|
@ -274,24 +337,35 @@
|
|||
}
|
||||
all_ws();
|
||||
if (consume(OTHER, "(")) {
|
||||
ret["arguments"] = argument_list();
|
||||
var args, pair;
|
||||
// [Constructor(DOMString str)]
|
||||
if (args = argument_list(store)) {
|
||||
ret["arguments"] = args;
|
||||
}
|
||||
// [MapClass(DOMString, DOMString)]
|
||||
else if (pair = type_pair()) {
|
||||
ret.typePair = pair;
|
||||
}
|
||||
// [Constructor()]
|
||||
else {
|
||||
ret["arguments"] = [];
|
||||
}
|
||||
all_ws();
|
||||
consume(OTHER, ")") || error("Unclosed argument in extended attribute");
|
||||
consume(OTHER, ")") || error("Unexpected token in extended attribute argument list or type pair");
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
// Note: we parse something simpler than the official syntax. It's all that ever
|
||||
// seems to be used
|
||||
var extended_attrs = function () {
|
||||
var extended_attrs = function (store) {
|
||||
var eas = [];
|
||||
all_ws();
|
||||
all_ws(store);
|
||||
if (!consume(OTHER, "[")) return eas;
|
||||
eas[0] = simple_extended_attr() || error("Extended attribute with not content");
|
||||
eas[0] = simple_extended_attr(store) || error("Extended attribute with not content");
|
||||
all_ws();
|
||||
while (consume(OTHER, ",")) {
|
||||
all_ws();
|
||||
eas.push(simple_extended_attr() || error("Trailing comma in extended attribute"));
|
||||
eas.push(simple_extended_attr(store) || error("Trailing comma in extended attribute"));
|
||||
all_ws();
|
||||
}
|
||||
consume(OTHER, "]") || error("No end of extended attribute");
|
||||
|
@ -314,8 +388,8 @@
|
|||
}
|
||||
};
|
||||
|
||||
var const_ = function () {
|
||||
all_ws();
|
||||
var const_ = function (store) {
|
||||
all_ws(store, "pea");
|
||||
if (!consume(ID, "const")) return;
|
||||
var ret = { type: "const", nullable: false };
|
||||
all_ws();
|
||||
|
@ -352,14 +426,14 @@
|
|||
}
|
||||
};
|
||||
|
||||
var operation_rest = function (ret) {
|
||||
var operation_rest = function (ret, store) {
|
||||
all_ws();
|
||||
if (!ret) ret = {};
|
||||
var name = consume(ID);
|
||||
ret.name = name ? name.value : null;
|
||||
all_ws();
|
||||
consume(OTHER, "(") || error("Invalid operation");
|
||||
ret["arguments"] = argument_list();
|
||||
ret["arguments"] = argument_list(store) || [];
|
||||
all_ws();
|
||||
consume(OTHER, ")") || error("Unterminated operation");
|
||||
all_ws();
|
||||
|
@ -367,8 +441,8 @@
|
|||
return ret;
|
||||
};
|
||||
|
||||
var callback = function () {
|
||||
all_ws();
|
||||
var callback = function (store) {
|
||||
all_ws(store, "pea");
|
||||
var ret;
|
||||
if (!consume(ID, "callback")) return;
|
||||
all_ws();
|
||||
|
@ -387,7 +461,7 @@
|
|||
ret.idlType = return_type();
|
||||
all_ws();
|
||||
consume(OTHER, "(") || error("No arguments in callback");
|
||||
ret["arguments"] = argument_list();
|
||||
ret["arguments"] = argument_list(store) || [];
|
||||
all_ws();
|
||||
consume(OTHER, ")") || error("Unterminated callback");
|
||||
all_ws();
|
||||
|
@ -395,8 +469,8 @@
|
|||
return ret;
|
||||
};
|
||||
|
||||
var attribute = function () {
|
||||
all_ws();
|
||||
var attribute = function (store) {
|
||||
all_ws(store, "pea");
|
||||
var grabbed = []
|
||||
, ret = {
|
||||
type: "attribute"
|
||||
|
@ -454,8 +528,8 @@
|
|||
return typ;
|
||||
};
|
||||
|
||||
var operation = function () {
|
||||
all_ws();
|
||||
var operation = function (store) {
|
||||
all_ws(store, "pea");
|
||||
var ret = {
|
||||
type: "operation"
|
||||
, getter: false
|
||||
|
@ -478,13 +552,13 @@
|
|||
if (ret.getter || ret.setter || ret.creator || ret.deleter || ret.legacycaller) {
|
||||
all_ws();
|
||||
ret.idlType = return_type();
|
||||
operation_rest(ret);
|
||||
operation_rest(ret, store);
|
||||
return ret;
|
||||
}
|
||||
if (consume(ID, "static")) {
|
||||
ret["static"] = true;
|
||||
ret.idlType = return_type();
|
||||
operation_rest(ret);
|
||||
operation_rest(ret, store);
|
||||
return ret;
|
||||
}
|
||||
else if (consume(ID, "stringifier")) {
|
||||
|
@ -492,7 +566,7 @@
|
|||
all_ws();
|
||||
if (consume(OTHER, ";")) return ret;
|
||||
ret.idlType = return_type();
|
||||
operation_rest(ret);
|
||||
operation_rest(ret, store);
|
||||
return ret;
|
||||
}
|
||||
ret.idlType = return_type();
|
||||
|
@ -513,7 +587,7 @@
|
|||
return ret;
|
||||
}
|
||||
else {
|
||||
operation_rest(ret);
|
||||
operation_rest(ret, store);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
@ -530,8 +604,8 @@
|
|||
}
|
||||
};
|
||||
|
||||
var serialiser = function () {
|
||||
all_ws();
|
||||
var serialiser = function (store) {
|
||||
all_ws(store, "pea");
|
||||
if (!consume(ID, "serializer")) return;
|
||||
var ret = { type: "serializer" };
|
||||
all_ws();
|
||||
|
@ -589,77 +663,84 @@
|
|||
else {
|
||||
ret.idlType = return_type();
|
||||
all_ws();
|
||||
ret.operation = operation_rest();
|
||||
ret.operation = operation_rest(null, store);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
var interface_ = function (isPartial) {
|
||||
all_ws();
|
||||
var interface_ = function (isPartial, store) {
|
||||
all_ws(isPartial ? null : store, "pea");
|
||||
if (!consume(ID, "interface")) return;
|
||||
all_ws();
|
||||
var name = consume(ID) || error("No name for interface");
|
||||
var ret = {
|
||||
var mems = []
|
||||
, ret = {
|
||||
type: "interface"
|
||||
, name: name.value
|
||||
, partial: false
|
||||
, members: []
|
||||
, members: mems
|
||||
};
|
||||
if (!isPartial) ret.inheritance = inheritance() || null;
|
||||
all_ws();
|
||||
consume(OTHER, "{") || error("Bodyless interface");
|
||||
while (true) {
|
||||
all_ws();
|
||||
all_ws(store ? mems : null);
|
||||
if (consume(OTHER, "}")) {
|
||||
all_ws();
|
||||
consume(OTHER, ";") || error("Missing semicolon after interface");
|
||||
return ret;
|
||||
}
|
||||
var ea = extended_attrs();
|
||||
var ea = extended_attrs(store ? mems : null);
|
||||
all_ws();
|
||||
var cnt = const_();
|
||||
var cnt = const_(store ? mems : null);
|
||||
if (cnt) {
|
||||
cnt.extAttrs = ea;
|
||||
ret.members.push(cnt);
|
||||
continue;
|
||||
}
|
||||
var mem = serialiser() || attribute() || operation() || error("Unknown member");
|
||||
var mem = serialiser(store ? mems : null) ||
|
||||
attribute(store ? mems : null) ||
|
||||
operation(store ? mems : null) ||
|
||||
error("Unknown member");
|
||||
mem.extAttrs = ea;
|
||||
ret.members.push(mem);
|
||||
}
|
||||
};
|
||||
|
||||
var partial = function () {
|
||||
all_ws();
|
||||
var partial = function (store) {
|
||||
all_ws(store, "pea");
|
||||
if (!consume(ID, "partial")) return;
|
||||
var thing = dictionary(true) || interface_(true) || error("Partial doesn't apply to anything");
|
||||
var thing = dictionary(true, store) ||
|
||||
interface_(true, store) ||
|
||||
error("Partial doesn't apply to anything");
|
||||
thing.partial = true;
|
||||
return thing;
|
||||
};
|
||||
|
||||
var dictionary = function (isPartial) {
|
||||
all_ws();
|
||||
var dictionary = function (isPartial, store) {
|
||||
all_ws(isPartial ? null : store, "pea");
|
||||
if (!consume(ID, "dictionary")) return;
|
||||
all_ws();
|
||||
var name = consume(ID) || error("No name for dictionary");
|
||||
var ret = {
|
||||
var mems = []
|
||||
, ret = {
|
||||
type: "dictionary"
|
||||
, name: name.value
|
||||
, partial: false
|
||||
, members: []
|
||||
, members: mems
|
||||
};
|
||||
if (!isPartial) ret.inheritance = inheritance() || null;
|
||||
all_ws();
|
||||
consume(OTHER, "{") || error("Bodyless dictionary");
|
||||
while (true) {
|
||||
all_ws();
|
||||
all_ws(store ? mems : null);
|
||||
if (consume(OTHER, "}")) {
|
||||
all_ws();
|
||||
consume(OTHER, ";") || error("Missing semicolon after dictionary");
|
||||
return ret;
|
||||
}
|
||||
var ea = extended_attrs();
|
||||
all_ws();
|
||||
var ea = extended_attrs(store ? mems : null);
|
||||
all_ws(store ? mems : null, "pea");
|
||||
var typ = type() || error("No type for dictionary member");
|
||||
all_ws();
|
||||
var name = consume(ID) || error("No name for dictionary member");
|
||||
|
@ -675,28 +756,29 @@
|
|||
}
|
||||
};
|
||||
|
||||
var exception = function () {
|
||||
all_ws();
|
||||
var exception = function (store) {
|
||||
all_ws(store, "pea");
|
||||
if (!consume(ID, "exception")) return;
|
||||
all_ws();
|
||||
var name = consume(ID) || error("No name for exception");
|
||||
var ret = {
|
||||
var mems = []
|
||||
, ret = {
|
||||
type: "exception"
|
||||
, name: name.value
|
||||
, members: []
|
||||
, members: mems
|
||||
};
|
||||
ret.inheritance = inheritance() || null;
|
||||
all_ws();
|
||||
consume(OTHER, "{") || error("Bodyless exception");
|
||||
while (true) {
|
||||
all_ws();
|
||||
all_ws(store ? mems : null);
|
||||
if (consume(OTHER, "}")) {
|
||||
all_ws();
|
||||
consume(OTHER, ";") || error("Missing semicolon after exception");
|
||||
return ret;
|
||||
}
|
||||
var ea = extended_attrs();
|
||||
all_ws();
|
||||
var ea = extended_attrs(store ? mems : null);
|
||||
all_ws(store ? mems : null, "pea");
|
||||
var cnt = const_();
|
||||
if (cnt) {
|
||||
cnt.extAttrs = ea;
|
||||
|
@ -718,21 +800,22 @@
|
|||
}
|
||||
};
|
||||
|
||||
var enum_ = function () {
|
||||
all_ws();
|
||||
var enum_ = function (store) {
|
||||
all_ws(store, "pea");
|
||||
if (!consume(ID, "enum")) return;
|
||||
all_ws();
|
||||
var name = consume(ID) || error("No name for enum");
|
||||
var ret = {
|
||||
var vals = []
|
||||
, ret = {
|
||||
type: "enum"
|
||||
, name: name.value
|
||||
, values: []
|
||||
, values: vals
|
||||
};
|
||||
all_ws();
|
||||
consume(OTHER, "{") || error("No curly for enum");
|
||||
var saw_comma = false;
|
||||
while (true) {
|
||||
all_ws();
|
||||
all_ws(store ? vals : null);
|
||||
if (consume(OTHER, "}")) {
|
||||
all_ws();
|
||||
if (saw_comma) error("Trailing comma in enum");
|
||||
|
@ -741,9 +824,10 @@
|
|||
}
|
||||
var val = consume(STR) || error("Unexpected value in enum");
|
||||
ret.values.push(val.value.replace(/"/g, ""));
|
||||
all_ws();
|
||||
all_ws(store ? vals : null);
|
||||
if (consume(OTHER, ",")) {
|
||||
all_ws();
|
||||
if (store) vals.push({ type: "," });
|
||||
all_ws(store ? vals : null);
|
||||
saw_comma = true;
|
||||
}
|
||||
else {
|
||||
|
@ -752,15 +836,15 @@
|
|||
}
|
||||
};
|
||||
|
||||
var typedef = function () {
|
||||
all_ws();
|
||||
var typedef = function (store) {
|
||||
all_ws(store, "pea");
|
||||
if (!consume(ID, "typedef")) return;
|
||||
var ret = {
|
||||
type: "typedef"
|
||||
};
|
||||
all_ws();
|
||||
ret.typeExtAttrs = extended_attrs();
|
||||
all_ws();
|
||||
all_ws(store, "tpea");
|
||||
ret.idlType = type() || error("No type in typedef");
|
||||
all_ws();
|
||||
var name = consume(ID) || error("No name in typedef");
|
||||
|
@ -770,8 +854,8 @@
|
|||
return ret;
|
||||
};
|
||||
|
||||
var implements_ = function () {
|
||||
all_ws();
|
||||
var implements_ = function (store) {
|
||||
all_ws(store, "pea");
|
||||
var target = consume(ID);
|
||||
if (!target) return;
|
||||
var w = all_ws();
|
||||
|
@ -794,24 +878,24 @@
|
|||
}
|
||||
};
|
||||
|
||||
var definition = function () {
|
||||
return callback() ||
|
||||
interface_() ||
|
||||
partial() ||
|
||||
dictionary() ||
|
||||
exception() ||
|
||||
enum_() ||
|
||||
typedef() ||
|
||||
implements_()
|
||||
var definition = function (store) {
|
||||
return callback(store) ||
|
||||
interface_(false, store) ||
|
||||
partial(store) ||
|
||||
dictionary(false, store) ||
|
||||
exception(store) ||
|
||||
enum_(store) ||
|
||||
typedef(store) ||
|
||||
implements_(store)
|
||||
;
|
||||
};
|
||||
|
||||
var definitions = function () {
|
||||
var definitions = function (store) {
|
||||
if (!tokens.length) return [];
|
||||
var defs = [];
|
||||
while (true) {
|
||||
var ea = extended_attrs()
|
||||
, def = definition();
|
||||
var ea = extended_attrs(store ? defs : null)
|
||||
, def = definition(store ? defs : null);
|
||||
if (!def) {
|
||||
if (ea.length) error("Stray extended attributes");
|
||||
break;
|
||||
|
@ -821,21 +905,20 @@
|
|||
}
|
||||
return defs;
|
||||
};
|
||||
var res = definitions();
|
||||
var res = definitions(opt.ws);
|
||||
if (tokens.length) error("Unrecognised tokens");
|
||||
return res;
|
||||
};
|
||||
|
||||
var obj = {
|
||||
parse: function (str) {
|
||||
var tokens = tokenise(str);
|
||||
return parse(tokens);
|
||||
}
|
||||
var inNode = typeof module !== "undefined" && module.exports
|
||||
, obj = {
|
||||
parse: function (str, opt) {
|
||||
if (!opt) opt = {};
|
||||
var tokens = tokenise(str);
|
||||
return parse(tokens, opt);
|
||||
}
|
||||
};
|
||||
if (typeof module !== "undefined" && module.exports) {
|
||||
module.exports = obj;
|
||||
}
|
||||
else {
|
||||
window.WebIDL2 = obj;
|
||||
}
|
||||
|
||||
if (inNode) module.exports = obj;
|
||||
else window.WebIDL2 = obj;
|
||||
}());
|
||||
|
|
|
@ -20,6 +20,10 @@ for documentation.
|
|||
<script>
|
||||
"use strict";
|
||||
|
||||
if (W3CTest.runner) {
|
||||
W3CTest.runner.requestLongerTimeout(2);
|
||||
}
|
||||
|
||||
runTests();
|
||||
|
||||
function runTests() {
|
||||
|
|
|
@ -8,131 +8,7 @@ policies and contribution forms [3].
|
|||
[3] http://www.w3.org/2004/10/27-testcases
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file automatically generates browser tests for WebIDL interfaces, using
|
||||
* the testharness.js framework. To use, first include the following:
|
||||
*
|
||||
* <script src=/resources/testharness.js></script>
|
||||
* <script src=/resources/testharnessreport.js></script>
|
||||
* <script src=/resources/WebIDLParser.js></script>
|
||||
* <script src=/resources/idlharness.js></script>
|
||||
*
|
||||
* Then you'll need some type of IDLs. Here's some script that can be run on a
|
||||
* spec written in HTML, which will grab all the elements with class="idl",
|
||||
* concatenate them, and replace the body so you can copy-paste:
|
||||
*
|
||||
var s = "";
|
||||
[].forEach.call(document.getElementsByClassName("idl"), function(idl) {
|
||||
//https://www.w3.org/Bugs/Public/show_bug.cgi?id=14914
|
||||
if (!idl.classList.contains("extract"))
|
||||
{
|
||||
s += idl.textContent + "\n\n";
|
||||
}
|
||||
});
|
||||
document.body.innerHTML = '<pre></pre>';
|
||||
document.body.firstChild.textContent = s;
|
||||
*
|
||||
* (TODO: write this in Python or something so that it can be done from the
|
||||
* command line instead.)
|
||||
*
|
||||
* Once you have that, put it in your script somehow. The easiest way is to
|
||||
* embed it literally in an HTML file with <script type=text/plain> or similar,
|
||||
* so that you don't have to do any escaping. Another possibility is to put it
|
||||
* in a separate .idl file that's fetched via XHR or similar. Sample usage:
|
||||
*
|
||||
* var idl_array = new IdlArray();
|
||||
* idl_array.add_untested_idls("interface Node { readonly attribute DOMString nodeName; };");
|
||||
* idl_array.add_idls("interface Document : Node { readonly attribute DOMString URL; };");
|
||||
* idl_array.add_objects({Document: ["document"]});
|
||||
* idl_array.test();
|
||||
*
|
||||
* This tests that window.Document exists and meets all the requirements of
|
||||
* WebIDL. It also tests that window.document (the result of evaluating the
|
||||
* string "document") has URL and nodeName properties that behave as they
|
||||
* should, and otherwise meets WebIDL's requirements for an object whose
|
||||
* primary interface is Document. It does not test that window.Node exists,
|
||||
* which is what you want if the Node interface is already tested in some other
|
||||
* specification's suite and your specification only extends or refers to it.
|
||||
* Of course, each IDL string can define many different things, and calls to
|
||||
* add_objects() can register many different objects for different interfaces:
|
||||
* this is a very simple example.
|
||||
*
|
||||
* TODO: Write assert_writable, assert_enumerable, assert_configurable and
|
||||
* their inverses, and use those instead of just checking
|
||||
* getOwnPropertyDescriptor.
|
||||
*
|
||||
* == Public methods of IdlArray ==
|
||||
*
|
||||
* IdlArray objects can be obtained with new IdlArray(). Anything not
|
||||
* documented in this section should be considered an implementation detail,
|
||||
* and outside callers should not use it.
|
||||
*
|
||||
* add_idls(idl_string):
|
||||
* Parses idl_string (throwing on parse error) and adds the results to the
|
||||
* IdlArray. All the definitions will be tested when you run test(). If
|
||||
* some of the definitions refer to other definitions, those must be present
|
||||
* too. For instance, if idl_string says that Document inherits from Node,
|
||||
* the Node interface must also have been provided in some call to add_idls()
|
||||
* or add_untested_idls().
|
||||
*
|
||||
* add_untested_idls(idl_string):
|
||||
* Like add_idls(), but the definitions will not be tested. If an untested
|
||||
* interface is added and then extended with a tested partial interface, the
|
||||
* members of the partial interface will still be tested. Also, all the
|
||||
* members will still be tested for objects added with add_objects(), because
|
||||
* you probably want to test that (for instance) window.document has all the
|
||||
* properties from Node, not just Document, even if the Node interface itself
|
||||
* is tested in a different test suite.
|
||||
*
|
||||
* add_objects(dict):
|
||||
* dict should be an object whose keys are the names of interfaces or
|
||||
* exceptions, and whose values are arrays of strings. When an interface or
|
||||
* exception is tested, every string registered for it with add_objects()
|
||||
* will be evaluated, and tests will be run on the result to verify that it
|
||||
* correctly implements that interface or exception. This is the only way to
|
||||
* test anything about [NoInterfaceObject] interfaces, and there are many
|
||||
* tests that can't be run on any interface without an object to fiddle with.
|
||||
*
|
||||
* The interface has to be the *primary* interface of all the objects
|
||||
* provided. For example, don't pass {Node: ["document"]}, but rather
|
||||
* {Document: ["document"]}. Assuming the Document interface was declared to
|
||||
* inherit from Node, this will automatically test that document implements
|
||||
* the Node interface too.
|
||||
*
|
||||
* Warning: methods will be called on any provided objects, in a manner that
|
||||
* WebIDL requires be safe. For instance, if a method has mandatory
|
||||
* arguments, the test suite will try calling it with too few arguments to
|
||||
* see if it throws an exception. If an implementation incorrectly runs the
|
||||
* function instead of throwing, this might have side effects, possibly even
|
||||
* preventing the test suite from running correctly.
|
||||
*
|
||||
* prevent_multiple_testing(name):
|
||||
* This is a niche method for use in case you're testing many objects that
|
||||
* implement the same interfaces, and don't want to retest the same
|
||||
* interfaces every single time. For instance, HTML defines many interfaces
|
||||
* that all inherit from HTMLElement, so the HTML test suite has something
|
||||
* like
|
||||
* .add_objects({
|
||||
* HTMLHtmlElement: ['document.documentElement'],
|
||||
* HTMLHeadElement: ['document.head'],
|
||||
* HTMLBodyElement: ['document.body'],
|
||||
* ...
|
||||
* })
|
||||
* and so on for dozens of element types. This would mean that it would
|
||||
* retest that each and every one of those elements implements HTMLElement,
|
||||
* Element, and Node, which would be thousands of basically redundant tests.
|
||||
* The test suite therefore calls prevent_multiple_testing("HTMLElement").
|
||||
* This means that once one object has been tested to implement HTMLElement
|
||||
* and its ancestors, no other object will be. Thus in the example code
|
||||
* above, the harness would test that document.documentElement correctly
|
||||
* implements HTMLHtmlElement, HTMLElement, Element, and Node; but
|
||||
* document.head would only be tested for HTMLHeadElement, and so on for
|
||||
* further objects.
|
||||
*
|
||||
* test():
|
||||
* Run all tests. This should be called after you've called all other
|
||||
* methods to add IDLs and objects.
|
||||
*/
|
||||
/* For user documentation see docs/idlharness.md */
|
||||
|
||||
/**
|
||||
* Notes for people who want to edit this file (not just use it as a library):
|
||||
|
@ -550,6 +426,9 @@ IdlArray.prototype.assert_type_is = function(value, type)
|
|||
return;
|
||||
|
||||
case "DOMString":
|
||||
case "ByteString":
|
||||
case "USVString":
|
||||
// TODO: https://github.com/w3c/testharness.js/issues/92
|
||||
assert_equals(typeof value, "string");
|
||||
return;
|
||||
|
||||
|
@ -1312,6 +1191,249 @@ IdlInterface.prototype.test_self = function()
|
|||
}.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property');
|
||||
};
|
||||
|
||||
//@}
|
||||
IdlInterface.prototype.test_member_const = function(member)
|
||||
//@{
|
||||
{
|
||||
test(function()
|
||||
{
|
||||
assert_own_property(window, this.name,
|
||||
"window does not have own property " + format_value(this.name));
|
||||
|
||||
// "For each constant defined on an interface A, there must be
|
||||
// a corresponding property on the interface object, if it
|
||||
// exists."
|
||||
assert_own_property(window[this.name], member.name);
|
||||
// "The value of the property is that which is obtained by
|
||||
// converting the constant’s IDL value to an ECMAScript
|
||||
// value."
|
||||
assert_equals(window[this.name][member.name], constValue(member.value),
|
||||
"property has wrong value");
|
||||
// "The property has attributes { [[Writable]]: false,
|
||||
// [[Enumerable]]: true, [[Configurable]]: false }."
|
||||
var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name);
|
||||
assert_false("get" in desc, "property has getter");
|
||||
assert_false("set" in desc, "property has setter");
|
||||
assert_false(desc.writable, "property is writable");
|
||||
assert_true(desc.enumerable, "property is not enumerable");
|
||||
assert_false(desc.configurable, "property is configurable");
|
||||
}.bind(this), this.name + " interface: constant " + member.name + " on interface object");
|
||||
// "In addition, a property with the same characteristics must
|
||||
// exist on the interface prototype object."
|
||||
test(function()
|
||||
{
|
||||
assert_own_property(window, this.name,
|
||||
"window does not have own property " + format_value(this.name));
|
||||
|
||||
if (this.has_extended_attribute("Callback")) {
|
||||
assert_false("prototype" in window[this.name],
|
||||
this.name + ' should not have a "prototype" property');
|
||||
return;
|
||||
}
|
||||
|
||||
assert_own_property(window[this.name], "prototype",
|
||||
'interface "' + this.name + '" does not have own property "prototype"');
|
||||
|
||||
assert_own_property(window[this.name].prototype, member.name);
|
||||
assert_equals(window[this.name].prototype[member.name], constValue(member.value),
|
||||
"property has wrong value");
|
||||
var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name);
|
||||
assert_false("get" in desc, "property has getter");
|
||||
assert_false("set" in desc, "property has setter");
|
||||
assert_false(desc.writable, "property is writable");
|
||||
assert_true(desc.enumerable, "property is not enumerable");
|
||||
assert_false(desc.configurable, "property is configurable");
|
||||
}.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object");
|
||||
};
|
||||
|
||||
|
||||
//@}
|
||||
IdlInterface.prototype.test_member_attribute = function(member)
|
||||
//@{
|
||||
{
|
||||
test(function()
|
||||
{
|
||||
assert_own_property(window, this.name,
|
||||
"window does not have own property " + format_value(this.name));
|
||||
assert_own_property(window[this.name], "prototype",
|
||||
'interface "' + this.name + '" does not have own property "prototype"');
|
||||
|
||||
if (member["static"]) {
|
||||
assert_own_property(window[this.name], member.name,
|
||||
"The interface object must have a property " +
|
||||
format_value(member.name));
|
||||
}
|
||||
else
|
||||
{
|
||||
assert_true(member.name in window[this.name].prototype,
|
||||
"The prototype object must have a property " +
|
||||
format_value(member.name));
|
||||
|
||||
if (!member.has_extended_attribute("LenientThis")) {
|
||||
assert_throws(new TypeError(), function() {
|
||||
window[this.name].prototype[member.name];
|
||||
}.bind(this), "getting property on prototype object must throw TypeError");
|
||||
} else {
|
||||
assert_equals(window[this.name].prototype[member.name], undefined,
|
||||
"getting property on prototype object must return undefined");
|
||||
}
|
||||
do_interface_attribute_asserts(window[this.name].prototype, member);
|
||||
}
|
||||
}.bind(this), this.name + " interface: attribute " + member.name);
|
||||
};
|
||||
|
||||
//@}
|
||||
IdlInterface.prototype.test_member_operation = function(member)
|
||||
//@{
|
||||
{
|
||||
test(function()
|
||||
{
|
||||
assert_own_property(window, this.name,
|
||||
"window does not have own property " + format_value(this.name));
|
||||
|
||||
if (this.has_extended_attribute("Callback")) {
|
||||
assert_false("prototype" in window[this.name],
|
||||
this.name + ' should not have a "prototype" property');
|
||||
return;
|
||||
}
|
||||
|
||||
assert_own_property(window[this.name], "prototype",
|
||||
'interface "' + this.name + '" does not have own property "prototype"');
|
||||
|
||||
// "For each unique identifier of an operation defined on the
|
||||
// interface, there must be a corresponding property on the
|
||||
// interface prototype object (if it is a regular operation) or
|
||||
// the interface object (if it is a static operation), unless
|
||||
// the effective overload set for that identifier and operation
|
||||
// and with an argument count of 0 (for the ECMAScript language
|
||||
// binding) has no entries."
|
||||
//
|
||||
var prototypeOrInterfaceObject;
|
||||
if (member["static"]) {
|
||||
assert_own_property(window[this.name], member.name,
|
||||
"interface prototype object missing static operation");
|
||||
prototypeOrInterfaceObject = window[this.name];
|
||||
}
|
||||
else
|
||||
{
|
||||
assert_own_property(window[this.name].prototype, member.name,
|
||||
"interface prototype object missing non-static operation");
|
||||
prototypeOrInterfaceObject = window[this.name].prototype;
|
||||
}
|
||||
|
||||
var desc = Object.getOwnPropertyDescriptor(prototypeOrInterfaceObject, member.name);
|
||||
// "The property has attributes { [[Writable]]: true,
|
||||
// [[Enumerable]]: true, [[Configurable]]: true }."
|
||||
assert_false("get" in desc, "property has getter");
|
||||
assert_false("set" in desc, "property has setter");
|
||||
assert_true(desc.writable, "property is not writable");
|
||||
assert_true(desc.enumerable, "property is not enumerable");
|
||||
assert_true(desc.configurable, "property is not configurable");
|
||||
// "The value of the property is a Function object whose
|
||||
// behavior is as follows . . ."
|
||||
assert_equals(typeof prototypeOrInterfaceObject[member.name], "function",
|
||||
"property must be a function");
|
||||
// "The value of the Function object’s “length” property is
|
||||
// a Number determined as follows:
|
||||
// ". . .
|
||||
// "Return the length of the shortest argument list of the
|
||||
// entries in S."
|
||||
//
|
||||
// TODO: Doesn't handle overloading or variadic arguments.
|
||||
assert_equals(prototypeOrInterfaceObject[member.name].length,
|
||||
member.arguments.filter(function(arg) {
|
||||
return !arg.optional;
|
||||
}).length,
|
||||
"property has wrong .length");
|
||||
|
||||
// Make some suitable arguments
|
||||
var args = member.arguments.map(function(arg) {
|
||||
return create_suitable_object(arg.idlType);
|
||||
});
|
||||
|
||||
// "Let O be a value determined as follows:
|
||||
// ". . .
|
||||
// "Otherwise, throw a TypeError."
|
||||
// This should be hit if the operation is not static, there is
|
||||
// no [ImplicitThis] attribute, and the this value is null.
|
||||
//
|
||||
// TODO: We currently ignore the [ImplicitThis] case.
|
||||
if (!member["static"]) {
|
||||
assert_throws(new TypeError(), function() {
|
||||
window[this.name].prototype[member.name].apply(null, args);
|
||||
}, "calling operation with this = null didn't throw TypeError");
|
||||
}
|
||||
|
||||
// ". . . If O is not null and is also not a platform object
|
||||
// that implements interface I, throw a TypeError."
|
||||
//
|
||||
// TODO: Test a platform object that implements some other
|
||||
// interface. (Have to be sure to get inheritance right.)
|
||||
assert_throws(new TypeError(), function() {
|
||||
window[this.name].prototype[member.name].apply({}, args);
|
||||
}, "calling operation with this = {} didn't throw TypeError");
|
||||
}.bind(this), this.name + " interface: operation " + member.name +
|
||||
"(" + member.arguments.map(function(m) { return m.idlType.idlType; }) +
|
||||
")");
|
||||
};
|
||||
|
||||
//@}
|
||||
IdlInterface.prototype.test_member_stringifier = function(member)
|
||||
//@{
|
||||
{
|
||||
test(function()
|
||||
{
|
||||
assert_own_property(window, this.name,
|
||||
"window does not have own property " + format_value(this.name));
|
||||
|
||||
if (this.has_extended_attribute("Callback")) {
|
||||
assert_false("prototype" in window[this.name],
|
||||
this.name + ' should not have a "prototype" property');
|
||||
return;
|
||||
}
|
||||
|
||||
assert_own_property(window[this.name], "prototype",
|
||||
'interface "' + this.name + '" does not have own property "prototype"');
|
||||
|
||||
// ". . . the property exists on the interface prototype object."
|
||||
var interfacePrototypeObject = window[this.name].prototype;
|
||||
assert_own_property(window[this.name].prototype, "toString",
|
||||
"interface prototype object missing non-static operation");
|
||||
|
||||
var desc = Object.getOwnPropertyDescriptor(interfacePrototypeObject, "toString");
|
||||
// "The property has attributes { [[Writable]]: B,
|
||||
// [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the
|
||||
// stringifier is unforgeable on the interface, and true otherwise."
|
||||
assert_false("get" in desc, "property has getter");
|
||||
assert_false("set" in desc, "property has setter");
|
||||
assert_true(desc.writable, "property is not writable");
|
||||
assert_true(desc.enumerable, "property is not enumerable");
|
||||
assert_true(desc.configurable, "property is not configurable");
|
||||
// "The value of the property is a Function object, which behaves as
|
||||
// follows . . ."
|
||||
assert_equals(typeof interfacePrototypeObject.toString, "function",
|
||||
"property must be a function");
|
||||
// "The value of the Function object’s “length” property is the Number
|
||||
// value 0."
|
||||
assert_equals(interfacePrototypeObject.toString.length, 0,
|
||||
"property has wrong .length");
|
||||
|
||||
// "Let O be the result of calling ToObject on the this value."
|
||||
assert_throws(new TypeError(), function() {
|
||||
window[this.name].prototype.toString.apply(null, []);
|
||||
}, "calling stringifier with this = null didn't throw TypeError");
|
||||
|
||||
// "If O is not an object that implements the interface on which the
|
||||
// stringifier was declared, then throw a TypeError."
|
||||
//
|
||||
// TODO: Test a platform object that implements some other
|
||||
// interface. (Have to be sure to get inheritance right.)
|
||||
assert_throws(new TypeError(), function() {
|
||||
window[this.name].prototype.toString.apply({}, []);
|
||||
}, "calling stringifier with this = {} didn't throw TypeError");
|
||||
}.bind(this), this.name + " interface: stringifier");
|
||||
};
|
||||
|
||||
//@}
|
||||
IdlInterface.prototype.test_members = function()
|
||||
//@{
|
||||
|
@ -1319,195 +1441,37 @@ IdlInterface.prototype.test_members = function()
|
|||
for (var i = 0; i < this.members.length; i++)
|
||||
{
|
||||
var member = this.members[i];
|
||||
if (member.untested)
|
||||
{
|
||||
if (member.untested) {
|
||||
continue;
|
||||
}
|
||||
if (member.type == "const")
|
||||
{
|
||||
test(function()
|
||||
{
|
||||
assert_own_property(window, this.name,
|
||||
"window does not have own property " + format_value(this.name));
|
||||
|
||||
// "For each constant defined on an interface A, there must be
|
||||
// a corresponding property on the interface object, if it
|
||||
// exists."
|
||||
assert_own_property(window[this.name], member.name);
|
||||
// "The value of the property is that which is obtained by
|
||||
// converting the constant’s IDL value to an ECMAScript
|
||||
// value."
|
||||
assert_equals(window[this.name][member.name], constValue(member.value),
|
||||
"property has wrong value");
|
||||
// "The property has attributes { [[Writable]]: false,
|
||||
// [[Enumerable]]: true, [[Configurable]]: false }."
|
||||
var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name);
|
||||
assert_false("get" in desc, "property has getter");
|
||||
assert_false("set" in desc, "property has setter");
|
||||
assert_false(desc.writable, "property is writable");
|
||||
assert_true(desc.enumerable, "property is not enumerable");
|
||||
assert_false(desc.configurable, "property is configurable");
|
||||
}.bind(this), this.name + " interface: constant " + member.name + " on interface object");
|
||||
// "In addition, a property with the same characteristics must
|
||||
// exist on the interface prototype object."
|
||||
test(function()
|
||||
{
|
||||
assert_own_property(window, this.name,
|
||||
"window does not have own property " + format_value(this.name));
|
||||
switch (member.type) {
|
||||
case "const":
|
||||
this.test_member_const(member);
|
||||
break;
|
||||
|
||||
if (this.has_extended_attribute("Callback")) {
|
||||
assert_false("prototype" in window[this.name],
|
||||
this.name + ' should not have a "prototype" property');
|
||||
return;
|
||||
}
|
||||
|
||||
assert_own_property(window[this.name], "prototype",
|
||||
'interface "' + this.name + '" does not have own property "prototype"');
|
||||
|
||||
assert_own_property(window[this.name].prototype, member.name);
|
||||
assert_equals(window[this.name].prototype[member.name], constValue(member.value),
|
||||
"property has wrong value");
|
||||
var desc = Object.getOwnPropertyDescriptor(window[this.name], member.name);
|
||||
assert_false("get" in desc, "property has getter");
|
||||
assert_false("set" in desc, "property has setter");
|
||||
assert_false(desc.writable, "property is writable");
|
||||
assert_true(desc.enumerable, "property is not enumerable");
|
||||
assert_false(desc.configurable, "property is configurable");
|
||||
}.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object");
|
||||
}
|
||||
else if (member.type == "attribute")
|
||||
{
|
||||
if (member.has_extended_attribute("Unforgeable"))
|
||||
{
|
||||
// We do the checks in test_interface_of instead
|
||||
continue;
|
||||
case "attribute":
|
||||
// For unforgeable attributes, we do the checks in
|
||||
// test_interface_of instead.
|
||||
if (!member.has_extended_attribute("Unforgeable")) {
|
||||
this.test_member_attribute(member);
|
||||
}
|
||||
test(function()
|
||||
{
|
||||
assert_own_property(window, this.name,
|
||||
"window does not have own property " + format_value(this.name));
|
||||
assert_own_property(window[this.name], "prototype",
|
||||
'interface "' + this.name + '" does not have own property "prototype"');
|
||||
break;
|
||||
|
||||
if (member["static"]) {
|
||||
assert_own_property(window[this.name], member.name,
|
||||
"The interface object must have a property " +
|
||||
format_value(member.name));
|
||||
}
|
||||
else
|
||||
{
|
||||
assert_true(member.name in window[this.name].prototype,
|
||||
"The prototype object must have a property " +
|
||||
format_value(member.name));
|
||||
|
||||
// TODO: Needs to test for LenientThis.
|
||||
assert_throws(new TypeError(), function() {
|
||||
window[this.name].prototype[member.name];
|
||||
}.bind(this), "getting property on prototype object must throw TypeError");
|
||||
do_interface_attribute_asserts(window[this.name].prototype, member);
|
||||
}
|
||||
}.bind(this), this.name + " interface: attribute " + member.name);
|
||||
}
|
||||
else if (member.type == "operation")
|
||||
{
|
||||
case "operation":
|
||||
// TODO: Need to correctly handle multiple operations with the same
|
||||
// identifier.
|
||||
if (!member.name)
|
||||
{
|
||||
// Unnamed getter or such
|
||||
continue;
|
||||
if (member.name) {
|
||||
this.test_member_operation(member);
|
||||
} else if (member.stringifier) {
|
||||
this.test_member_stringifier(member);
|
||||
}
|
||||
test(function()
|
||||
{
|
||||
assert_own_property(window, this.name,
|
||||
"window does not have own property " + format_value(this.name));
|
||||
break;
|
||||
|
||||
if (this.has_extended_attribute("Callback")) {
|
||||
assert_false("prototype" in window[this.name],
|
||||
this.name + ' should not have a "prototype" property');
|
||||
return;
|
||||
}
|
||||
|
||||
assert_own_property(window[this.name], "prototype",
|
||||
'interface "' + this.name + '" does not have own property "prototype"');
|
||||
|
||||
// "For each unique identifier of an operation defined on the
|
||||
// interface, there must be a corresponding property on the
|
||||
// interface prototype object (if it is a regular operation) or
|
||||
// the interface object (if it is a static operation), unless
|
||||
// the effective overload set for that identifier and operation
|
||||
// and with an argument count of 0 (for the ECMAScript language
|
||||
// binding) has no entries."
|
||||
//
|
||||
var prototypeOrInterfaceObject;
|
||||
if (member["static"]) {
|
||||
assert_own_property(window[this.name], member.name,
|
||||
"interface prototype object missing static operation");
|
||||
prototypeOrInterfaceObject = window[this.name];
|
||||
}
|
||||
else
|
||||
{
|
||||
assert_own_property(window[this.name].prototype, member.name,
|
||||
"interface prototype object missing non-static operation");
|
||||
prototypeOrInterfaceObject = window[this.name].prototype;
|
||||
}
|
||||
|
||||
var desc = Object.getOwnPropertyDescriptor(prototypeOrInterfaceObject, member.name);
|
||||
// "The property has attributes { [[Writable]]: true,
|
||||
// [[Enumerable]]: true, [[Configurable]]: true }."
|
||||
assert_false("get" in desc, "property has getter");
|
||||
assert_false("set" in desc, "property has setter");
|
||||
assert_true(desc.writable, "property is not writable");
|
||||
assert_true(desc.enumerable, "property is not enumerable");
|
||||
assert_true(desc.configurable, "property is not configurable");
|
||||
// "The value of the property is a Function object whose
|
||||
// behavior is as follows . . ."
|
||||
assert_equals(typeof prototypeOrInterfaceObject[member.name], "function",
|
||||
"property must be a function");
|
||||
// "The value of the Function object’s “length” property is
|
||||
// a Number determined as follows:
|
||||
// ". . .
|
||||
// "Return the length of the shortest argument list of the
|
||||
// entries in S."
|
||||
//
|
||||
// TODO: Doesn't handle overloading or variadic arguments.
|
||||
assert_equals(prototypeOrInterfaceObject[member.name].length,
|
||||
member.arguments.filter(function(arg) {
|
||||
return !arg.optional;
|
||||
}).length,
|
||||
"property has wrong .length");
|
||||
|
||||
// Make some suitable arguments
|
||||
var args = member.arguments.map(function(arg) {
|
||||
return create_suitable_object(arg.idlType);
|
||||
});
|
||||
|
||||
// "Let O be a value determined as follows:
|
||||
// ". . .
|
||||
// "Otherwise, throw a TypeError."
|
||||
// This should be hit if the operation is not static, there is
|
||||
// no [ImplicitThis] attribute, and the this value is null.
|
||||
//
|
||||
// TODO: We currently ignore the [ImplicitThis] case.
|
||||
if (!member["static"]) {
|
||||
assert_throws(new TypeError(), function() {
|
||||
window[this.name].prototype[member.name].apply(null, args);
|
||||
}, "calling operation with this = null didn't throw TypeError");
|
||||
}
|
||||
|
||||
// ". . . If O is not null and is also not a platform object
|
||||
// that implements interface I, throw a TypeError."
|
||||
//
|
||||
// TODO: Test a platform object that implements some other
|
||||
// interface. (Have to be sure to get inheritance right.)
|
||||
assert_throws(new TypeError(), function() {
|
||||
window[this.name].prototype[member.name].apply({}, args);
|
||||
}, "calling operation with this = {} didn't throw TypeError");
|
||||
}.bind(this), this.name + " interface: operation " + member.name +
|
||||
"(" + member.arguments.map(function(m) { return m.idlType.idlType; }) +
|
||||
")");
|
||||
default:
|
||||
// TODO: check more member types.
|
||||
break;
|
||||
}
|
||||
// TODO: check more member types, like stringifier
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1748,11 +1712,14 @@ function do_interface_attribute_asserts(obj, member)
|
|||
// value 0."
|
||||
assert_equals(typeof desc.get, "function", "getter must be Function");
|
||||
assert_equals(desc.get.length, 0, "getter length must be 0");
|
||||
// TODO: Account for LenientThis
|
||||
assert_throws(new TypeError(), function()
|
||||
{
|
||||
desc.get.call({});
|
||||
}.bind(this), "calling getter on wrong object type must throw TypeError");
|
||||
if (!member.has_extended_attribute("LenientThis")) {
|
||||
assert_throws(new TypeError(), function() {
|
||||
desc.get.call({});
|
||||
}.bind(this), "calling getter on wrong object type must throw TypeError");
|
||||
} else {
|
||||
assert_equals(desc.get.call({}), undefined,
|
||||
"calling getter on wrong object type must return undefined");
|
||||
}
|
||||
|
||||
// TODO: Test calling setter on the interface prototype (should throw
|
||||
// TypeError in most cases).
|
||||
|
@ -1774,6 +1741,14 @@ function do_interface_attribute_asserts(obj, member)
|
|||
{
|
||||
assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes");
|
||||
assert_equals(desc.set.length, 1, "setter length must be 1");
|
||||
if (!member.has_extended_attribute("LenientThis")) {
|
||||
assert_throws(new TypeError(), function() {
|
||||
desc.set.call({});
|
||||
}.bind(this), "calling setter on wrong object type must throw TypeError");
|
||||
} else {
|
||||
assert_equals(desc.set.call({}), undefined,
|
||||
"calling setter on wrong object type must return undefined");
|
||||
}
|
||||
}
|
||||
}
|
||||
//@}
|
||||
|
@ -1826,6 +1801,8 @@ function create_suitable_object(type)
|
|||
return 7;
|
||||
|
||||
case "DOMString":
|
||||
case "ByteString":
|
||||
case "USVString":
|
||||
return "foo";
|
||||
|
||||
case "object":
|
||||
|
|
|
@ -90,3 +90,18 @@ table#results span.actual {
|
|||
white-space:pre;
|
||||
}
|
||||
|
||||
span.ok {
|
||||
color:green;
|
||||
}
|
||||
|
||||
tr.error {
|
||||
color:red;
|
||||
}
|
||||
|
||||
span.timeout {
|
||||
color:red;
|
||||
}
|
||||
|
||||
span.ok, span.timeout, span.error {
|
||||
font-variant:small-caps;
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1689,11 +1689,7 @@ MediaDecoder::IsRawEnabled()
|
|||
bool
|
||||
MediaDecoder::IsOpusEnabled()
|
||||
{
|
||||
#ifdef MOZ_OPUS
|
||||
return Preferences::GetBool("media.opus.enabled");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -12,11 +12,8 @@
|
|||
|
||||
#include"GeckoProfiler.h"
|
||||
#include "OggWriter.h"
|
||||
#ifdef MOZ_OPUS
|
||||
#include "OpusTrackEncoder.h"
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_VORBIS
|
||||
#include "VorbisTrackEncoder.h"
|
||||
#endif
|
||||
|
|
|
@ -11,12 +11,14 @@ EXPORTS += [
|
|||
'ContainerWriter.h',
|
||||
'EncodedFrameContainer.h',
|
||||
'MediaEncoder.h',
|
||||
'OpusTrackEncoder.h',
|
||||
'TrackEncoder.h',
|
||||
'TrackMetadataBase.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'MediaEncoder.cpp',
|
||||
'OpusTrackEncoder.cpp',
|
||||
'TrackEncoder.cpp',
|
||||
]
|
||||
|
||||
|
@ -24,10 +26,6 @@ if CONFIG['MOZ_OMX_ENCODER']:
|
|||
EXPORTS += ['OmxTrackEncoder.h']
|
||||
UNIFIED_SOURCES += ['OmxTrackEncoder.cpp']
|
||||
|
||||
if CONFIG['MOZ_OPUS']:
|
||||
EXPORTS += ['OpusTrackEncoder.h']
|
||||
UNIFIED_SOURCES += ['OpusTrackEncoder.cpp']
|
||||
|
||||
if CONFIG['MOZ_WEBM_ENCODER']:
|
||||
EXPORTS += ['VorbisTrackEncoder.h',
|
||||
'VP8TrackEncoder.h',
|
||||
|
|
|
@ -49,10 +49,8 @@ OggCodecState::Create(ogg_page* aPage)
|
|||
codecState = new TheoraState(aPage);
|
||||
} else if (aPage->body_len > 6 && memcmp(aPage->body+1, "vorbis", 6) == 0) {
|
||||
codecState = new VorbisState(aPage);
|
||||
#ifdef MOZ_OPUS
|
||||
} else if (aPage->body_len > 8 && memcmp(aPage->body, "OpusHead", 8) == 0) {
|
||||
codecState = new OpusState(aPage);
|
||||
#endif
|
||||
} else if (aPage->body_len > 8 && memcmp(aPage->body, "fishead\0", 8) == 0) {
|
||||
codecState = new SkeletonState(aPage);
|
||||
} else {
|
||||
|
@ -795,7 +793,6 @@ nsresult VorbisState::ReconstructVorbisGranulepos()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_OPUS
|
||||
OpusState::OpusState(ogg_page* aBosPage) :
|
||||
OggCodecState(aBosPage, true),
|
||||
mParser(nullptr),
|
||||
|
@ -1078,7 +1075,6 @@ bool OpusState::ReconstructOpusGranulepos(void)
|
|||
mPrevPageGranulepos = last->granulepos;
|
||||
return true;
|
||||
}
|
||||
#endif /* MOZ_OPUS */
|
||||
|
||||
SkeletonState::SkeletonState(ogg_page* aBosPage) :
|
||||
OggCodecState(aBosPage, true),
|
||||
|
|
|
@ -13,14 +13,12 @@
|
|||
#else
|
||||
#include <vorbis/codec.h>
|
||||
#endif
|
||||
#ifdef MOZ_OPUS
|
||||
#include <opus/opus.h>
|
||||
#include "opus/opus_multistream.h"
|
||||
// For MOZ_SAMPLE_TYPE_*
|
||||
#include "mozilla/dom/HTMLMediaElement.h"
|
||||
#include "MediaDecoderStateMachine.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#endif
|
||||
#include <nsAutoRef.h>
|
||||
#include <nsDeque.h>
|
||||
#include <nsTArray.h>
|
||||
|
@ -323,7 +321,6 @@ private:
|
|||
};
|
||||
|
||||
class OpusState : public OggCodecState {
|
||||
#ifdef MOZ_OPUS
|
||||
public:
|
||||
explicit OpusState(ogg_page* aBosPage);
|
||||
virtual ~OpusState();
|
||||
|
@ -375,7 +372,6 @@ private:
|
|||
// where we may need to trim some samples from the end.
|
||||
int64_t mPrevPageGranulepos;
|
||||
|
||||
#endif /* MOZ_OPUS */
|
||||
};
|
||||
|
||||
// Constructs a 32bit version number out of two 16 bit major,minor
|
||||
|
|
|
@ -13,12 +13,10 @@
|
|||
#include "VideoUtils.h"
|
||||
#include "theora/theoradec.h"
|
||||
#include <algorithm>
|
||||
#ifdef MOZ_OPUS
|
||||
#include "opus/opus.h"
|
||||
extern "C" {
|
||||
#include "opus/opus_multistream.h"
|
||||
}
|
||||
#endif
|
||||
#include "mozilla/dom/TimeRanges.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "VorbisUtils.h"
|
||||
|
@ -62,9 +60,7 @@ static const uint32_t SEEK_FUZZ_USECS = 500000;
|
|||
|
||||
// The number of microseconds of "pre-roll" we use for Opus streams.
|
||||
// The specification recommends 80 ms.
|
||||
#ifdef MOZ_OPUS
|
||||
static const int64_t SEEK_OPUS_PREROLL = 80 * USECS_PER_MS;
|
||||
#endif /* MOZ_OPUS */
|
||||
|
||||
enum PageSyncResult {
|
||||
PAGE_SYNC_ERROR = 1,
|
||||
|
@ -136,10 +132,8 @@ OggReader::OggReader(AbstractMediaDecoder* aDecoder)
|
|||
mMonitor("OggReader"),
|
||||
mTheoraState(nullptr),
|
||||
mVorbisState(nullptr),
|
||||
#ifdef MOZ_OPUS
|
||||
mOpusState(nullptr),
|
||||
mOpusEnabled(MediaDecoder::IsOpusEnabled()),
|
||||
#endif /* MOZ_OPUS */
|
||||
mSkeletonState(nullptr),
|
||||
mVorbisSerial(0),
|
||||
mOpusSerial(0),
|
||||
|
@ -183,11 +177,9 @@ nsresult OggReader::ResetDecode(bool start)
|
|||
if (mVorbisState && NS_FAILED(mVorbisState->Reset())) {
|
||||
res = NS_ERROR_FAILURE;
|
||||
}
|
||||
#ifdef MOZ_OPUS
|
||||
if (mOpusState && NS_FAILED(mOpusState->Reset(start))) {
|
||||
res = NS_ERROR_FAILURE;
|
||||
}
|
||||
#endif /* MOZ_OPUS */
|
||||
if (mTheoraState && NS_FAILED(mTheoraState->Reset())) {
|
||||
res = NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -217,10 +209,8 @@ void OggReader::BuildSerialList(nsTArray<uint32_t>& aTracks)
|
|||
if (HasAudio()) {
|
||||
if (mVorbisState) {
|
||||
aTracks.AppendElement(mVorbisState->mSerial);
|
||||
#ifdef MOZ_OPUS
|
||||
} else if (mOpusState) {
|
||||
aTracks.AppendElement(mOpusState->mSerial);
|
||||
#endif /* MOZ_OPUS */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -359,7 +349,6 @@ void OggReader::SetupMediaTracksInfo(const nsTArray<uint32_t>& aSerials)
|
|||
mInfo.mAudio.mHasAudio = true;
|
||||
mInfo.mAudio.mRate = vorbisState->mInfo.rate;
|
||||
mInfo.mAudio.mChannels = vorbisState->mInfo.channels;
|
||||
#ifdef MOZ_OPUS
|
||||
} else if (codecState->GetType() == OggCodecState::TYPE_OPUS) {
|
||||
OpusState* opusState = static_cast<OpusState*>(codecState);
|
||||
if (!(mOpusState && mOpusState->mSerial == opusState->mSerial)) {
|
||||
|
@ -373,7 +362,6 @@ void OggReader::SetupMediaTracksInfo(const nsTArray<uint32_t>& aSerials)
|
|||
mInfo.mAudio.mHasAudio = true;
|
||||
mInfo.mAudio.mRate = opusState->mRate;
|
||||
mInfo.mAudio.mChannels = opusState->mChannels;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -450,7 +438,6 @@ nsresult OggReader::ReadMetadata(MediaInfo* aInfo,
|
|||
} else {
|
||||
s->Deactivate();
|
||||
}
|
||||
#ifdef MOZ_OPUS
|
||||
} else if (s->GetType() == OggCodecState::TYPE_OPUS && ReadHeaders(s)) {
|
||||
if (mOpusEnabled) {
|
||||
if (!mOpusState) {
|
||||
|
@ -464,7 +451,6 @@ nsresult OggReader::ReadMetadata(MediaInfo* aInfo,
|
|||
NS_WARNING("Opus decoding disabled."
|
||||
" See media.opus.enabled in about:config");
|
||||
}
|
||||
#endif // MOZ_OPUS
|
||||
} else if (s->GetType() == OggCodecState::TYPE_SKELETON && !mSkeletonState) {
|
||||
mSkeletonState = static_cast<SkeletonState*>(s);
|
||||
} else {
|
||||
|
@ -570,7 +556,7 @@ nsresult OggReader::DecodeVorbis(ogg_packet* aPacket) {
|
|||
}
|
||||
return NS_OK;
|
||||
}
|
||||
#ifdef MOZ_OPUS
|
||||
|
||||
nsresult OggReader::DecodeOpus(ogg_packet* aPacket) {
|
||||
NS_ASSERTION(aPacket->granulepos != -1, "Must know opus granulepos!");
|
||||
|
||||
|
@ -682,16 +668,12 @@ nsresult OggReader::DecodeOpus(ogg_packet* aPacket) {
|
|||
|
||||
return NS_OK;
|
||||
}
|
||||
#endif /* MOZ_OPUS */
|
||||
|
||||
bool OggReader::DecodeAudioData()
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
DebugOnly<bool> haveCodecState = mVorbisState != nullptr
|
||||
#ifdef MOZ_OPUS
|
||||
|| mOpusState != nullptr
|
||||
#endif /* MOZ_OPUS */
|
||||
;
|
||||
DebugOnly<bool> haveCodecState = mVorbisState != nullptr ||
|
||||
mOpusState != nullptr;
|
||||
NS_ASSERTION(haveCodecState, "Need audio codec state to decode audio");
|
||||
|
||||
// Read the next data packet. Skip any non-data packets we encounter.
|
||||
|
@ -699,10 +681,8 @@ bool OggReader::DecodeAudioData()
|
|||
OggCodecState* codecState;
|
||||
if (mVorbisState)
|
||||
codecState = static_cast<OggCodecState*>(mVorbisState);
|
||||
#ifdef MOZ_OPUS
|
||||
else
|
||||
codecState = static_cast<OggCodecState*>(mOpusState);
|
||||
#endif /* MOZ_OPUS */
|
||||
do {
|
||||
if (packet) {
|
||||
OggCodecState::ReleasePacket(packet);
|
||||
|
@ -719,10 +699,8 @@ bool OggReader::DecodeAudioData()
|
|||
nsAutoRef<ogg_packet> autoRelease(packet);
|
||||
if (mVorbisState) {
|
||||
DecodeVorbis(packet);
|
||||
#ifdef MOZ_OPUS
|
||||
} else if (mOpusState) {
|
||||
DecodeOpus(packet);
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((packet->e_o_s) && (!ReadOggChain())) {
|
||||
|
@ -749,9 +727,7 @@ void OggReader::SetChained(bool aIsChained) {
|
|||
bool OggReader::ReadOggChain()
|
||||
{
|
||||
bool chained = false;
|
||||
#ifdef MOZ_OPUS
|
||||
OpusState* newOpusState = nullptr;
|
||||
#endif /* MOZ_OPUS */
|
||||
VorbisState* newVorbisState = nullptr;
|
||||
nsAutoPtr<MetadataTags> tags;
|
||||
|
||||
|
@ -778,11 +754,9 @@ bool OggReader::ReadOggChain()
|
|||
if (mVorbisState && (codecState->GetType() == OggCodecState::TYPE_VORBIS)) {
|
||||
newVorbisState = static_cast<VorbisState*>(codecState.get());
|
||||
}
|
||||
#ifdef MOZ_OPUS
|
||||
else if (mOpusState && (codecState->GetType() == OggCodecState::TYPE_OPUS)) {
|
||||
newOpusState = static_cast<OpusState*>(codecState.get());
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
@ -819,7 +793,6 @@ bool OggReader::ReadOggChain()
|
|||
tags = newVorbisState->GetTags();
|
||||
}
|
||||
|
||||
#ifdef MOZ_OPUS
|
||||
if ((newOpusState && ReadHeaders(newOpusState)) &&
|
||||
(mOpusState->mRate == newOpusState->mRate) &&
|
||||
(mOpusState->mChannels == newOpusState->mChannels)) {
|
||||
|
@ -835,7 +808,6 @@ bool OggReader::ReadOggChain()
|
|||
chained = true;
|
||||
tags = newOpusState->GetTags();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (chained) {
|
||||
SetChained(true);
|
||||
|
@ -1441,12 +1413,10 @@ nsresult OggReader::SeekInUnbuffered(int64_t aTarget,
|
|||
if (HasVideo() && mTheoraState) {
|
||||
keyframeOffsetMs = mTheoraState->MaxKeyframeOffset();
|
||||
}
|
||||
#ifdef MOZ_OPUS
|
||||
// Add in the Opus pre-roll if necessary, as well.
|
||||
if (HasAudio() && mOpusState) {
|
||||
keyframeOffsetMs = std::max(keyframeOffsetMs, SEEK_OPUS_PREROLL);
|
||||
}
|
||||
#endif /* MOZ_OPUS */
|
||||
int64_t seekTarget = std::max(aStartTime, aTarget - keyframeOffsetMs);
|
||||
// Minimize the bisection search space using the known timestamps from the
|
||||
// buffered ranges.
|
||||
|
@ -1476,11 +1446,9 @@ nsresult OggReader::SeekInternal(int64_t aTarget,
|
|||
MediaResource* resource = mDecoder->GetResource();
|
||||
NS_ENSURE_TRUE(resource != nullptr, NS_ERROR_FAILURE);
|
||||
int64_t adjustedTarget = aTarget;
|
||||
#ifdef MOZ_OPUS
|
||||
if (HasAudio() && mOpusState){
|
||||
adjustedTarget = std::max(aStartTime, aTarget - SEEK_OPUS_PREROLL);
|
||||
}
|
||||
#endif /* MOZ_OPUS */
|
||||
|
||||
if (adjustedTarget == aStartTime) {
|
||||
// We've seeked to the media start. Just seek to the offset of the first
|
||||
|
@ -1788,10 +1756,8 @@ nsresult OggReader::SeekBisection(int64_t aTarget,
|
|||
if (HasAudio() && granulepos > 0 && audioTime == -1) {
|
||||
if (mVorbisState && serial == mVorbisState->mSerial) {
|
||||
audioTime = mVorbisState->Time(granulepos);
|
||||
#ifdef MOZ_OPUS
|
||||
} else if (mOpusState && serial == mOpusState->mSerial) {
|
||||
audioTime = mOpusState->Time(granulepos);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1966,12 +1932,10 @@ nsresult OggReader::GetBuffered(dom::TimeRanges* aBuffered)
|
|||
startTime = VorbisState::Time(&mVorbisInfo, granulepos);
|
||||
NS_ASSERTION(startTime > 0, "Must have positive start time");
|
||||
}
|
||||
#ifdef MOZ_OPUS
|
||||
else if (mOpusState && serial == mOpusSerial) {
|
||||
startTime = OpusState::Time(mOpusPreSkip, granulepos);
|
||||
NS_ASSERTION(startTime > 0, "Must have positive start time");
|
||||
}
|
||||
#endif /* MOZ_OPUS */
|
||||
else if (mTheoraState && serial == mTheoraSerial) {
|
||||
startTime = TheoraState::Time(&mTheoraInfo, granulepos);
|
||||
NS_ASSERTION(startTime > 0, "Must have positive start time");
|
||||
|
|
|
@ -65,11 +65,8 @@ public:
|
|||
int64_t aTimeThreshold);
|
||||
|
||||
virtual bool HasAudio() {
|
||||
return (mVorbisState != 0 && mVorbisState->mActive)
|
||||
#ifdef MOZ_OPUS
|
||||
|| (mOpusState != 0 && mOpusState->mActive)
|
||||
#endif /* MOZ_OPUS */
|
||||
;
|
||||
return (mVorbisState != 0 && mVorbisState->mActive) ||
|
||||
(mOpusState != 0 && mOpusState->mActive);
|
||||
}
|
||||
|
||||
virtual bool HasVideo() {
|
||||
|
@ -285,7 +282,6 @@ private:
|
|||
// Decode state of the Vorbis bitstream we're decoding, if we have audio.
|
||||
VorbisState* mVorbisState;
|
||||
|
||||
#ifdef MOZ_OPUS
|
||||
// Decode state of the Opus bitstream we're decoding, if we have one.
|
||||
OpusState *mOpusState;
|
||||
|
||||
|
@ -293,7 +289,6 @@ private:
|
|||
// contructor was called. We can't check it dynamically because
|
||||
// we're not on the main thread;
|
||||
bool mOpusEnabled;
|
||||
#endif /* MOZ_OPUS */
|
||||
|
||||
// Decode state of the Skeleton bitstream.
|
||||
SkeletonState* mSkeletonState;
|
||||
|
|
|
@ -169,11 +169,9 @@ WebMReader::WebMReader(AbstractMediaDecoder* aDecoder)
|
|||
: MediaDecoderReader(aDecoder)
|
||||
, mContext(nullptr)
|
||||
, mPacketCount(0)
|
||||
#ifdef MOZ_OPUS
|
||||
, mOpusDecoder(nullptr)
|
||||
, mSkip(0)
|
||||
, mSeekPreroll(0)
|
||||
#endif
|
||||
, mVideoTrack(0)
|
||||
, mAudioTrack(0)
|
||||
, mAudioStartUsec(-1)
|
||||
|
@ -184,9 +182,7 @@ WebMReader::WebMReader(AbstractMediaDecoder* aDecoder)
|
|||
, mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
|
||||
, mHasVideo(false)
|
||||
, mHasAudio(false)
|
||||
#ifdef MOZ_OPUS
|
||||
, mPaddingDiscarded(false)
|
||||
#endif
|
||||
{
|
||||
MOZ_COUNT_CTOR(WebMReader);
|
||||
#ifdef PR_LOGGING
|
||||
|
@ -304,7 +300,6 @@ nsresult WebMReader::ResetDecode()
|
|||
// aren't fatal and it fails when ResetDecode is called at a
|
||||
// time when no vorbis data has been read.
|
||||
vorbis_synthesis_restart(&mVorbisDsp);
|
||||
#ifdef MOZ_OPUS
|
||||
} else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
|
||||
if (mOpusDecoder) {
|
||||
// Reset the decoder.
|
||||
|
@ -312,7 +307,6 @@ nsresult WebMReader::ResetDecode()
|
|||
mSkip = mOpusParser->mPreSkip;
|
||||
mPaddingDiscarded = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
mVideoPackets.Reset();
|
||||
|
@ -514,7 +508,6 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
|||
|
||||
mInfo.mAudio.mRate = mVorbisDsp.vi->rate;
|
||||
mInfo.mAudio.mChannels = mVorbisDsp.vi->channels;
|
||||
#ifdef MOZ_OPUS
|
||||
} else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
|
||||
unsigned char* data = 0;
|
||||
size_t length = 0;
|
||||
|
@ -547,7 +540,6 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
|||
|
||||
mInfo.mAudio.mChannels = mOpusParser->mChannels;
|
||||
mSeekPreroll = params.seek_preroll;
|
||||
#endif
|
||||
} else {
|
||||
Cleanup();
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -568,7 +560,6 @@ WebMReader::IsMediaSeekable()
|
|||
return mContext && nestegg_has_cues(mContext);
|
||||
}
|
||||
|
||||
#ifdef MOZ_OPUS
|
||||
bool WebMReader::InitOpusDecoder()
|
||||
{
|
||||
int r;
|
||||
|
@ -586,7 +577,6 @@ bool WebMReader::InitOpusDecoder()
|
|||
|
||||
return r == OPUS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
|
||||
{
|
||||
|
@ -653,11 +643,9 @@ bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
|
|||
return false;
|
||||
}
|
||||
} else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
|
||||
#ifdef MOZ_OPUS
|
||||
if (!DecodeOpus(data, length, aOffset, tstamp_usecs, aPacket)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -739,7 +727,6 @@ bool WebMReader::DecodeVorbis(const unsigned char* aData, size_t aLength,
|
|||
return true;
|
||||
}
|
||||
|
||||
#ifdef MOZ_OPUS
|
||||
bool WebMReader::DecodeOpus(const unsigned char* aData, size_t aLength,
|
||||
int64_t aOffset, uint64_t aTstampUsecs,
|
||||
nestegg_packet* aPacket)
|
||||
|
@ -876,7 +863,6 @@ bool WebMReader::DecodeOpus(const unsigned char* aData, size_t aLength,
|
|||
|
||||
return true;
|
||||
}
|
||||
#endif /* MOZ_OPUS */
|
||||
|
||||
nsReturnRef<NesteggPacketHolder> WebMReader::NextPacket(TrackType aTrackType)
|
||||
{
|
||||
|
|
|
@ -24,9 +24,7 @@
|
|||
#include "vorbis/codec.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_OPUS
|
||||
#include "OpusParser.h"
|
||||
#endif
|
||||
|
||||
// Holds a nestegg_packet, and its file offset. This is needed so we
|
||||
// know the offset in the file we've played up to, in order to calculate
|
||||
|
@ -190,10 +188,8 @@ public:
|
|||
MediaTaskQueue* GetTaskQueue() { return mTaskQueue; }
|
||||
|
||||
protected:
|
||||
#ifdef MOZ_OPUS
|
||||
// Setup opus decoder
|
||||
bool InitOpusDecoder();
|
||||
#endif
|
||||
|
||||
// Decode a nestegg packet of audio data. Push the audio data on the
|
||||
// audio queue. Returns true when there's more audio to decode,
|
||||
|
@ -205,11 +201,9 @@ protected:
|
|||
bool DecodeVorbis(const unsigned char* aData, size_t aLength,
|
||||
int64_t aOffset, uint64_t aTstampUsecs,
|
||||
int32_t* aTotalFrames);
|
||||
#ifdef MOZ_OPUS
|
||||
bool DecodeOpus(const unsigned char* aData, size_t aLength,
|
||||
int64_t aOffset, uint64_t aTstampUsecs,
|
||||
nestegg_packet* aPacket);
|
||||
#endif
|
||||
|
||||
// Release context and set to null. Called when an error occurs during
|
||||
// reading metadata or destruction of the reader itself.
|
||||
|
@ -235,13 +229,11 @@ private:
|
|||
vorbis_block mVorbisBlock;
|
||||
int64_t mPacketCount;
|
||||
|
||||
#ifdef MOZ_OPUS
|
||||
// Opus decoder state
|
||||
nsAutoPtr<OpusParser> mOpusParser;
|
||||
OpusMSDecoder *mOpusDecoder;
|
||||
uint16_t mSkip; // Samples left to trim before playback.
|
||||
uint64_t mSeekPreroll; // Nanoseconds to discard after seeking.
|
||||
#endif
|
||||
|
||||
// Queue of video and audio packets that have been read but not decoded. These
|
||||
// must only be accessed from the state machine thread.
|
||||
|
@ -288,12 +280,10 @@ private:
|
|||
bool mHasVideo;
|
||||
bool mHasAudio;
|
||||
|
||||
#ifdef MOZ_OPUS
|
||||
// Opus padding should only be discarded on the final packet. Once this
|
||||
// is set to true, if the reader attempts to decode any further packets it
|
||||
// will raise an error so we can indicate that the file is invalid.
|
||||
bool mPaddingDiscarded;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -153,7 +153,6 @@ th_info_init
|
|||
th_packet_isheader
|
||||
th_packet_iskeyframe
|
||||
th_setup_free
|
||||
#ifdef MOZ_OPUS
|
||||
opus_decoder_create
|
||||
opus_decoder_destroy
|
||||
opus_decoder_ctl
|
||||
|
@ -172,7 +171,6 @@ opus_encoder_destroy
|
|||
opus_encoder_ctl
|
||||
opus_encode
|
||||
opus_encode_float
|
||||
#endif
|
||||
#ifndef MOZ_NATIVE_PNG
|
||||
MOZ_APNG_get_first_frame_is_hidden
|
||||
MOZ_APNG_get_next_frame_blend_op
|
||||
|
|
|
@ -3220,10 +3220,10 @@ nsTreeBodyFrame::PaintCell(int32_t aRowIndex,
|
|||
srcX = currX + remainingWidth - (srcX - cellRect.x);
|
||||
destX = currX + remainingWidth - (destX - cellRect.x);
|
||||
}
|
||||
Point p1(pc->CSSPixelsToDevPixels(srcX),
|
||||
pc->CSSPixelsToDevPixels(lineY + mRowHeight / 2));
|
||||
Point p2(pc->CSSPixelsToDevPixels(destX),
|
||||
pc->CSSPixelsToDevPixels(lineY + mRowHeight / 2));
|
||||
Point p1(pc->AppUnitsToGfxUnits(srcX),
|
||||
pc->AppUnitsToGfxUnits(lineY + mRowHeight / 2));
|
||||
Point p2(pc->AppUnitsToGfxUnits(destX),
|
||||
pc->AppUnitsToGfxUnits(lineY + mRowHeight / 2));
|
||||
SnapLineToDevicePixelsForStroking(p1, p2, *drawTarget);
|
||||
drawTarget->StrokeLine(p1, p2, colorPatt, strokeOptions);
|
||||
}
|
||||
|
@ -3234,15 +3234,15 @@ nsTreeBodyFrame::PaintCell(int32_t aRowIndex,
|
|||
// Paint full vertical line only if we have next sibling.
|
||||
bool hasNextSibling;
|
||||
mView->HasNextSibling(currentParent, aRowIndex, &hasNextSibling);
|
||||
Point p1(pc->CSSPixelsToDevPixels(srcX),
|
||||
pc->CSSPixelsToDevPixels(lineY));
|
||||
Point p1(pc->AppUnitsToGfxUnits(srcX),
|
||||
pc->AppUnitsToGfxUnits(lineY));
|
||||
Point p2;
|
||||
p2.x = pc->CSSPixelsToDevPixels(srcX);
|
||||
p2.x = pc->AppUnitsToGfxUnits(srcX);
|
||||
|
||||
if (hasNextSibling)
|
||||
p2.y = pc->CSSPixelsToDevPixels(lineY + mRowHeight);
|
||||
p2.y = pc->AppUnitsToGfxUnits(lineY + mRowHeight);
|
||||
else if (i == level)
|
||||
p2.y = pc->CSSPixelsToDevPixels(lineY + mRowHeight / 2);
|
||||
p2.y = pc->AppUnitsToGfxUnits(lineY + mRowHeight / 2);
|
||||
|
||||
SnapLineToDevicePixelsForStroking(p1, p2, *drawTarget);
|
||||
drawTarget->StrokeLine(p1, p2, colorPatt, strokeOptions);
|
||||
|
|
|
@ -276,9 +276,7 @@ pref("media.fragmented-mp4.use-blank-decoder", false);
|
|||
pref("media.raw.enabled", true);
|
||||
#endif
|
||||
pref("media.ogg.enabled", true);
|
||||
#ifdef MOZ_OPUS
|
||||
pref("media.opus.enabled", true);
|
||||
#endif
|
||||
#ifdef MOZ_WAVE
|
||||
pref("media.wave.enabled", true);
|
||||
#endif
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "prlog.h"
|
||||
#include "RootHashes.inc" // Note: Generated by genRootCAHashes.js
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
|
||||
// Note: New CAs will show up as UNKNOWN_ROOT until
|
||||
// RootHashes.inc is updated to include them. 0 is reserved by
|
||||
|
|
|
@ -2,13 +2,12 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['TabEngine', 'TabSetRecord'];
|
||||
this.EXPORTED_SYMBOLS = ["TabEngine", "TabSetRecord"];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
const TABS_TTL = 604800; // 7 days
|
||||
const TABS_TTL = 604800; // 7 days.
|
||||
const TAB_ENTRIES_LIMIT = 25; // How many URLs to include in tab history.
|
||||
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
@ -27,7 +26,7 @@ this.TabSetRecord = function TabSetRecord(collection, id) {
|
|||
TabSetRecord.prototype = {
|
||||
__proto__: CryptoWrapper.prototype,
|
||||
_logName: "Sync.Record.Tabs",
|
||||
ttl: TABS_TTL
|
||||
ttl: TABS_TTL,
|
||||
};
|
||||
|
||||
Utils.deferGetSet(TabSetRecord, "cleartext", ["clientName", "tabs"]);
|
||||
|
@ -36,7 +35,7 @@ Utils.deferGetSet(TabSetRecord, "cleartext", ["clientName", "tabs"]);
|
|||
this.TabEngine = function TabEngine(service) {
|
||||
SyncEngine.call(this, "Tabs", service);
|
||||
|
||||
// Reset the client on every startup so that we fetch recent tabs
|
||||
// Reset the client on every startup so that we fetch recent tabs.
|
||||
this._resetClient();
|
||||
}
|
||||
TabEngine.prototype = {
|
||||
|
@ -47,7 +46,7 @@ TabEngine.prototype = {
|
|||
|
||||
syncPriority: 3,
|
||||
|
||||
getChangedIDs: function getChangedIDs() {
|
||||
getChangedIDs: function () {
|
||||
// No need for a proper timestamp (no conflict resolution needed).
|
||||
let changedIDs = {};
|
||||
if (this._tracker.modified)
|
||||
|
@ -55,22 +54,22 @@ TabEngine.prototype = {
|
|||
return changedIDs;
|
||||
},
|
||||
|
||||
// API for use by Weave UI code to give user choices of tabs to open:
|
||||
getAllClients: function TabEngine_getAllClients() {
|
||||
// API for use by Sync UI code to give user choices of tabs to open.
|
||||
getAllClients: function () {
|
||||
return this._store._remoteClients;
|
||||
},
|
||||
|
||||
getClientById: function TabEngine_getClientById(id) {
|
||||
getClientById: function (id) {
|
||||
return this._store._remoteClients[id];
|
||||
},
|
||||
|
||||
_resetClient: function TabEngine__resetClient() {
|
||||
_resetClient: function () {
|
||||
SyncEngine.prototype._resetClient.call(this);
|
||||
this._store.wipe();
|
||||
this._tracker.modified = true;
|
||||
},
|
||||
|
||||
removeClientData: function removeClientData() {
|
||||
removeClientData: function () {
|
||||
let url = this.engineURL + "/" + this.service.clientsEngine.localID;
|
||||
this.service.resource(url).delete();
|
||||
},
|
||||
|
@ -94,7 +93,7 @@ function TabStore(name, engine) {
|
|||
TabStore.prototype = {
|
||||
__proto__: Store.prototype,
|
||||
|
||||
itemExists: function TabStore_itemExists(id) {
|
||||
itemExists: function (id) {
|
||||
return id == this.engine.service.clientsEngine.localID;
|
||||
},
|
||||
|
||||
|
@ -131,23 +130,40 @@ TabStore.prototype = {
|
|||
continue;
|
||||
}
|
||||
|
||||
// Until we store full or partial history, just grab the current entry.
|
||||
// index is 1 based, so make sure we adjust.
|
||||
let entry = tabState.entries[tabState.index - 1];
|
||||
let acceptable = !filter ? (url) => url :
|
||||
(url) => url && !filteredUrls.test(url);
|
||||
|
||||
// Filter out some urls if necessary. SessionStore can return empty
|
||||
// tabs in some cases - easiest thing is to just ignore them for now.
|
||||
if (!entry.url || filter && filteredUrls.test(entry.url)) {
|
||||
let entries = tabState.entries;
|
||||
let index = tabState.index;
|
||||
let current = entries[index - 1];
|
||||
|
||||
// We ignore the tab completely if the current entry url is
|
||||
// not acceptable (we need something accurate to open).
|
||||
if (!acceptable(current.url)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// I think it's also possible that attributes[.image] might not be set
|
||||
// so handle that as well.
|
||||
// The element at `index` is the current page. Previous URLs were
|
||||
// previously visited URLs; subsequent URLs are in the 'forward' stack,
|
||||
// which we can't represent in Sync, so we truncate here.
|
||||
let candidates = (entries.length == index) ?
|
||||
entries :
|
||||
entries.slice(0, index);
|
||||
|
||||
let urls = candidates.map((entry) => entry.url)
|
||||
.filter(acceptable)
|
||||
.reverse(); // Because Sync puts current at index 0, and history after.
|
||||
|
||||
// Truncate if necessary.
|
||||
if (urls.length > TAB_ENTRIES_LIMIT) {
|
||||
urls.length = TAB_ENTRIES_LIMIT;
|
||||
}
|
||||
|
||||
allTabs.push({
|
||||
title: entry.title || "",
|
||||
urlHistory: [entry.url],
|
||||
title: current.title || "",
|
||||
urlHistory: urls,
|
||||
icon: tabState.attributes && tabState.attributes.image || "",
|
||||
lastUsed: Math.floor((tabState.lastAccessed || 0) / 1000)
|
||||
lastUsed: Math.floor((tabState.lastAccessed || 0) / 1000),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -155,7 +171,7 @@ TabStore.prototype = {
|
|||
return allTabs;
|
||||
},
|
||||
|
||||
createRecord: function createRecord(id, collection) {
|
||||
createRecord: function (id, collection) {
|
||||
let record = new TabSetRecord(collection, id);
|
||||
record.clientName = this.engine.service.clientsEngine.localName;
|
||||
|
||||
|
@ -188,7 +204,7 @@ TabStore.prototype = {
|
|||
return record;
|
||||
},
|
||||
|
||||
getAllIDs: function TabStore_getAllIds() {
|
||||
getAllIDs: function () {
|
||||
// Don't report any tabs if all windows are in private browsing for
|
||||
// first syncs.
|
||||
let ids = {};
|
||||
|
@ -214,31 +230,38 @@ TabStore.prototype = {
|
|||
return ids;
|
||||
},
|
||||
|
||||
wipe: function TabStore_wipe() {
|
||||
wipe: function () {
|
||||
this._remoteClients = {};
|
||||
},
|
||||
|
||||
create: function TabStore_create(record) {
|
||||
create: function (record) {
|
||||
this._log.debug("Adding remote tabs from " + record.clientName);
|
||||
this._remoteClients[record.id] = record.cleartext;
|
||||
|
||||
// Lose some precision, but that's good enough (seconds)
|
||||
// Lose some precision, but that's good enough (seconds).
|
||||
let roundModify = Math.floor(record.modified / 1000);
|
||||
let notifyState = Svc.Prefs.get("notifyTabState");
|
||||
// If there's no existing pref, save this first modified time
|
||||
if (notifyState == null)
|
||||
|
||||
// If there's no existing pref, save this first modified time.
|
||||
if (notifyState == null) {
|
||||
Svc.Prefs.set("notifyTabState", roundModify);
|
||||
// Don't change notifyState if it's already 0 (don't notify)
|
||||
else if (notifyState == 0)
|
||||
return;
|
||||
// We must have gotten a new tab that isn't the same as last time
|
||||
else if (notifyState != roundModify)
|
||||
}
|
||||
|
||||
// Don't change notifyState if it's already 0 (don't notify).
|
||||
if (notifyState == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We must have gotten a new tab that isn't the same as last time.
|
||||
if (notifyState != roundModify) {
|
||||
Svc.Prefs.set("notifyTabState", 0);
|
||||
}
|
||||
},
|
||||
|
||||
update: function update(record) {
|
||||
update: function (record) {
|
||||
this._log.trace("Ignoring tab updates as local ones win");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
@ -247,7 +270,7 @@ function TabTracker(name, engine) {
|
|||
Svc.Obs.add("weave:engine:start-tracking", this);
|
||||
Svc.Obs.add("weave:engine:stop-tracking", this);
|
||||
|
||||
// Make sure "this" pointer is always set correctly for event listeners
|
||||
// Make sure "this" pointer is always set correctly for event listeners.
|
||||
this.onTab = Utils.bind2(this, this.onTab);
|
||||
this._unregisterListeners = Utils.bind2(this, this._unregisterListeners);
|
||||
}
|
||||
|
@ -256,16 +279,17 @@ TabTracker.prototype = {
|
|||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
||||
|
||||
loadChangedIDs: function loadChangedIDs() {
|
||||
loadChangedIDs: function () {
|
||||
// Don't read changed IDs from disk at start up.
|
||||
},
|
||||
|
||||
clearChangedIDs: function clearChangedIDs() {
|
||||
clearChangedIDs: function () {
|
||||
this.modified = false;
|
||||
},
|
||||
|
||||
_topics: ["pageshow", "TabOpen", "TabClose", "TabSelect"],
|
||||
_registerListenersForWindow: function registerListenersFW(window) {
|
||||
|
||||
_registerListenersForWindow: function (window) {
|
||||
this._log.trace("Registering tab listeners in window");
|
||||
for each (let topic in this._topics) {
|
||||
window.addEventListener(topic, this.onTab, false);
|
||||
|
@ -273,11 +297,11 @@ TabTracker.prototype = {
|
|||
window.addEventListener("unload", this._unregisterListeners, false);
|
||||
},
|
||||
|
||||
_unregisterListeners: function unregisterListeners(event) {
|
||||
_unregisterListeners: function (event) {
|
||||
this._unregisterListenersForWindow(event.target);
|
||||
},
|
||||
|
||||
_unregisterListenersForWindow: function unregisterListenersFW(window) {
|
||||
_unregisterListenersForWindow: function (window) {
|
||||
this._log.trace("Removing tab listeners in window");
|
||||
window.removeEventListener("unload", this._unregisterListeners, false);
|
||||
for each (let topic in this._topics) {
|
||||
|
@ -318,7 +342,7 @@ TabTracker.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
onTab: function onTab(event) {
|
||||
onTab: function (event) {
|
||||
if (event.originalTarget.linkedBrowser) {
|
||||
let browser = event.originalTarget.linkedBrowser;
|
||||
if (PrivateBrowsingUtils.isBrowserPrivate(browser) &&
|
||||
|
@ -334,7 +358,8 @@ TabTracker.prototype = {
|
|||
// For page shows, bump the score 10% of the time, emulating a partial
|
||||
// score. We don't want to sync too frequently. For all other page
|
||||
// events, always bump the score.
|
||||
if (event.type != "pageshow" || Math.random() < .1)
|
||||
if (event.type != "pageshow" || Math.random() < .1) {
|
||||
this.score += SCORE_INCREMENT_SMALL;
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
|
|
@ -138,8 +138,16 @@ function mockGetTabState (tab) {
|
|||
return tab;
|
||||
}
|
||||
|
||||
function mockGetWindowEnumerator(url, numWindows, numTabs) {
|
||||
function mockGetWindowEnumerator(url, numWindows, numTabs, indexes, moreURLs) {
|
||||
let elements = [];
|
||||
|
||||
function url2entry(url) {
|
||||
return {
|
||||
url: ((typeof url == "function") ? url() : url),
|
||||
title: "title"
|
||||
};
|
||||
}
|
||||
|
||||
for (let w = 0; w < numWindows; ++w) {
|
||||
let tabs = [];
|
||||
let win = {
|
||||
|
@ -153,11 +161,8 @@ function mockGetWindowEnumerator(url, numWindows, numTabs) {
|
|||
|
||||
for (let t = 0; t < numTabs; ++t) {
|
||||
tabs.push(TestingUtils.deepCopy({
|
||||
index: 1,
|
||||
entries: [{
|
||||
url: ((typeof url == "string") ? url : url()),
|
||||
title: "title"
|
||||
}],
|
||||
index: indexes ? indexes() : 1,
|
||||
entries: (moreURLs ? [url].concat(moreURLs()) : [url]).map(url2entry),
|
||||
attributes: {
|
||||
image: "image"
|
||||
},
|
||||
|
|
|
@ -52,23 +52,43 @@ function test_getAllTabs() {
|
|||
let store = getMockStore();
|
||||
let tabs;
|
||||
|
||||
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "http://foo.com", 1, 1);
|
||||
let threeUrls = ["http://foo.com", "http://fuubar.com", "http://barbar.com"];
|
||||
|
||||
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "http://bar.com", 1, 1, () => 2, () => threeUrls);
|
||||
|
||||
_("Get all tabs.");
|
||||
tabs = store.getAllTabs();
|
||||
_("Tabs: " + JSON.stringify(tabs));
|
||||
do_check_eq(tabs.length, 1);
|
||||
do_check_eq(tabs[0].title, "title");
|
||||
do_check_eq(tabs[0].urlHistory.length, 1);
|
||||
do_check_eq(tabs[0].urlHistory[0], ["http://foo.com"]);
|
||||
do_check_eq(tabs[0].urlHistory.length, 2);
|
||||
do_check_eq(tabs[0].urlHistory[0], "http://foo.com");
|
||||
do_check_eq(tabs[0].urlHistory[1], "http://bar.com");
|
||||
do_check_eq(tabs[0].icon, "image");
|
||||
do_check_eq(tabs[0].lastUsed, 1);
|
||||
|
||||
_("Get all tabs, and check that filtering works.");
|
||||
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "about:foo", 1, 1);
|
||||
let twoUrls = ["about:foo", "http://fuubar.com"];
|
||||
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "http://foo.com", 1, 1, () => 2, () => twoUrls);
|
||||
tabs = store.getAllTabs(true);
|
||||
_("Filtered: " + JSON.stringify(tabs));
|
||||
do_check_eq(tabs.length, 0);
|
||||
|
||||
_("Get all tabs, and check that the entries safety limit works.");
|
||||
let allURLs = [];
|
||||
for (let i = 0; i < 50; i++) {
|
||||
allURLs.push("http://foo" + i + ".bar");
|
||||
}
|
||||
allURLs.splice(35, 0, "about:foo", "about:bar", "about:foobar");
|
||||
|
||||
store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "http://bar.com", 1, 1, () => 45, () => allURLs);
|
||||
tabs = store.getAllTabs((url) => url.startsWith("about"));
|
||||
|
||||
_("Sliced: " + JSON.stringify(tabs));
|
||||
do_check_eq(tabs.length, 1);
|
||||
do_check_eq(tabs[0].urlHistory.length, 25);
|
||||
do_check_eq(tabs[0].urlHistory[0], "http://foo40.bar");
|
||||
do_check_eq(tabs[0].urlHistory[24], "http://foo16.bar");
|
||||
}
|
||||
|
||||
function test_createRecord() {
|
||||
|
|
|
@ -458,9 +458,7 @@ static nsDefaultMimeTypeEntry defaultMimeEntries [] =
|
|||
{ VIDEO_OGG, "ogg" },
|
||||
{ APPLICATION_OGG, "ogg" },
|
||||
{ AUDIO_OGG, "oga" },
|
||||
#ifdef MOZ_OPUS
|
||||
{ AUDIO_OGG, "opus" },
|
||||
#endif
|
||||
#ifdef MOZ_WEBM
|
||||
{ VIDEO_WEBM, "webm" },
|
||||
{ AUDIO_WEBM, "webm" },
|
||||
|
|
Загрузка…
Ссылка в новой задаче