Merge m-c to b2g-inbound a=merge

This commit is contained in:
Wes Kocher 2014-09-04 18:52:01 -07:00
commit 2cc342f7d3
13480 changed files with 603479 additions and 62871 deletions

3
.gitignore vendored
View File

@ -61,3 +61,6 @@ GTAGS
GRTAGS
GSYMS
GPATH
# Git clone directory for updating web-platform-tests
testing/web-platform/sync/

View File

@ -75,3 +75,6 @@ GPATH
# Unit tests for Loop
^browser/components/loop/standalone/content/config\.js$
^browser/components/loop/standalone/node_modules/
# Git clone directory for updating web-platform-tests
^testing/web-platform/sync/

View File

@ -315,27 +315,4 @@ config/export:
endif
# Interdependencies for parallel export.
js/xpconnect/src/export: dom/bindings/export xpcom/xpidl/export
accessible/xpcom/export: xpcom/xpidl/export
ifdef ENABLE_CLANG_PLUGIN
js/src/export config/host: build/clang-plugin/export
endif
# Interdependencies that moz.build world don't know about yet for compilation.
# Note some others are hardcoded or "guessed" in recursivemake.py and emitter.py
ifeq ($(MOZ_WIDGET_TOOLKIT),gtk3)
toolkit/library/target: widget/gtk/mozgtk/gtk3/target
endif
ifdef MOZ_LDAP_XPCOM
ldap/target: config/external/nss/target mozglue/build/target
toolkit/library/target: ldap/target
endif
ifndef MOZ_FOLD_LIBS
ifndef MOZ_NATIVE_SQLITE
config/external/nss/target: db/sqlite3/src/target
endif
endif
ifeq ($(MOZ_REPLACE_MALLOC_LINKAGE),dummy library)
mozglue/build/target: memory/replace/dummy/target
endif
# There used to be build interdependencies here. They are now in config/recurse.mk

View File

@ -677,6 +677,13 @@ nsAccessiblePivot::AdjustStartPosition(Accessible* aAccessible,
}
}
if (aAccessible == mPosition && mStartOffset != -1 && mEndOffset != -1) {
HyperTextAccessible* text = aAccessible->AsHyperText();
if (text) {
matched = text->GetChildAtOffset(mStartOffset);
}
}
return matched;
}

View File

@ -112,16 +112,6 @@
toIndex: 5
}]
}],
// XXX: Bug 980510: doing next after text traversal should
// bring us to the next paragraph.
[ContentMessages.simpleMoveNext, {
speak: 'You\'re a good guy, mon frere. ' +
'That means brother in French. ' +
'I don\'t know how I know that. ' +
'I took four years of Spanish.',
speak_checkFunc: 'todo_is' // Bug 980510
}],
// XXX: extra move op here because of bug 980510.
[ContentMessages.simpleMoveNext, {
speak: 'You\'re a good guy, mon frere. ' +
'That means brother in French. ' +

1
aclocal.m4 vendored
View File

@ -34,6 +34,7 @@ builtin(include, build/autoconf/python-virtualenv.m4)dnl
builtin(include, build/autoconf/winsdk.m4)dnl
builtin(include, build/autoconf/icu.m4)dnl
builtin(include, build/autoconf/ffi.m4)dnl
builtin(include, build/autoconf/clang-plugin.m4)dnl
MOZ_PROG_CHECKMSYS()

View File

@ -21,6 +21,7 @@
<head>
<title>&loadError.label;</title>
<link rel="stylesheet" href="chrome://browser/content/aboutneterror/netError.css" type="text/css" media="all" />
<link rel="stylesheet" href="chrome://browser/skin/aboutNetError.css" type="text/css" media="all" />
<!-- If the location of the favicon is changed here, the FAVICON_ERRORPAGE_URL symbol in
toolkit/components/places/src/nsFaviconService.h should be updated. -->
<link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/warning-16.png"/>

View File

@ -109,9 +109,9 @@ function onSubmitStatus(subj, topic, data) {
ok(!!remoteID, "serverCrashID should be set");
// Remove the submitted report file.
let file = Services.dirsvc.get("UAppData", Ci.nsILocalFile);
file.append("Crash Reports");
file.append("submitted");
let file = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsILocalFile);
file.initWithPath(Services.crashmanager._submittedDumpsDir);
file.append(remoteID + ".txt");
ok(file.exists(), "Submitted report file should exist");
file.remove(false);

View File

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
Cu.import("resource://gre/modules/Services.jsm");
const CRASH_URL = "http://example.com/browser/browser/base/content/test/plugins/plugin_crashCommentAndURL.html";
@ -85,7 +86,7 @@ function doNextRun() {
}
}
function onCrash() {
function onCrash(event) {
try {
let plugin = gBrowser.contentDocument.getElementById("plugin");
let elt = gPluginHandler.getPluginUI.bind(gPluginHandler, plugin);
@ -95,7 +96,13 @@ function onCrash() {
currentRun.shouldSubmissionUIBeVisible ? "block" : "none",
"Submission UI visibility should be correct");
if (!currentRun.shouldSubmissionUIBeVisible) {
// Done with this run.
// Done with this run. We don't submit the crash, so we will have to
// remove the dump manually.
let propBag = event.detail.QueryInterface(Ci.nsIPropertyBag2);
let crashID = propBag.getPropertyAsAString("pluginDumpID");
ok(!!crashID, "pluginDumpID should be set");
CrashSubmit.delete(crashID);
doNextRun();
return;
}
@ -116,8 +123,21 @@ function onSubmitStatus(subj, topic, data) {
if (data != "success" && data != "failed")
return;
let extra = getPropertyBagValue(subj.QueryInterface(Ci.nsIPropertyBag),
"extra");
let propBag = subj.QueryInterface(Ci.nsIPropertyBag);
if (data == "success") {
let remoteID = getPropertyBagValue(propBag, "serverCrashID");
ok(!!remoteID, "serverCrashID should be set");
// Remove the submitted report file.
let file = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsILocalFile);
file.initWithPath(Services.crashmanager._submittedDumpsDir);
file.append(remoteID + ".txt");
ok(file.exists(), "Submitted report file should exist");
file.remove(false);
}
let extra = getPropertyBagValue(propBag, "extra");
ok(extra instanceof Ci.nsIPropertyBag, "Extra data should be property bag");
let val = getPropertyBagValue(extra, "PluginUserComment");

View File

@ -52,6 +52,7 @@ function CustomizeMode(aWindow) {
this.window = aWindow;
this.document = aWindow.document;
this.browser = aWindow.gBrowser;
this.areas = new Set();
// There are two palettes - there's the palette that can be overlayed with
// toolbar items in browser.xul. This is invisible, and never seen by the
@ -223,11 +224,16 @@ CustomizeMode.prototype = {
customizer.parentNode.selectedPanel = customizer;
customizer.hidden = false;
this._wrapToolbarItemSync(CustomizableUI.AREA_TABSTRIP);
let customizableToolbars = document.querySelectorAll("toolbar[customizable=true]:not([autohide=true]):not([collapsed=true])");
for (let toolbar of customizableToolbars)
toolbar.setAttribute("customizing", true);
yield this._doTransition(true);
Services.obs.addObserver(this, "lightweight-theme-window-updated", false);
// Let everybody in this window know that we're about to customize.
CustomizableUI.dispatchToolboxEvent("customizationstarting", {}, window);
@ -254,10 +260,6 @@ CustomizeMode.prototype = {
this._skipSourceNodeCheck = Services.prefs.getPrefType(kSkipSourceNodePref) == Ci.nsIPrefBranch.PREF_BOOL &&
Services.prefs.getBoolPref(kSkipSourceNodePref);
let customizableToolbars = document.querySelectorAll("toolbar[customizable=true]:not([autohide=true]):not([collapsed=true])");
for (let toolbar of customizableToolbars)
toolbar.setAttribute("customizing", true);
CustomizableUI.addListener(this);
window.PanelUI.endBatchUpdate();
this._customizing = true;
@ -419,7 +421,7 @@ CustomizeMode.prototype = {
}
// And drop all area references.
this.areas = [];
this.areas.clear();
// Let everybody in this window know that we're starting to
// exit customization mode.
@ -500,15 +502,15 @@ CustomizeMode.prototype = {
let deferred = Promise.defer();
let deck = this.document.getElementById("content-deck");
let customizeTransitionEnd = function(aEvent) {
let customizeTransitionEnd = (aEvent) => {
if (aEvent != "timedout" &&
(aEvent.originalTarget != deck || aEvent.propertyName != "margin-left")) {
return;
}
this.window.clearTimeout(catchAllTimeout);
// Bug 962677: We let the event loop breathe for before we do the final
// stage of the transition to improve perceived performance.
this.window.setTimeout(function () {
// We request an animation frame to do the final stage of the transition
// to improve perceived performance. (bug 962677)
this.window.requestAnimationFrame(() => {
deck.removeEventListener("transitionend", customizeTransitionEnd);
if (!aEntering) {
@ -521,13 +523,14 @@ CustomizeMode.prototype = {
CustomizableUI.dispatchToolboxEvent("customization-transitionend", aEntering, this.window);
deferred.resolve();
}.bind(this), 0);
}.bind(this);
});
};
deck.addEventListener("transitionend", customizeTransitionEnd);
if (gDisableAnimation) {
this.document.getElementById("tab-view-deck").setAttribute("fastcustomizeanimation", true);
}
if (aEntering) {
this.document.documentElement.setAttribute("customizing", true);
this.document.documentElement.setAttribute("customize-entering", true);
@ -797,7 +800,7 @@ CustomizeMode.prototype = {
dispatchFunction(function() {
let wrapper = this.wrapToolbarItem(aNode, aPlace);
deferred.resolve(wrapper);
}.bind(this))
}.bind(this));
return deferred.promise;
},
@ -969,22 +972,47 @@ CustomizeMode.prototype = {
return toolbarItem;
},
_wrapToolbarItems: function() {
let window = this.window;
// Add drag-and-drop event handlers to all of the customizable areas.
return Task.spawn(function() {
this.areas = [];
for (let area of CustomizableUI.areas) {
let target = CustomizableUI.getCustomizeTargetForArea(area, window);
this._addDragHandlers(target);
for (let child of target.children) {
if (this.isCustomizableItem(child)) {
yield this.deferredWrapToolbarItem(child, CustomizableUI.getPlaceForItem(child));
}
}
this.areas.push(target);
_wrapToolbarItem: function*(aArea) {
let target = CustomizableUI.getCustomizeTargetForArea(aArea, this.window);
if (!target || this.areas.has(target)) {
return null;
}
this._addDragHandlers(target);
for (let child of target.children) {
if (this.isCustomizableItem(child) && !this.isWrappedToolbarItem(child)) {
yield this.deferredWrapToolbarItem(child, CustomizableUI.getPlaceForItem(child)).then(null, ERROR);
}
}.bind(this)).then(null, ERROR);
}
this.areas.add(target);
return target;
},
_wrapToolbarItemSync: function(aArea) {
let target = CustomizableUI.getCustomizeTargetForArea(aArea, this.window);
if (!target || this.areas.has(target)) {
return null;
}
this._addDragHandlers(target);
try {
for (let child of target.children) {
if (this.isCustomizableItem(child) && !this.isWrappedToolbarItem(child)) {
this.wrapToolbarItem(child, CustomizableUI.getPlaceForItem(child));
}
}
} catch (ex) {
ERROR(ex, ex.stack);
}
this.areas.add(target);
return target;
},
_wrapToolbarItems: function*() {
for (let area of CustomizableUI.areas) {
yield this._wrapToolbarItem(area);
}
},
_addDragHandlers: function(aTarget) {
@ -1208,7 +1236,7 @@ CustomizeMode.prototype = {
this._wrapItemsInArea(aContainer);
this._addDragHandlers(aContainer);
DragPositionManager.add(this.window, aArea, aContainer);
this.areas.push(aContainer);
this.areas.add(aContainer);
}
},
@ -1217,8 +1245,7 @@ CustomizeMode.prototype = {
this._unwrapItemsInArea(aContainer);
this._removeDragHandlers(aContainer);
DragPositionManager.remove(this.window, aArea, aContainer);
let index = this.areas.indexOf(aContainer);
this.areas.splice(index, 1);
this.areas.delete(aContainer);
}
},

View File

@ -42,12 +42,12 @@ add_task(function*() {
info("Check that removing the area registration from within customize mode works");
CustomizableUI.unregisterArea(TOOLBARID);
ok(CustomizableUI.inDefaultState, "Now that the toolbar is no longer registered, should be in default state.");
ok(!(new Set(gCustomizeMode.areas)).has(toolbar), "Toolbar shouldn't be known to customize mode.");
ok(!gCustomizeMode.areas.has(toolbar), "Toolbar shouldn't be known to customize mode.");
CustomizableUI.registerArea(TOOLBARID, {legacy: true, defaultPlacements: []});
CustomizableUI.registerToolbarNode(toolbar, []);
ok(!CustomizableUI.inDefaultState, "Now that the toolbar is registered again, should no longer be in default state.");
ok((new Set(gCustomizeMode.areas)).has(toolbar), "Toolbar should be known to customize mode again.");
ok(gCustomizeMode.areas.has(toolbar), "Toolbar should be known to customize mode again.");
simulateItemDrag(syncButton, toolbar);
ok(CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved out of palette");
@ -118,7 +118,7 @@ add_task(function*() {
ok(wasInformedCorrectlyOfAreaDisappearing, "Should be told about window closing.");
// Closing the other window should not be counted against this window's customize mode:
is(syncButton.parentNode.localName, "toolbarpaletteitem", "Sync button's parent node should still be a wrapper.");
isnot(gCustomizeMode.areas.indexOf(toolbar), -1, "Toolbar should still be a customizable area for this customize mode instance.");
ok(gCustomizeMode.areas.has(toolbar), "Toolbar should still be a customizable area for this customize mode instance.");
yield gCustomizeMode.reset();

View File

@ -627,23 +627,6 @@ let gInitializeTimerFunc = () => {
* Public API
*/
this.MozLoopService = {
#ifdef DEBUG
// Test-only helpers
get internal() {
return MozLoopServiceInternal;
},
get gFxAOAuthTokenData() {
return gFxAOAuthTokenData;
},
resetFxA: function() {
gFxAOAuthClientPromise = null;
gFxAOAuthClient = null;
gFxAOAuthTokenData = null;
},
#endif
_DNSService: gDNSService,
set initializeTimerFunc(value) {

View File

@ -11,7 +11,7 @@
<link rel="stylesheet" type="text/css" href="loop/shared/css/common.css">
<link rel="stylesheet" type="text/css" href="loop/shared/css/conversation.css">
</head>
<body class="conversation-window" onload="loop.conversation.init();">
<body class="fx-embedded" onload="loop.conversation.init();">
<div id="messages"></div>

View File

@ -27,10 +27,14 @@ loop.conversation = (function(OT, mozL10n) {
model: React.PropTypes.object.isRequired
},
getInitialState: function() {
getInitialProps: function() {
return {showDeclineMenu: false};
},
getInitialState: function() {
return {showDeclineMenu: this.props.showDeclineMenu};
},
componentDidMount: function() {
window.addEventListener("click", this.clickHandler);
window.addEventListener("blur", this._hideDeclineMenu);

View File

@ -27,10 +27,14 @@ loop.conversation = (function(OT, mozL10n) {
model: React.PropTypes.object.isRequired
},
getInitialState: function() {
getInitialProps: function() {
return {showDeclineMenu: false};
},
getInitialState: function() {
return {showDeclineMenu: this.props.showDeclineMenu};
},
componentDidMount: function() {
window.addEventListener("click", this.clickHandler);
window.addEventListener("blur", this._hideDeclineMenu);

View File

@ -18,19 +18,11 @@ body {
background: #fbfbfb;
}
button {
/* Resetting default <button> font properties; eg. strangely enough, FF mac
wants to use Helvetica/12px whatever we define for parent elements */
font-family: "Lucida Grande", sans-serif;
font-size: 1em;
}
img {
border: none;
}
h1, h2, h3 {
font-family: "Open Sans", sans-serif;
color: #666;
}
@ -220,8 +212,9 @@ p {
justify-content: center;
}
.btn-group .btn {
.btn-chevron-menu-group .btn {
flex: 1;
border-radius: 2px;
border-bottom-right-radius: 0;
border-top-right-radius: 0;
}
@ -250,8 +243,8 @@ p {
.alert .close {
position: relative;
top: -.2em;
right: -1em;
top: -.1rem;
right: -1rem;
}
/* Misc */
@ -269,24 +262,25 @@ p {
.close {
float: right;
font-size: 20px;
font-size: 1rem;
font-weight: bold;
line-height: 1em;
line-height: 1rem;
color: #000;
opacity: .2;
opacity: .4;
background: none;
border: none;
cursor: pointer;
}
.close:hover {
opacity: .6;
}
.close:before {
/* \2716 is unicode representation of the close button icon */
content: '\2716';
}
.btn.close {
background: none;
border: none;
cursor: pointer;
}
/* Transitions */
.fade-out {
transition: opacity 0.5s ease-in;
@ -346,15 +340,19 @@ p {
line-height: 16px;
}
.windows {
/* Using star selector to override
* the specificity of other selectors
* if performance is an issue we could
* explicitely list all the elements */
.windows * {
font-family: 'Segoe';
}
.mac {
.mac * {
font-family: 'Lucida Grande';
}
.linux {
.linux * {
/* XXX requires fallbacks */
font-family: 'Ubuntu', sans-serif;
}

View File

@ -3,7 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Shared conversation window styles */
.standalone .video-layout-wrapper {
.standalone .video-layout-wrapper,
.conversation .media video {
background-color: #444;
}
@ -11,37 +12,25 @@
position: relative;
}
.standalone .conversation {
margin: 0 auto;
max-height: 100vh;
}
.conversation-toolbar {
z-index: 999; /* required to have it superimposed to the video element */
border: 1px solid #5a5a5a;
border-left: 0;
border-right: 0;
}
.conversation .conversation-toolbar {
position: absolute;
left: 0;
right: 0;
background: rgba(0,0,0,.70);
}
/* desktop version */
.conversation-window .conversation-toolbar {
.fx-embedded .conversation-toolbar {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 26px;
background: rgba(0,0,0,.70);
}
/* standalone version */
.standalone .conversation-toolbar {
bottom: 0;
}
.standalone .conversation-toolbar {
background: rgba(0,0,0,.50);
padding: 20px;
height: 64px;
}
@ -111,11 +100,11 @@
padding: 0 20px;
}
.conversation-window .conversation-toolbar .btn-hangup {
.fx-embedded .conversation-toolbar .btn-hangup {
background-image: url(../img/hangup-inverse-14x14.png);
}
@media (min-resolution: 2dppx) {
.conversation-window .conversation-toolbar .btn-hangup {
.fx-embedded .conversation-toolbar .btn-hangup {
background-image: url(../img/hangup-inverse-14x14@2x.png);
}
}
@ -166,54 +155,17 @@
}
}
/* Video elements */
.conversation .media video {
background: #eee;
}
/* Nested video elements */
.conversation .media.nested {
position: relative;
text-align: center;
}
/* fluid aspect ratio trick, see http://stackoverflow.com/a/10441480/330911 */
.conversation .media.nested .remote_wrapper {
display: inline-block;
position: relative;
width: 100%;
padding-bottom: 75%; /* XXX forced 4:3 ratio, see bug 1020445 */
}
.conversation .media.nested .remote {
display: inline-block;
position: absolute; /* workaround for lack of object-fit; see bug 1020445 */
width: 100%;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: #000;
}
.conversation .media.nested .local {
.fx-embedded .remote_wrapper {
position: absolute;
bottom: 4px;
right: 0;
width: 30%;
max-width: 140px;
/* next two lines are workaround for lack of object-fit; see bug 1020445 */
height: 22.5%;
max-height: 105px;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
}
.standalone .conversation .media.nested .local {
/* required to have it superimposed on the control toolbar */
.standalone .local-stream {
/* required to have it superimposed to the control toolbar */
z-index: 1001;
bottom: 10px;
right: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
@ -224,7 +176,7 @@
float: left;
}
.conversation .media.side-by-side .local {
.conversation .media.side-by-side .local-stream {
width: 50%;
}
@ -262,7 +214,6 @@
.incoming-call-action-group .btn-group-chevron,
.incoming-call-action-group .btn-group {
width: 100%;
max-width: 120px; /* required by the UI Showcase, but the not real code */
}
/* XXX Once we get the incoming call avatar, bug 1047435, the H2 should
@ -474,3 +425,151 @@
color: #CCC;
text-align: center;
}
.fx-embedded .local-stream {
position: absolute;
right: 3px;
bottom: 5px;
/* next two lines are workaround for lack of object-fit; see bug 1020445 */
max-width: 140px;
width: 30%;
height: 28%;
max-height: 105px;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.5);
}
.conversation .media.nested .remote {
display: inline-block;
position: absolute; /* workaround for lack of object-fit; see bug 1020445 */
width: 100%;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
/*
* XXX this approach is fragile because it makes assumptions
* about the generated OT markup, any change will break it
*/
.local-stream.local-stream-audio,
.standalone .OT_subscriber .OT_video-poster,
.fx-embedded .OT_video-container .OT_video-poster,
.local-stream-audio .OT_publisher .OT_video-poster {
background-image: url("../img/audio-call-avatar.svg");
background-repeat: no-repeat;
background-color: #4BA6E7;
background-size: contain;
background-position: center;
}
.fx-embedded .media.nested {
min-height: 200px;
}
@media screen and (min-width:640px) {
/* Force full height on all parents up to the video elements
* this way we can ensure the aspect ratio and use height 100%
* on the video element
* */
html, body, #main,
.video-layout-wrapper,
.conversation {
height: 100%;
}
.standalone .conversation-toolbar {
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
.fx-embedded .local-stream {
position: fixed;
}
.standalone .local-stream {
position: absolute;
right: 15px;
bottom: 15px;
width: 20%;
height: 20%;
max-width: 400px;
max-height: 300px;
}
/* Nested video elements */
.conversation .media.nested {
position: relative;
height: 100%;
}
.standalone .remote_wrapper {
position: relative;
width: 100%;
height: 100%;
}
.standalone {
max-width: 1000px;
margin: 0 auto;
}
}
@media screen and (max-width:640px) {
.standalone .video-layout-wrapper,
.standalone .conversation {
height: 100%;
}
.standalone .media {
height: 90%;
}
.standalone .OT_subscriber {
height: 100%;
width: auto;
}
.standalone .media.nested {
min-height: 500px;
}
.standalone .local-stream {
flex: 1;
min-width: 120px;
min-height: 150px;
width: 100%;
box-shadow: none;
}
/* Nested video elements */
.conversation .media.nested {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex: 1 1 0%;
}
.standalone .video_wrapper.remote_wrapper {
/* Because of OT markup we need to set a high flex value
* Flex rule assures remote and local streams stack on top of eachother
* Computed width is not 100% unless the `width` rule */
flex: 2;
width: 100%;
position: relative;
}
}
@media screen and (max-width:420px) {
/* Restore video height so that we get
* vertical centering for free on a small screen
**/
.standalone .conversation .media video {
height: 100%;
}
}

View File

@ -59,6 +59,11 @@
text-align: center;
}
.btn-email,
.btn-copy {
border-radius: 2px;
}
.share > .action .btn:hover {
background-color: #008ACB;
border: 1px solid #008ACB;

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="129 5 252 253" width="21pc" height="253pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2014-08-13 17:00Z</dc:date><!-- Produced by OmniGraffle Professional 5.4.4 --></metadata><defs><linearGradient x1="0" x2="1" id="Gradient" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#4ba6e9"/><stop offset="1" stop-color="#4ba7bf"/></linearGradient></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><g><title>Layer 1</title><rect x="129" y="5" width="252" height="252" fill="#4BA6E7"/><circle cx="255" cy="106" r="54.000088" fill="#badbeb"/><path d="M 159.01782 257 L 350.98218 257 C 351.46667 236.66908 342.1 216.2136 322.88218 200.69928 C 285.39187 170.43352 224.60813 170.43352 187.11782 200.69928 C 167.9 216.2136 158.53333 236.66909 159.01782 257 Z" fill="#badbeb"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -6,7 +6,7 @@
var loop = loop || {};
loop.shared = loop.shared || {};
loop.shared.router = (function(l10n) {
loop.shared.router = (function() {
"use strict";
/**
@ -186,4 +186,4 @@ loop.shared.router = (function(l10n) {
BaseRouter: BaseRouter,
BaseConversationRouter: BaseConversationRouter
};
})(document.webL10n || document.mozL10n);
})();

View File

@ -253,6 +253,25 @@ loop.shared.views = (function(_, OT, l10n) {
this.stopPublishing);
this.props.model.startSession();
/**
* OT inserts inline styles into the markup. Using a listener for
* resize events helps us trigger a full width/height on the element
* so that they update to the correct dimensions.
* */
window.addEventListener('orientationchange', this.updateVideoContainer);
window.addEventListener('resize', this.updateVideoContainer);
},
updateVideoContainer: function() {
var localStreamParent = document.querySelector('.local .OT_publisher');
var remoteStreamParent = document.querySelector('.remote .OT_subscriber');
if (localStreamParent) {
localStreamParent.style.width = "100%";
}
if (remoteStreamParent) {
remoteStreamParent.style.height = "100%";
}
},
componentWillUnmount: function() {
@ -345,20 +364,25 @@ loop.shared.views = (function(_, OT, l10n) {
},
render: function() {
var localStreamClasses = React.addons.classSet({
local: true,
"local-stream": true,
"local-stream-audio": !this.state.video.enabled
});
/* jshint ignore:start */
return (
React.DOM.div({className: "video-layout-wrapper"},
React.DOM.div({className: "conversation"},
ConversationToolbar({video: this.state.video,
audio: this.state.audio,
publishStream: this.publishStream,
hangup: this.hangup}),
React.DOM.div({className: "media nested"},
React.DOM.div({className: "video_wrapper remote_wrapper"},
React.DOM.div({className: "video_inner remote"})
),
React.DOM.div({className: "local"})
)
React.DOM.div({className: localStreamClasses})
),
ConversationToolbar({video: this.state.video,
audio: this.state.audio,
publishStream: this.publishStream,
hangup: this.hangup})
)
)
);
@ -735,7 +759,7 @@ loop.shared.views = (function(_, OT, l10n) {
/**
* Adds a l10n rror notification to the stack and renders it.
*
* @param {String} messageId L10n message id
* @param {String} messageId L10n message id
*/
errorL10n: function(messageId) {
this.error(l10n.get(messageId));
@ -797,4 +821,4 @@ loop.shared.views = (function(_, OT, l10n) {
UnsupportedBrowserView: UnsupportedBrowserView,
UnsupportedDeviceView: UnsupportedDeviceView
};
})(_, window.OT, document.webL10n || document.mozL10n);
})(_, window.OT, navigator.mozL10n || document.mozL10n);

View File

@ -253,6 +253,25 @@ loop.shared.views = (function(_, OT, l10n) {
this.stopPublishing);
this.props.model.startSession();
/**
* OT inserts inline styles into the markup. Using a listener for
* resize events helps us trigger a full width/height on the element
* so that they update to the correct dimensions.
* */
window.addEventListener('orientationchange', this.updateVideoContainer);
window.addEventListener('resize', this.updateVideoContainer);
},
updateVideoContainer: function() {
var localStreamParent = document.querySelector('.local .OT_publisher');
var remoteStreamParent = document.querySelector('.remote .OT_subscriber');
if (localStreamParent) {
localStreamParent.style.width = "100%";
}
if (remoteStreamParent) {
remoteStreamParent.style.height = "100%";
}
},
componentWillUnmount: function() {
@ -345,20 +364,25 @@ loop.shared.views = (function(_, OT, l10n) {
},
render: function() {
var localStreamClasses = React.addons.classSet({
local: true,
"local-stream": true,
"local-stream-audio": !this.state.video.enabled
});
/* jshint ignore:start */
return (
<div className="video-layout-wrapper">
<div className="conversation">
<ConversationToolbar video={this.state.video}
audio={this.state.audio}
publishStream={this.publishStream}
hangup={this.hangup} />
<div className="media nested">
<div className="video_wrapper remote_wrapper">
<div className="video_inner remote"></div>
</div>
<div className="local"></div>
<div className={localStreamClasses}></div>
</div>
<ConversationToolbar video={this.state.video}
audio={this.state.audio}
publishStream={this.publishStream}
hangup={this.hangup} />
</div>
</div>
);
@ -735,7 +759,7 @@ loop.shared.views = (function(_, OT, l10n) {
/**
* Adds a l10n rror notification to the stack and renders it.
*
* @param {String} messageId L10n message id
* @param {String} messageId L10n message id
*/
errorL10n: function(messageId) {
this.error(l10n.get(messageId));
@ -797,4 +821,4 @@ loop.shared.views = (function(_, OT, l10n) {
UnsupportedBrowserView: UnsupportedBrowserView,
UnsupportedDeviceView: UnsupportedDeviceView
};
})(_, window.OT, document.webL10n || document.mozL10n);
})(_, window.OT, navigator.mozL10n || document.mozL10n);

File diff suppressed because one or more lines are too long

View File

@ -44,6 +44,7 @@ browser.jar:
content/browser/loop/shared/img/svg/glyph-account-16x16.svg (content/shared/img/svg/glyph-account-16x16.svg)
content/browser/loop/shared/img/svg/glyph-signin-16x16.svg (content/shared/img/svg/glyph-signin-16x16.svg)
content/browser/loop/shared/img/svg/glyph-signout-16x16.svg (content/shared/img/svg/glyph-signout-16x16.svg)
content/browser/loop/shared/img/audio-call-avatar.svg (content/shared/img/audio-call-avatar.svg)
# Shared scripts
content/browser/loop/shared/js/feedbackApiClient.js (content/shared/js/feedbackApiClient.js)
@ -54,7 +55,11 @@ browser.jar:
content/browser/loop/shared/js/websocket.js (content/shared/js/websocket.js)
# Shared libs
#ifdef DEBUG
content/browser/loop/shared/libs/react-0.11.1.js (content/shared/libs/react-0.11.1.js)
#else
content/browser/loop/shared/libs/react-0.11.1.js (content/shared/libs/react-0.11.1-prod.js)
#endif
content/browser/loop/shared/libs/lodash-2.4.1.js (content/shared/libs/lodash-2.4.1.js)
content/browser/loop/shared/libs/jquery-2.1.0.js (content/shared/libs/jquery-2.1.0.js)
content/browser/loop/shared/libs/backbone-1.1.2.js (content/shared/libs/backbone-1.1.2.js)

View File

@ -18,9 +18,6 @@ EXTRA_JS_MODULES.loop += [
'LoopStorage.jsm',
'MozLoopAPI.jsm',
'MozLoopPushHandler.jsm',
'MozLoopService.jsm',
'MozLoopWorker.js',
]
EXTRA_PP_JS_MODULES.loop += [
'MozLoopService.jsm',
]

View File

@ -109,6 +109,7 @@ body,
.standalone-header-title {
font-size: 1.8rem;
line-height: 2.2rem;
}
.standalone-call-btn-label {
@ -137,8 +138,9 @@ body,
.start-audio-only-call {
width: 100%;
border: none;
color: #111;
background-color: #fff;
background-size: 10px;
background-color: #F0F0F0;
background-image: url("../shared/img/audio-default-16x16@1.5x.png");
background-position: 90% center;
}

View File

@ -26,7 +26,7 @@
window.OTProperties.cssURL = window.OTProperties.assetURL + 'css/ot.css';
</script>
<script type="text/javascript" src="shared/libs/sdk.js"></script>
<script type="text/javascript" src="libs/webl10n-20130617.js"></script>
<script type="text/javascript" src="libs/l10n-gaia-4e35bf8f0569.js"></script>
<script type="text/javascript" src="shared/libs/react-0.11.1.js"></script>
<script type="text/javascript" src="shared/libs/jquery-2.1.0.js"></script>
<script type="text/javascript" src="shared/libs/lodash-2.4.1.js"></script>

View File

@ -8,7 +8,7 @@
/* jshint newcap:false */
var loop = loop || {};
loop.webapp = (function($, _, OT, webL10n) {
loop.webapp = (function($, _, OT, mozL10n) {
"use strict";
loop.config = loop.config || {};
@ -16,8 +16,7 @@ loop.webapp = (function($, _, OT, webL10n) {
var sharedModels = loop.shared.models,
sharedViews = loop.shared.views,
baseServerUrl = loop.config.serverUrl,
__ = webL10n.get;
baseServerUrl = loop.config.serverUrl;
/**
* App router.
@ -46,11 +45,11 @@ loop.webapp = (function($, _, OT, webL10n) {
}
return (
React.DOM.div({className: "promote-firefox"},
React.DOM.h3(null, __("promote_firefox_hello_heading")),
React.DOM.h3(null, mozL10n.get("promote_firefox_hello_heading")),
React.DOM.p(null,
React.DOM.a({className: "btn btn-large btn-accept",
href: "https://www.mozilla.org/firefox/"},
__("get_firefox_button")
mozL10n.get("get_firefox_button")
)
)
)
@ -72,8 +71,8 @@ loop.webapp = (function($, _, OT, webL10n) {
React.DOM.div({className: "expired-url-info"},
React.DOM.div({className: "info-panel"},
React.DOM.div({className: "firefox-logo"}),
React.DOM.h1(null, __("call_url_unavailable_notification_heading")),
React.DOM.h4(null, __("call_url_unavailable_notification_message2"))
React.DOM.h1(null, mozL10n.get("call_url_unavailable_notification_heading")),
React.DOM.h4(null, mozL10n.get("call_url_unavailable_notification_message2"))
),
PromoteFirefoxView({helper: this.props.helper})
)
@ -94,7 +93,7 @@ loop.webapp = (function($, _, OT, webL10n) {
"hide": !this.props.urlCreationDateString.length
});
var callUrlCreationDateString = __("call_url_creation_date_label", {
var callUrlCreationDateString = mozL10n.get("call_url_creation_date_label", {
"call_url_creation_date": this.props.urlCreationDateString
});
@ -102,7 +101,7 @@ loop.webapp = (function($, _, OT, webL10n) {
/* jshint ignore:start */
React.DOM.header({className: "standalone-header container-box"},
React.DOM.h1({className: "standalone-header-title"},
React.DOM.strong(null, __("brandShortname")), " ", __("clientShortname")
React.DOM.strong(null, mozL10n.get("brandShortname")), " ", mozL10n.get("clientShortname")
),
React.DOM.div({className: "loop-logo", title: "Firefox WebRTC! logo"}),
React.DOM.h3({className: "call-url"},
@ -141,11 +140,15 @@ loop.webapp = (function($, _, OT, webL10n) {
*
*/
getInitialProps: function() {
return {showCallOptionsMenu: false};
},
getInitialState: function() {
return {
urlCreationDateString: '',
disableCallButton: false,
showCallOptionsMenu: false
showCallOptionsMenu: this.props.showCallOptionsMenu
};
},
@ -219,18 +222,16 @@ loop.webapp = (function($, _, OT, webL10n) {
},
render: function() {
var tos_link_name = __("terms_of_use_link_text");
var privacy_notice_name = __("privacy_notice_link_text");
var tos_link_name = mozL10n.get("terms_of_use_link_text");
var privacy_notice_name = mozL10n.get("privacy_notice_link_text");
var tosHTML = __("legal_text_and_links", {
var tosHTML = mozL10n.get("legal_text_and_links", {
"terms_of_use_url": "<a target=_blank href='" +
"https://accounts.firefox.com/legal/terms'>" + tos_link_name + "</a>",
"privacy_notice_url": "<a target=_blank href='" +
"https://www.mozilla.org/privacy/'>" + privacy_notice_name + "</a>"
});
var btnClassStartCall = "btn btn-large btn-accept " +
loop.shared.utils.getTargetPlatform();
var dropdownMenuClasses = React.addons.classSet({
"native-dropdown-large-parent": true,
"standalone-dropdown-menu": true,
@ -250,7 +251,7 @@ loop.webapp = (function($, _, OT, webL10n) {
urlCreationDateString: this.state.urlCreationDateString}),
React.DOM.p({className: "standalone-call-btn-label"},
__("initiate_call_button_label2")
mozL10n.get("initiate_call_button_label2")
),
React.DOM.div({id: "messages"}),
@ -261,18 +262,18 @@ loop.webapp = (function($, _, OT, webL10n) {
React.DOM.div({className: "btn-group-chevron"},
React.DOM.div({className: "btn-group"},
React.DOM.button({className: btnClassStartCall,
React.DOM.button({className: "btn btn-large btn-accept",
onClick: this._initiateOutgoingCall("audio-video"),
disabled: this.state.disableCallButton,
title: __("initiate_audio_video_call_tooltip2")},
title: mozL10n.get("initiate_audio_video_call_tooltip2")},
React.DOM.span({className: "standalone-call-btn-text"},
__("initiate_audio_video_call_button2")
mozL10n.get("initiate_audio_video_call_button2")
),
React.DOM.span({className: "standalone-call-btn-video-icon"})
),
React.DOM.div({className: "btn-chevron",
onClick: this._toggleCallOptionsMenu}
onClick: this._toggleCallOptionsMenu}
)
),
@ -285,7 +286,7 @@ loop.webapp = (function($, _, OT, webL10n) {
React.DOM.button({className: "start-audio-only-call",
onClick: this._initiateOutgoingCall("audio"),
disabled: this.state.disableCallButton},
__("initiate_audio_call_button2")
mozL10n.get("initiate_audio_call_button2")
)
)
)
@ -571,11 +572,9 @@ loop.webapp = (function($, _, OT, webL10n) {
router.navigate("unsupportedBrowser", {trigger: true});
}
document.body.classList.add(loop.shared.utils.getTargetPlatform());
// Set the 'lang' and 'dir' attributes to <html> when the page is translated
document.documentElement.lang = document.webL10n.getLanguage();
document.documentElement.dir = document.webL10n.getDirection();
document.documentElement.lang = mozL10n.language.code;
document.documentElement.dir = mozL10n.language.direction;
}
return {
@ -588,4 +587,4 @@ loop.webapp = (function($, _, OT, webL10n) {
WebappHelper: WebappHelper,
WebappRouter: WebappRouter
};
})(jQuery, _, window.OT, document.webL10n);
})(jQuery, _, window.OT, navigator.mozL10n);

View File

@ -8,7 +8,7 @@
/* jshint newcap:false */
var loop = loop || {};
loop.webapp = (function($, _, OT, webL10n) {
loop.webapp = (function($, _, OT, mozL10n) {
"use strict";
loop.config = loop.config || {};
@ -16,8 +16,7 @@ loop.webapp = (function($, _, OT, webL10n) {
var sharedModels = loop.shared.models,
sharedViews = loop.shared.views,
baseServerUrl = loop.config.serverUrl,
__ = webL10n.get;
baseServerUrl = loop.config.serverUrl;
/**
* App router.
@ -46,11 +45,11 @@ loop.webapp = (function($, _, OT, webL10n) {
}
return (
<div className="promote-firefox">
<h3>{__("promote_firefox_hello_heading")}</h3>
<h3>{mozL10n.get("promote_firefox_hello_heading")}</h3>
<p>
<a className="btn btn-large btn-accept"
href="https://www.mozilla.org/firefox/">
{__("get_firefox_button")}
{mozL10n.get("get_firefox_button")}
</a>
</p>
</div>
@ -72,8 +71,8 @@ loop.webapp = (function($, _, OT, webL10n) {
<div className="expired-url-info">
<div className="info-panel">
<div className="firefox-logo" />
<h1>{__("call_url_unavailable_notification_heading")}</h1>
<h4>{__("call_url_unavailable_notification_message2")}</h4>
<h1>{mozL10n.get("call_url_unavailable_notification_heading")}</h1>
<h4>{mozL10n.get("call_url_unavailable_notification_message2")}</h4>
</div>
<PromoteFirefoxView helper={this.props.helper} />
</div>
@ -94,7 +93,7 @@ loop.webapp = (function($, _, OT, webL10n) {
"hide": !this.props.urlCreationDateString.length
});
var callUrlCreationDateString = __("call_url_creation_date_label", {
var callUrlCreationDateString = mozL10n.get("call_url_creation_date_label", {
"call_url_creation_date": this.props.urlCreationDateString
});
@ -102,7 +101,7 @@ loop.webapp = (function($, _, OT, webL10n) {
/* jshint ignore:start */
<header className="standalone-header container-box">
<h1 className="standalone-header-title">
<strong>{__("brandShortname")}</strong> {__("clientShortname")}
<strong>{mozL10n.get("brandShortname")}</strong> {mozL10n.get("clientShortname")}
</h1>
<div className="loop-logo" title="Firefox WebRTC! logo"></div>
<h3 className="call-url">
@ -141,11 +140,15 @@ loop.webapp = (function($, _, OT, webL10n) {
*
*/
getInitialProps: function() {
return {showCallOptionsMenu: false};
},
getInitialState: function() {
return {
urlCreationDateString: '',
disableCallButton: false,
showCallOptionsMenu: false
showCallOptionsMenu: this.props.showCallOptionsMenu
};
},
@ -219,18 +222,16 @@ loop.webapp = (function($, _, OT, webL10n) {
},
render: function() {
var tos_link_name = __("terms_of_use_link_text");
var privacy_notice_name = __("privacy_notice_link_text");
var tos_link_name = mozL10n.get("terms_of_use_link_text");
var privacy_notice_name = mozL10n.get("privacy_notice_link_text");
var tosHTML = __("legal_text_and_links", {
var tosHTML = mozL10n.get("legal_text_and_links", {
"terms_of_use_url": "<a target=_blank href='" +
"https://accounts.firefox.com/legal/terms'>" + tos_link_name + "</a>",
"privacy_notice_url": "<a target=_blank href='" +
"https://www.mozilla.org/privacy/'>" + privacy_notice_name + "</a>"
});
var btnClassStartCall = "btn btn-large btn-accept " +
loop.shared.utils.getTargetPlatform();
var dropdownMenuClasses = React.addons.classSet({
"native-dropdown-large-parent": true,
"standalone-dropdown-menu": true,
@ -250,7 +251,7 @@ loop.webapp = (function($, _, OT, webL10n) {
urlCreationDateString={this.state.urlCreationDateString} />
<p className="standalone-call-btn-label">
{__("initiate_call_button_label2")}
{mozL10n.get("initiate_call_button_label2")}
</p>
<div id="messages"></div>
@ -261,18 +262,18 @@ loop.webapp = (function($, _, OT, webL10n) {
<div className="btn-group-chevron">
<div className="btn-group">
<button className={btnClassStartCall}
<button className="btn btn-large btn-accept"
onClick={this._initiateOutgoingCall("audio-video")}
disabled={this.state.disableCallButton}
title={__("initiate_audio_video_call_tooltip2")} >
title={mozL10n.get("initiate_audio_video_call_tooltip2")} >
<span className="standalone-call-btn-text">
{__("initiate_audio_video_call_button2")}
{mozL10n.get("initiate_audio_video_call_button2")}
</span>
<span className="standalone-call-btn-video-icon"></span>
</button>
<div className="btn-chevron"
onClick={this._toggleCallOptionsMenu}>
onClick={this._toggleCallOptionsMenu}>
</div>
</div>
@ -285,7 +286,7 @@ loop.webapp = (function($, _, OT, webL10n) {
<button className="start-audio-only-call"
onClick={this._initiateOutgoingCall("audio")}
disabled={this.state.disableCallButton} >
{__("initiate_audio_call_button2")}
{mozL10n.get("initiate_audio_call_button2")}
</button>
</li>
</ul>
@ -571,11 +572,9 @@ loop.webapp = (function($, _, OT, webL10n) {
router.navigate("unsupportedBrowser", {trigger: true});
}
document.body.classList.add(loop.shared.utils.getTargetPlatform());
// Set the 'lang' and 'dir' attributes to <html> when the page is translated
document.documentElement.lang = document.webL10n.getLanguage();
document.documentElement.dir = document.webL10n.getDirection();
document.documentElement.lang = mozL10n.language.code;
document.documentElement.dir = mozL10n.language.direction;
}
return {
@ -588,4 +587,4 @@ loop.webapp = (function($, _, OT, webL10n) {
WebappHelper: WebappHelper,
WebappRouter: WebappRouter
};
})(jQuery, _, window.OT, document.webL10n);
})(jQuery, _, window.OT, navigator.mozL10n);

View File

@ -1,71 +1,2 @@
## LOCALIZATION NOTE: In this file, don't translate the part between {{..}}
[en]
restart_call=Rejoin
conversation_has_ended=Your conversation has ended.
call_timeout_notification_text=Your call did not go through.
missing_conversation_info=Missing conversation information.
network_disconnected=The network connection terminated abruptly.
peer_ended_conversation2=The person you were calling has ended the conversation.
connection_error_see_console_notification=Call failed; see console for details.
generic_failure_title=Something went wrong.
generic_failure_with_reason2=You can try again or email a link to be reached at later.
generic_failure_no_reason2=Would you like to try again?
retry_call_button=Retry
feedback_report_user_button=Report User
unable_retrieve_call_info=Unable to retrieve conversation information.
hangup_button_title=Hang up
hangup_button_caption2=Exit
mute_local_audio_button_title=Mute your audio
unmute_local_audio_button_title=Unmute your audio
mute_local_video_button_title=Mute your video
unmute_local_video_button_title=Unmute your video
outgoing_call_title=Start conversation?
call_with_contact_title=Conversation with {{incomingCallIdentity}}
welcome=Welcome to the {{clientShortname}} web client.
incompatible_browser=Incompatible Browser
powered_by_webrtc=The audio and video components of {{clientShortname}} are powered by WebRTC.
use_latest_firefox.innerHTML=Please try this link in a WebRTC-enabled browser, such as <a href="{{ff_url}}">{{brandShortname}}</a>.
incompatible_device=Incompatible device
sorry_device_unsupported=Sorry, {{clientShortname}} does not currently support your device.
use_firefox_windows_mac_linux=Please open this page using the latest {{brandShortname}} on Windows, Android, Mac or Linux.
connection_error_see_console_notification=Call failed; see console for details.
call_url_unavailable_notification_heading=Oops!
call_url_unavailable_notification_message2=Sorry, this URL is not available. It may be expired or entered incorrectly.
promote_firefox_hello_heading=Download {{brandShortname}} to make free audio and video calls!
get_firefox_button=Get {{brandShortname}}
initiate_call_button_label2=Ready to start your conversation?
initiate_audio_video_call_button2=Start
initiate_audio_video_call_tooltip2=Start a video conversation
initiate_audio_call_button2=Voice conversation
reject_incoming_call=Cancel
legal_text_and_links=By using this product you agree to the {{terms_of_use_url}} and {{privacy_notice_url}}
terms_of_use_link_text=Terms of use
privacy_notice_link_text=Privacy notice
brandShortname=Firefox
clientShortname=WebRTC!
## LOCALIZATION NOTE (call_url_creation_date_label): Example output: (from May 26, 2014)
call_url_creation_date_label=(from {{call_url_creation_date}})
call_progress_connecting_description=Connecting…
call_progress_ringing_description=Ringing…
@import url(loop.en-US.properties)
[fr]
call_timeout_notification_text=Votre appel n'a pas abouti.
missing_conversation_info=Informations de communication manquantes.
network_disconnected=La connexion réseau semble avoir été interrompue.
unable_retrieve_call_info=Impossible de récupérer les informations liées à cet appel.
hangup_button_title=Terminer l'appel
hangup_button_caption=Raccrocher
mute_local_audio_button_title=Couper la diffusion audio
unmute_local_audio_button_title=Reprendre la diffusion audio
mute_local_video_button_title=Couper la diffusion vidéo
unmute_local_video_button_title=Reprendre la diffusion vidéo
welcome=Bienvenue sur {{clientShortname}}.
incompatible_browser=Navigateur non supporté
powered_by_webrtc=Les fonctionnalités audio et vidéo de {{clientShortname}} utilisent WebRTC.
use_latest_firefox.innerHTML=Veuillez essayer ce lien dans un navigateur acceptant WebRTC, par exemple <a href="{{ff_url}}">{{brandShortname}}</a>.
incompatible_device=Plateforme non supportée
sorry_device_unsupported=Désolé, {{clientShortname}} ne fonctionne actuellement pas sur votre appareil.
use_firefox_windows_mac_linux=Merci d'ouvrir cette page avec une version récente de {{brandShortname}} pour Windows, Android, Mac ou Linux.
call_url_unavailable_notification_heading=Oups !
promote_firefox_hello_heading=Téléchargez {{brandShortname}} pour passer des appels audio et vidéo gratuitement !
get_firefox_button=Téléchargez {{brandShortname}}

View File

@ -0,0 +1,48 @@
## LOCALIZATION NOTE: In this file, don't translate the part between {{..}}
restart_call=Rejoin
conversation_has_ended=Your conversation has ended.
call_timeout_notification_text=Your call did not go through.
missing_conversation_info=Missing conversation information.
network_disconnected=The network connection terminated abruptly.
peer_ended_conversation2=The person you were calling has ended the conversation.
connection_error_see_console_notification=Call failed; see console for details.
generic_failure_title=Something went wrong.
generic_failure_with_reason2=You can try again or email a link to be reached at later.
generic_failure_no_reason2=Would you like to try again?
retry_call_button=Retry
feedback_report_user_button=Report User
unable_retrieve_call_info=Unable to retrieve conversation information.
hangup_button_title=Hang up
hangup_button_caption2=Exit
mute_local_audio_button_title=Mute your audio
unmute_local_audio_button_title=Unmute your audio
mute_local_video_button_title=Mute your video
unmute_local_video_button_title=Unmute your video
outgoing_call_title=Start conversation?
call_with_contact_title=Conversation with {{incomingCallIdentity}}
welcome=Welcome to the {{clientShortname}} web client.
incompatible_browser=Incompatible Browser
powered_by_webrtc=The audio and video components of {{clientShortname}} are powered by WebRTC.
use_latest_firefox.innerHTML=Please try this link in a WebRTC-enabled browser, such as <a href="{{ff_url}}">{{brandShortname}}</a>.
incompatible_device=Incompatible device
sorry_device_unsupported=Sorry, {{clientShortname}} does not currently support your device.
use_firefox_windows_mac_linux=Please open this page using the latest {{brandShortname}} on Windows, Android, Mac or Linux.
connection_error_see_console_notification=Call failed; see console for details.
call_url_unavailable_notification_heading=Oops!
call_url_unavailable_notification_message2=Sorry, this URL is not available. It may be expired or entered incorrectly.
promote_firefox_hello_heading=Download {{brandShortname}} to make free audio and video calls!
get_firefox_button=Get {{brandShortname}}
initiate_call_button_label2=Ready to start your conversation?
initiate_audio_video_call_button2=Start
initiate_audio_video_call_tooltip2=Start a video conversation
initiate_audio_call_button2=Voice conversation
reject_incoming_call=Cancel
legal_text_and_links=By using this product you agree to the {{terms_of_use_url}} and {{privacy_notice_url}}
terms_of_use_link_text=Terms of use
privacy_notice_link_text=Privacy notice
brandShortname=Firefox
clientShortname=WebRTC!
## LOCALIZATION NOTE (call_url_creation_date_label): Example output: (from May 26, 2014)
call_url_creation_date_label=(from {{call_url_creation_date}})
call_progress_connecting_description=Connecting…
call_progress_ringing_description=Ringing…

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ support-files =
[browser_CardDavImporter.js]
[browser_fxa_login.js]
skip-if = !debug || e10s
skip-if = e10s
[browser_loop_fxa_server.js]
[browser_LoopContacts.js]
[browser_mozLoop_appVersionInfo.js]

View File

@ -7,6 +7,7 @@
"use strict";
const gFxAOAuthTokenData = Cu.import("resource:///modules/loop/MozLoopService.jsm", {}).gFxAOAuthTokenData;
const BASE_URL = "http://mochi.test:8888/browser/browser/components/loop/test/mochitest/loop_fxa.sjs?";
add_task(function* setup() {
@ -29,27 +30,27 @@ add_task(function* checkOAuthParams() {
state: "state",
};
yield promiseOAuthParamsSetup(BASE_URL, params);
let client = yield MozLoopService.internal.promiseFxAOAuthClient();
let client = yield MozLoopServiceInternal.promiseFxAOAuthClient();
for (let key of Object.keys(params)) {
ise(client.parameters[key], params[key], "Check " + key + " was passed to the OAuth client");
}
});
add_task(function* basicAuthorization() {
let result = yield MozLoopService.internal.promiseFxAOAuthAuthorization();
let result = yield MozLoopServiceInternal.promiseFxAOAuthAuthorization();
is(result.code, "code1", "Check code");
is(result.state, "state", "Check state");
});
add_task(function* sameOAuthClientForTwoCalls() {
MozLoopService.resetFxA();
let client1 = yield MozLoopService.internal.promiseFxAOAuthClient();
let client2 = yield MozLoopService.internal.promiseFxAOAuthClient();
resetFxA();
let client1 = yield MozLoopServiceInternal.promiseFxAOAuthClient();
let client2 = yield MozLoopServiceInternal.promiseFxAOAuthClient();
ise(client1, client2, "The same client should be returned");
});
add_task(function* paramsInvalid() {
MozLoopService.resetFxA();
resetFxA();
// Delete the params so an empty object is returned.
yield promiseDeletedOAuthParams(BASE_URL);
let result = null;
@ -64,7 +65,7 @@ add_task(function* paramsInvalid() {
});
add_task(function* params_nonJSON() {
MozLoopService.resetFxA();
resetFxA();
Services.prefs.setCharPref("loop.server", "https://loop.invalid");
let result = null;
let loginPromise = MozLoopService.logInToFxA();
@ -76,7 +77,7 @@ add_task(function* params_nonJSON() {
});
add_task(function* invalidState() {
MozLoopService.resetFxA();
resetFxA();
let params = {
client_id: "client_id",
content_uri: BASE_URL + "/content",
@ -92,7 +93,7 @@ add_task(function* invalidState() {
});
add_task(function* basicRegistration() {
MozLoopService.resetFxA();
resetFxA();
let params = {
client_id: "client_id",
content_uri: BASE_URL + "/content",
@ -102,14 +103,14 @@ add_task(function* basicRegistration() {
};
yield promiseOAuthParamsSetup(BASE_URL, params);
let tokenData = yield MozLoopService.internal.promiseFxAOAuthToken("code1", "state");
let tokenData = yield MozLoopServiceInternal.promiseFxAOAuthToken("code1", "state");
is(tokenData.access_token, "code1_access_token", "Check access_token");
is(tokenData.scope, "profile", "Check scope");
is(tokenData.token_type, "bearer", "Check token_type");
});
add_task(function* registrationWithInvalidState() {
MozLoopService.resetFxA();
resetFxA();
let params = {
client_id: "client_id",
content_uri: BASE_URL + "/content",
@ -119,7 +120,7 @@ add_task(function* registrationWithInvalidState() {
};
yield promiseOAuthParamsSetup(BASE_URL, params);
let tokenPromise = MozLoopService.internal.promiseFxAOAuthToken("code1", "state");
let tokenPromise = MozLoopServiceInternal.promiseFxAOAuthToken("code1", "state");
yield tokenPromise.then(body => {
ok(false, "Promise should have rejected");
},
@ -129,7 +130,7 @@ add_task(function* registrationWithInvalidState() {
});
add_task(function* registrationWith401() {
MozLoopService.resetFxA();
resetFxA();
let params = {
client_id: "client_id",
content_uri: BASE_URL + "/content",
@ -140,7 +141,7 @@ add_task(function* registrationWith401() {
};
yield promiseOAuthParamsSetup(BASE_URL, params);
let tokenPromise = MozLoopService.internal.promiseFxAOAuthToken("code1", "state");
let tokenPromise = MozLoopServiceInternal.promiseFxAOAuthToken("code1", "state");
yield tokenPromise.then(body => {
ok(false, "Promise should have rejected");
},
@ -150,7 +151,7 @@ add_task(function* registrationWith401() {
});
add_task(function* basicAuthorizationAndRegistration() {
MozLoopService.resetFxA();
resetFxA();
let params = {
client_id: "client_id",
content_uri: BASE_URL + "/content",
@ -167,7 +168,7 @@ add_task(function* basicAuthorizationAndRegistration() {
});
add_task(function* loginWithParams401() {
MozLoopService.resetFxA();
resetFxA();
let params = {
client_id: "client_id",
content_uri: BASE_URL + "/content",
@ -184,12 +185,12 @@ add_task(function* loginWithParams401() {
},
error => {
ise(error.code, 401, "Check error code");
ise(MozLoopService.gFxAOAuthTokenData, null, "Check there is no saved token data");
ise(gFxAOAuthTokenData, null, "Check there is no saved token data");
});
});
add_task(function* loginWithRegistration401() {
MozLoopService.resetFxA();
resetFxA();
let params = {
client_id: "client_id",
content_uri: BASE_URL + "/content",
@ -206,6 +207,6 @@ add_task(function* loginWithRegistration401() {
},
error => {
ise(error.code, 401, "Check error code");
ise(MozLoopService.gFxAOAuthTokenData, null, "Check there is no saved token data");
ise(gFxAOAuthTokenData, null, "Check there is no saved token data");
});
});

View File

@ -7,9 +7,6 @@
"use strict";
const MozLoopServiceInternal = Cu.import("resource:///modules/loop/MozLoopService.jsm", {}).
MozLoopServiceInternal;
registerCleanupFunction(function*() {
MozLoopService.doNotDisturb = false;
yield MozLoopServiceInternal.clearError("testing");

View File

@ -1,6 +1,9 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const MozLoopServiceInternal = Cu.import("resource:///modules/loop/MozLoopService.jsm", {}).
MozLoopServiceInternal;
var gMozLoopAPI;
function promiseGetMozLoopAPI() {
@ -91,6 +94,13 @@ function promiseOAuthParamsSetup(baseURL, params) {
return deferred.promise;
}
function resetFxA() {
let global = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
global.gFxAOAuthClientPromise = null;
global.gFxAOAuthClient = null;
global.gFxAOAuthTokenData = null;
}
function promiseDeletedOAuthParams(baseURL) {
let deferred = Promise.defer();
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].

View File

@ -20,7 +20,7 @@
<script src="../../content/shared/libs/jquery-2.1.0.js"></script>
<script src="../../content/shared/libs/lodash-2.4.1.js"></script>
<script src="../../content/shared/libs/backbone-1.1.2.js"></script>
<script src="../../standalone/content/libs/webl10n-20130617.js"></script>
<script src="../../standalone/content/libs/l10n-gaia-4e35bf8f0569.js"></script>
<!-- test dependencies -->
<script src="vendor/mocha-1.17.1.js"></script>

View File

@ -6,7 +6,7 @@
/* jshint newcap:false */
var expect = chai.expect;
var l10n = document.webL10n || document.mozL10n;
var l10n = navigator.mozL10n || document.mozL10n;
var TestUtils = React.addons.TestUtils;
describe("loop.shared.views", function() {

View File

@ -20,7 +20,7 @@
<script src="../../content/shared/libs/jquery-2.1.0.js"></script>
<script src="../../content/shared/libs/lodash-2.4.1.js"></script>
<script src="../../content/shared/libs/backbone-1.1.2.js"></script>
<script src="../../standalone/content/libs/webl10n-20130617.js"></script>
<script src="../../standalone/content/libs/l10n-gaia-4e35bf8f0569.js"></script>
<!-- test dependencies -->
<script src="../shared/vendor/mocha-1.17.1.js"></script>
<script src="../shared/vendor/chai-1.9.0.js"></script>

View File

@ -8,7 +8,7 @@
* for any requested string id.
* @type {Object}
*/
document.webL10n = document.mozL10n = {
navigator.mozL10n = document.mozL10n = {
get: function(stringId, vars) {
// upcase the first letter

View File

@ -3,15 +3,21 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
.showcase {
width: 730px;
width: 100%;
margin: 0 auto;
}
.showcase-menu,
.showcase {
min-width: 350px;
max-width: 730px;
}
.showcase > header {
position: fixed;
top: 0;
background-color: #fbfbfb;
z-index: 1000;
z-index: 100000;
width: 100%;
padding-bottom: 1em;
}
@ -26,15 +32,17 @@
.showcase-menu > a {
margin-right: .5em;
padding: .4rem;
margin-top: .2rem;
}
.showcase > section {
position: relative;
padding-top: 10em;
padding-top: 12em;
clear: both;
}
.showcase > section > h1 {
margin: 1em 0;
border-bottom: 1px solid #aaa;
}
@ -50,11 +58,18 @@
margin-bottom: 6em;
}
.showcase > section > h2 {
font-size: 1.5em;
font-weight: bold;
margin: 1.5em 0;
}
.showcase > section .example > h3 {
font-size: 1.2em;
font-weight: bold;
border-bottom: 1px dashed #aaa;
margin: .5em 0;
margin: 1em 0;
text-align: left;
}
.showcase p.note {
@ -64,21 +79,73 @@
font-style: italic;
}
.override-position * {
/* Specific for toolbar component atm
* disables position absolute so that the parent div can
* compute the dimensions and prevent collapse */
position: relative !important;
}
/* Just for the showcase to look sane */
.showcase .fx-embedded .local-stream {
position: absolute;
}
.showcase p.note > strong {
font-weight: bold;
}
/* Images as fake videos for conversation view */
.conversation .media.nested .remote,
.conversation .media.nested .local {
background-size: contain;
/*
* Switched to using height: 100% in standalone version
* this mocks it for the ui so that the component has height
* */
.standalone .video-layout-wrapper,
.standalone .remote_wrapper {
min-height: 550px;
}
.conversation .media.nested .remote {
@media screen and (max-width:640px) {
.standalone .local-stream {
background-size: cover;
}
.standalone .local-stream,
.conversation .media.nested .remote {
background-size: cover;
background-position: center;
}
.standalone .remote_wrapper {
width: 100%;
background-size: cover;
background-position: center;
}
}
.remote_wrapper {
background-image: url("sample-img/video-screen-remote.png");
background-repeat: no-repeat;
background-size: cover;
}
.local-stream {
background-image: url("sample-img/video-screen-local.png");
background-repeat: no-repeat;
}
.conversation .media.nested .local {
background-image: url("sample-img/video-screen-local.png");
.local-stream.local:not(.local-stream-audio) {
background-size: cover;
}
.incoming-call-action-group .btn-group-chevron,
.incoming-call-action-group .btn-group {
/* Prevent box overflow due to long string */
max-width: 120px;
}
.conversation .media.nested .remote {
/* Height of obsolute box covers media control buttons. UI showcase only.
* When tokbox inserts the markup into the page the problem goes away */
bottom: auto;
}

View File

@ -44,14 +44,20 @@
}
);
// Local mocks
var mockClient = {
requestCallUrl: noop,
requestCallUrlInfo: noop
};
var mockConversationModel = new loop.shared.models.ConversationModel({}, {sdk: {}});
var mockSDK = {};
var mockConversationModel = new loop.shared.models.ConversationModel({}, {
sdk: mockSDK
});
mockConversationModel.startSession = noop;
// Fake notifier
var mockNotifier = {};
var Example = React.createClass({displayName: 'Example',
@ -121,13 +127,23 @@
Section({name: "IncomingCallView"},
Example({summary: "Default", dashed: "true", style: {width: "280px"}},
IncomingCallView({model: mockConversationModel})
React.DOM.div({className: "fx-embedded"},
IncomingCallView({model: mockConversationModel})
)
)
),
Section({name: "IncomingCallView-ActiveState"},
Example({summary: "Default", dashed: "true", style: {width: "280px"}},
React.DOM.div({className: "fx-embedded"},
IncomingCallView({model: mockConversationModel, showDeclineMenu: true})
)
)
),
Section({name: "ConversationToolbar"},
React.DOM.h3(null, "Desktop Conversation Window"),
React.DOM.div({className: "conversation-window"},
React.DOM.h2(null, "Desktop Conversation Window"),
React.DOM.div({className: "fx-embedded override-position"},
Example({summary: "Default (260x265)", dashed: "true"},
ConversationToolbar({video: {enabled: true},
audio: {enabled: true},
@ -148,8 +164,8 @@
)
),
React.DOM.h3(null, "Standalone"),
React.DOM.div({className: "standalone"},
React.DOM.h2(null, "Standalone"),
React.DOM.div({className: "standalone override-position"},
Example({summary: "Default"},
ConversationToolbar({video: {enabled: true},
audio: {enabled: true},
@ -176,7 +192,8 @@
React.DOM.div({className: "standalone"},
StartConversationView({model: mockConversationModel,
client: mockClient,
notifier: mockNotifier})
notifier: mockNotifier,
showCallOptionsMenu: true})
)
)
),
@ -184,26 +201,70 @@
Section({name: "ConversationView"},
Example({summary: "Desktop conversation window", dashed: "true",
style: {width: "260px", height: "265px"}},
React.DOM.div({className: "conversation-window"},
ConversationView({sdk: {},
React.DOM.div({className: "fx-embedded"},
ConversationView({sdk: mockSDK,
model: mockConversationModel,
video: {enabled: true},
audio: {enabled: true}})
)
),
Example({summary: "Desktop conversation window large", dashed: "true"},
React.DOM.div({className: "breakpoint", 'data-breakpoint-width': "800px",
'data-breakpoint-height': "600px"},
React.DOM.div({className: "fx-embedded"},
ConversationView({sdk: mockSDK,
video: {enabled: true},
audio: {enabled: true},
model: mockConversationModel})
)
)
),
Example({summary: "Desktop conversation window local audio stream",
dashed: "true", style: {width: "260px", height: "265px"}},
React.DOM.div({className: "fx-embedded"},
ConversationView({sdk: mockSDK,
video: {enabled: false},
audio: {enabled: true},
model: mockConversationModel})
)
),
Example({summary: "Standalone version"},
React.DOM.div({className: "standalone"},
ConversationView({sdk: {},
model: mockConversationModel,
ConversationView({sdk: mockSDK,
video: {enabled: true},
audio: {enabled: true}})
audio: {enabled: true},
model: mockConversationModel})
)
)
),
Section({name: "ConversationView-640"},
Example({summary: "640px breakpoint for conversation view"},
React.DOM.div({className: "breakpoint",
style: {"text-align":"center"},
'data-breakpoint-width': "400px",
'data-breakpoint-height': "780px"},
React.DOM.div({className: "standalone"},
ConversationView({sdk: mockSDK,
video: {enabled: true},
audio: {enabled: true},
model: mockConversationModel})
)
)
)
),
Section({name: "ConversationView-LocalAudio"},
Example({summary: "Local stream is audio only"},
React.DOM.div({className: "standalone"},
ConversationView({sdk: mockSDK,
video: {enabled: false},
audio: {enabled: true},
model: mockConversationModel})
)
),
Example({summary: "Default"},
ConversationView({sdk: {},
model: mockConversationModel,
video: {enabled: true},
audio: {enabled: true}})
)
),
@ -230,15 +291,77 @@
Example({summary: "Non-Firefox User"},
CallUrlExpiredView({helper: {isFirefox: returnFalse}})
)
),
Section({name: "AlertMessages"},
Example({summary: "Various alerts"},
React.DOM.div({className: "alert alert-warning"},
React.DOM.button({className: "close"}),
React.DOM.p({className: "message"},
"The person you were calling has ended the conversation."
)
),
React.DOM.br(null),
React.DOM.div({className: "alert alert-error"},
React.DOM.button({className: "close"}),
React.DOM.p({className: "message"},
"The person you were calling has ended the conversation."
)
)
)
)
)
);
}
});
/**
* Render components that have different styles across
* CSS media rules in their own iframe to mimic the viewport
* */
function _renderComponentsInIframes() {
var parents = document.querySelectorAll('.breakpoint');
[].forEach.call(parents, appendChildInIframe);
/**
* Extracts the component from the DOM and appends in the an iframe
*
* @type {HTMLElement} parent - Parent DOM node of a component & iframe
* */
function appendChildInIframe(parent) {
var styles = document.querySelector('head').children;
var component = parent.children[0];
var iframe = document.createElement('iframe');
var width = parent.dataset.breakpointWidth;
var height = parent.dataset.breakpointHeight;
iframe.style.width = width;
iframe.style.height = height;
parent.appendChild(iframe);
iframe.src = "about:blank";
// Workaround for bug 297685
iframe.onload = function () {
var iframeHead = iframe.contentDocument.querySelector('head');
iframe.contentDocument.documentElement.querySelector('body')
.appendChild(component);
[].forEach.call(styles, function(style) {
iframeHead.appendChild(style.cloneNode(true));
});
};
}
}
window.addEventListener("DOMContentLoaded", function() {
var body = document.body;
body.className = loop.shared.utils.getTargetPlatform();
React.renderComponent(App(null), document.body);
React.renderComponent(App(null), body);
_renderComponentsInIframes();
});
})();

View File

@ -44,14 +44,20 @@
}
);
// Local mocks
var mockClient = {
requestCallUrl: noop,
requestCallUrlInfo: noop
};
var mockConversationModel = new loop.shared.models.ConversationModel({}, {sdk: {}});
var mockSDK = {};
var mockConversationModel = new loop.shared.models.ConversationModel({}, {
sdk: mockSDK
});
mockConversationModel.startSession = noop;
// Fake notifier
var mockNotifier = {};
var Example = React.createClass({
@ -121,13 +127,23 @@
<Section name="IncomingCallView">
<Example summary="Default" dashed="true" style={{width: "280px"}}>
<IncomingCallView model={mockConversationModel} />
<div className="fx-embedded">
<IncomingCallView model={mockConversationModel} />
</div>
</Example>
</Section>
<Section name="IncomingCallView-ActiveState">
<Example summary="Default" dashed="true" style={{width: "280px"}}>
<div className="fx-embedded" >
<IncomingCallView model={mockConversationModel} showDeclineMenu={true} />
</div>
</Example>
</Section>
<Section name="ConversationToolbar">
<h3>Desktop Conversation Window</h3>
<div className="conversation-window">
<h2>Desktop Conversation Window</h2>
<div className="fx-embedded override-position">
<Example summary="Default (260x265)" dashed="true">
<ConversationToolbar video={{enabled: true}}
audio={{enabled: true}}
@ -148,8 +164,8 @@
</Example>
</div>
<h3>Standalone</h3>
<div className="standalone">
<h2>Standalone</h2>
<div className="standalone override-position">
<Example summary="Default">
<ConversationToolbar video={{enabled: true}}
audio={{enabled: true}}
@ -176,7 +192,8 @@
<div className="standalone">
<StartConversationView model={mockConversationModel}
client={mockClient}
notifier={mockNotifier} />
notifier={mockNotifier}
showCallOptionsMenu={true} />
</div>
</Example>
</Section>
@ -184,26 +201,70 @@
<Section name="ConversationView">
<Example summary="Desktop conversation window" dashed="true"
style={{width: "260px", height: "265px"}}>
<div className="conversation-window">
<ConversationView sdk={{}}
<div className="fx-embedded">
<ConversationView sdk={mockSDK}
model={mockConversationModel}
video={{enabled: true}}
audio={{enabled: true}} />
</div>
</Example>
<Example summary="Desktop conversation window large" dashed="true">
<div className="breakpoint" data-breakpoint-width="800px"
data-breakpoint-height="600px">
<div className="fx-embedded">
<ConversationView sdk={mockSDK}
video={{enabled: true}}
audio={{enabled: true}}
model={mockConversationModel} />
</div>
</div>
</Example>
<Example summary="Desktop conversation window local audio stream"
dashed="true" style={{width: "260px", height: "265px"}}>
<div className="fx-embedded">
<ConversationView sdk={mockSDK}
video={{enabled: false}}
audio={{enabled: true}}
model={mockConversationModel} />
</div>
</Example>
<Example summary="Standalone version">
<div className="standalone">
<ConversationView sdk={{}}
model={mockConversationModel}
<ConversationView sdk={mockSDK}
video={{enabled: true}}
audio={{enabled: true}} />
audio={{enabled: true}}
model={mockConversationModel} />
</div>
</Example>
<Example summary="Default">
<ConversationView sdk={{}}
model={mockConversationModel}
video={{enabled: true}}
audio={{enabled: true}} />
</Section>
<Section name="ConversationView-640">
<Example summary="640px breakpoint for conversation view">
<div className="breakpoint"
style={{"text-align":"center"}}
data-breakpoint-width="400px"
data-breakpoint-height="780px">
<div className="standalone">
<ConversationView sdk={mockSDK}
video={{enabled: true}}
audio={{enabled: true}}
model={mockConversationModel} />
</div>
</div>
</Example>
</Section>
<Section name="ConversationView-LocalAudio">
<Example summary="Local stream is audio only">
<div className="standalone">
<ConversationView sdk={mockSDK}
video={{enabled: false}}
audio={{enabled: true}}
model={mockConversationModel} />
</div>
</Example>
</Section>
@ -231,14 +292,76 @@
<CallUrlExpiredView helper={{isFirefox: returnFalse}} />
</Example>
</Section>
<Section name="AlertMessages">
<Example summary="Various alerts">
<div className="alert alert-warning">
<button className="close"></button>
<p className="message">
The person you were calling has ended the conversation.
</p>
</div>
<br />
<div className="alert alert-error">
<button className="close"></button>
<p className="message">
The person you were calling has ended the conversation.
</p>
</div>
</Example>
</Section>
</ShowCase>
);
}
});
/**
* Render components that have different styles across
* CSS media rules in their own iframe to mimic the viewport
* */
function _renderComponentsInIframes() {
var parents = document.querySelectorAll('.breakpoint');
[].forEach.call(parents, appendChildInIframe);
/**
* Extracts the component from the DOM and appends in the an iframe
*
* @type {HTMLElement} parent - Parent DOM node of a component & iframe
* */
function appendChildInIframe(parent) {
var styles = document.querySelector('head').children;
var component = parent.children[0];
var iframe = document.createElement('iframe');
var width = parent.dataset.breakpointWidth;
var height = parent.dataset.breakpointHeight;
iframe.style.width = width;
iframe.style.height = height;
parent.appendChild(iframe);
iframe.src = "about:blank";
// Workaround for bug 297685
iframe.onload = function () {
var iframeHead = iframe.contentDocument.querySelector('head');
iframe.contentDocument.documentElement.querySelector('body')
.appendChild(component);
[].forEach.call(styles, function(style) {
iframeHead.appendChild(style.cloneNode(true));
});
};
}
}
window.addEventListener("DOMContentLoaded", function() {
var body = document.body;
body.className = loop.shared.utils.getTargetPlatform();
React.renderComponent(<App />, document.body);
React.renderComponent(<App />, body);
_renderComponentsInIframes();
});
})();

View File

@ -2200,22 +2200,54 @@ function truncateString(str, maxLength) {
function parseAttributeValues(attr, doc) {
attr = attr.trim();
// Handle bad user inputs by appending a " or ' if it fails to parse without them.
let el = DOMParser.parseFromString("<div " + attr + "></div>", "text/html").body.childNodes[0] ||
DOMParser.parseFromString("<div " + attr + "\"></div>", "text/html").body.childNodes[0] ||
DOMParser.parseFromString("<div " + attr + "'></div>", "text/html").body.childNodes[0];
// Prepare other versions of the string to be parsed by appending a " or '
// and using those if the first one fails to parse without these characters
let stringsToParse = [
"<div " + attr + "></div>",
"<div " + attr + "\"></div>",
"<div " + attr + "'></div>"
];
// Try to parse as XML, this way, if the string is wellformed, this will
// preserve the case.
let parsedAttributes = [];
for (let str of stringsToParse) {
let parsed = DOMParser.parseFromString(str, "text/xml");
// The XML parser generates a valid XML document even when parsing errors
// occur, in which case the document contains a <parsererror> node, so check
// that the document contains our expected DIV
if (parsed.childNodes[0].localName === "div") {
for (let {name, value} of parsed.childNodes[0].attributes) {
parsedAttributes.push({ name, value });
}
break;
}
}
// If the XML parsing failed, parse as HTML to get malformed attributes
if (parsedAttributes.length === 0) {
for (let str of stringsToParse) {
let parsed = DOMParser.parseFromString(str, "text/html");
// Check that the parsed document does contain the expected DIV as a child
// of <body>
if (parsed.body.childNodes[0]) {
for (let {name, value} of parsed.body.childNodes[0].attributes) {
parsedAttributes.push({ name, value });
}
break;
}
}
}
let div = doc.createElement("div");
let attributes = [];
for (let attribute of el.attributes) {
for (let {name, value} of parsedAttributes) {
// Try to set on an element in the document, throws exception on bad input.
// Prevents InvalidCharacterError - "String contains an invalid character".
try {
div.setAttribute(attribute.name, attribute.value);
attributes.push({
name: attribute.name,
value: attribute.value
});
div.setAttribute(name, value);
attributes.push({ name, value });
}
catch(e) { }
}

View File

@ -12,6 +12,7 @@ support-files =
doc_markup_pagesize_01.html
doc_markup_pagesize_02.html
doc_markup_search.html
doc_markup_svg_attributes.html
doc_markup_toggle.html
doc_markup_tooltip.png
head.js
@ -80,6 +81,7 @@ skip-if = e10s # Bug 1036421 - Tag editing isn't remote-safe
[browser_markupview_tag_edit_06.js]
[browser_markupview_tag_edit_07.js]
[browser_markupview_tag_edit_08.js]
[browser_markupview_tag_edit_09.js]
[browser_markupview_textcontent_edit_01.js]
[browser_markupview_toggle_01.js]
[browser_markupview_toggle_02.js]

View File

@ -0,0 +1,74 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that editing a mixed-case attribute preserves the case
const TEST_URL = TEST_URL_ROOT + "doc_markup_svg_attributes.html";
let test = asyncTest(function*() {
let {inspector} = yield addTab(TEST_URL).then(openInspector);
yield inspector.markup.expandAll();
yield selectNode("svg", inspector);
yield testWellformedMixedCase(inspector);
yield testMalformedMixedCase(inspector);
});
function* testWellformedMixedCase(inspector) {
info("Modifying a mixed-case attribute, " +
"expecting the attribute's case to be preserved");
info("Listening to markup mutations");
let onMutated = inspector.once("markupmutation");
info("Focusing the viewBox attribute editor");
let {editor} = yield getContainerForSelector("svg", inspector);
let attr = editor.attrs["viewBox"].querySelector(".editable");
attr.focus();
EventUtils.sendKey("return", inspector.panelWin);
info("Editing the attribute value and waiting for the mutation event");
let input = inplaceEditor(attr).input;
input.value = "viewBox=\"0 0 1 1\"";
EventUtils.sendKey("return", inspector.panelWin);
yield onMutated;
assertAttributes("svg", {
"viewBox": "0 0 1 1",
"width": "200",
"height": "200"
});
}
function* testMalformedMixedCase(inspector) {
info("Modifying a mixed-case attribute, making sure to generate a parsing" +
"error, and expecting the attribute's case to NOT be preserved");
// See /browser/devtools/markupview/markup-view.js:parseAttributeValues
// When attributes are malformed, they cannot be parsed with the XML parser
// and so we fall back to the HTML parser which lowercases attributes.
info("Listening to markup mutations");
let onMutated = inspector.once("markupmutation");
info("Focusing the viewBox attribute editor");
let {editor} = yield getContainerForSelector("svg", inspector);
let attr = editor.attrs["viewBox"].querySelector(".editable");
attr.focus();
EventUtils.sendKey("return", inspector.panelWin);
info("Editing the attribute value and waiting for the mutation event");
let input = inplaceEditor(attr).input;
input.value = "viewBox=\"<>\"";
EventUtils.sendKey("return", inspector.panelWin);
yield onMutated;
assertAttributes("svg", {
"viewbox": "<>",
"width": "200",
"height": "200"
});
}

View File

@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<body>
<svg viewBox="0 0 2 2" width=200 height=200>
<circle cx=1 cy=1 r=1 fill=lime />
</svg>
</body>
</html>

View File

@ -1,5 +1,3 @@
/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/*
* Copyright 2013 Mozilla Foundation
*
@ -429,13 +427,6 @@ ChromeActions.prototype = {
.getService(Ci.nsIClipboardHelper);
clipboard.copyString(data);
},
unsafeSetClipboard: function (data) {
if (typeof data !== 'string') {
return;
}
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
clipboard.copyString(data);
},
endActivation: function () {
if (ActivationQueue.currentNonActive === this) {
ActivationQueue.activateNext();
@ -552,8 +543,7 @@ RequestListener.prototype.receive = function(event) {
}
if (sync) {
var response = actions[action].call(this.actions, data);
var detail = event.detail;
detail.response = response;
event.detail.response = response;
} else {
var response;
if (event.detail.callback) {
@ -680,35 +670,29 @@ var ActivationQueue = {
function activateShumwayScripts(window, preview) {
function loadScripts(scripts, callback) {
function scriptLoaded() {
leftToLoad--;
if (leftToLoad === 0) {
function loadScript(i) {
if (i >= scripts.length) {
callback();
return;
}
}
var leftToLoad = scripts.length;
var document = window.document.wrappedJSObject;
var head = document.getElementsByTagName('head')[0];
for (var i = 0; i < scripts.length; i++) {
var script = document.createElement('script');
script.type = "text/javascript";
script.src = scripts[i];
script.onload = scriptLoaded;
script.onload = function () {
loadScript(i + 1);
};
head.appendChild(script);
}
var document = window.document.wrappedJSObject;
var head = document.getElementsByTagName('head')[0];
loadScript(0);
}
function initScripts() {
if (preview) {
loadScripts(['resource://shumway/web/preview.js'], function () {
window.wrappedJSObject.runSniffer();
});
} else {
loadScripts(['resource://shumway/shumway.js',
'resource://shumway/web/avm-sandbox.js'], function () {
window.wrappedJSObject.runViewer();
});
}
loadScripts(['resource://shumway/shumway.gfx.js',
'resource://shumway/web/viewer.js'], function () {
window.wrappedJSObject.runViewer();
});
}
window.wrappedJSObject.SHUMWAY_ROOT = "resource://shumway/";

View File

@ -1,5 +1,3 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2013 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");

View File

@ -0,0 +1,127 @@
/*
precision mediump float;
varying vec4 vColor;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
out vec4 FragmentColor;
uniform float offset[5] = float[]( 0.0, 1.0, 2.0, 3.0, 4.0 );
uniform float weight[5] = float[]( 0.2270270270, 0.1945945946, 0.1216216216,
0.0540540541, 0.0162162162 );
void main(void)
{
FragmentColor = texture2D( uSampler, vec2(vCoordinate) * weight[0];
for (int i=1; i<5; i++) {
FragmentColor +=
texture2D( uSampler, ( vec2(gl_FragCoord)+vec2(0.0, offset[i]) )/1024.0 )
* weight[i];
FragmentColor +=
texture2D( uSampler, ( vec2(gl_FragCoord)-vec2(0.0, offset[i]) )/1024.0 )
* weight[i];
}
}
*/
/*
precision mediump float;
varying vec4 vColor;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
void main() {
const int sampleRadius = 16;
const int samples = sampleRadius * 2 + 1;
float dy = 1.0 / 512.0;
vec4 sample = vec4(0, 0, 0, 0);
for (int i = -sampleRadius; i <= sampleRadius; i++) {
sample += texture2D(uSampler, vCoordinate + vec2(0, float(i) * dy));
}
gl_FragColor = sample / float(samples);
// gl_FragColor = texture2D(uSampler, vCoordinate);
}
*/
precision mediump float;
varying vec4 vColor;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
void main() {
vec4 sum = vec4(0.0);
float blur = 1.0 / 512.0 * 1.0;
sum += texture2D(uSampler, vec2(vCoordinate.x - 4.0 * blur, vCoordinate.y)) * 0.05;
sum += texture2D(uSampler, vec2(vCoordinate.x - 3.0 * blur, vCoordinate.y)) * 0.09;
sum += texture2D(uSampler, vec2(vCoordinate.x - 2.0 * blur, vCoordinate.y)) * 0.12;
sum += texture2D(uSampler, vec2(vCoordinate.x - blur, vCoordinate.y)) * 0.15;
sum += texture2D(uSampler, vec2(vCoordinate.x, vCoordinate.y)) * 0.16;
sum += texture2D(uSampler, vec2(vCoordinate.x + blur, vCoordinate.y)) * 0.15;
sum += texture2D(uSampler, vec2(vCoordinate.x + 2.0 * blur, vCoordinate.y)) * 0.12;
sum += texture2D(uSampler, vec2(vCoordinate.x + 3.0 * blur, vCoordinate.y)) * 0.09;
sum += texture2D(uSampler, vec2(vCoordinate.x + 4.0 * blur, vCoordinate.y)) * 0.05;
gl_FragColor = sum;
// gl_FragColor = texture2D(uSampler, vCoordinate);
}
/*
precision mediump float;
varying vec4 vColor;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
void main() {
vec4 sum = vec4(0.0);
float blur = 0.1;
sum += texture2D(uSampler, vec2(vCoordinate.x - 4.0 * blur, vCoordinate.y)) * 0.05;
sum += texture2D(uSampler, vec2(vCoordinate.x - 3.0 * blur, vCoordinate.y)) * 0.09;
sum += texture2D(uSampler, vec2(vCoordinate.x - 2.0 * blur, vCoordinate.y)) * 0.12;
sum += texture2D(uSampler, vec2(vCoordinate.x - blur, vCoordinate.y)) * 0.15;
sum += texture2D(uSampler, vec2(vCoordinate.x, vCoordinate.y)) * 0.16;
sum += texture2D(uSampler, vec2(vCoordinate.x + blur, vCoordinate.y)) * 0.15;
sum += texture2D(uSampler, vec2(vCoordinate.x + 2.0 * blur, vCoordinate.y)) * 0.12;
sum += texture2D(uSampler, vec2(vCoordinate.x + 3.0 * blur, vCoordinate.y)) * 0.09;
sum += texture2D(uSampler, vec2(vCoordinate.x + 4.0 * blur, vCoordinate.y)) * 0.05;
gl_FragColor = sum;
// gl_FragColor = texture2D(uSampler, vCoordinate);
}
*/
/*
precision mediump float;
varying vec4 vColor;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
void main() {
gl_FragColor = texture2D(uSampler, vCoordinate);
}
*/
/*
precision mediump float;
varying vec2 vCoordinate;
varying float vColor;
uniform float blur;
uniform sampler2D uSampler;
void main(void) {
vec4 sum = vec4(0.0);
sum += texture2D(uSampler, vec2(vCoordinate.x - 4.0*blur, vCoordinate.y)) * 0.05;
sum += texture2D(uSampler, vec2(vCoordinate.x - 3.0*blur, vCoordinate.y)) * 0.09;
sum += texture2D(uSampler, vec2(vCoordinate.x - 2.0*blur, vCoordinate.y)) * 0.12;
sum += texture2D(uSampler, vec2(vCoordinate.x - blur, vCoordinate.y)) * 0.15;
sum += texture2D(uSampler, vec2(vCoordinate.x, vCoordinate.y)) * 0.16;
sum += texture2D(uSampler, vec2(vCoordinate.x + blur, vCoordinate.y)) * 0.15;
sum += texture2D(uSampler, vec2(vCoordinate.x + 2.0*blur, vCoordinate.y)) * 0.12;
sum += texture2D(uSampler, vec2(vCoordinate.x + 3.0*blur, vCoordinate.y)) * 0.09;
sum += texture2D(uSampler, vec2(vCoordinate.x + 4.0*blur, vCoordinate.y)) * 0.05;
gl_FragColor = sum;
}
*/

View File

@ -0,0 +1,16 @@
precision mediump float;
varying vec4 vColor;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
void main() {
const int sampleRadius = 8;
const int samples = sampleRadius * 2 + 1;
float dx = 0.01;
vec4 sample = vec4(0, 0, 0, 0);
for (int i = -sampleRadius; i <= sampleRadius; i++) {
sample += texture2D(uSampler, vCoordinate + vec2(0, float(i) * dy));
}
gl_FragColor = sample / float(samples);
}

View File

@ -0,0 +1,19 @@
uniform vec2 uResolution;
uniform mat3 uTransformMatrix;
uniform float uZ;
attribute vec2 aPosition;
attribute vec4 aColor;
attribute vec2 aCoordinate;
varying vec4 vColor;
varying vec2 vCoordinate;
void main() {
vec2 position = ((uTransformMatrix * vec3(aPosition, 1.0)).xy / uResolution) * 2.0 - 1.0;
position *= vec2(1.0, -1.0);
// position *= vec2(40.0, -4.0);
gl_Position = vec4(vec3(position, uZ), 1.0);
vColor = aColor;
vCoordinate = aCoordinate;
}

View File

@ -0,0 +1,46 @@
precision mediump float;
varying vec4 vColor;
uniform mat4 uColorMatrix;
uniform vec4 uColorVector;
uniform sampler2D uSampler[8];
varying vec2 vCoordinate;
varying float vKind;
varying float vSampler;
void main() {
vec4 color;
int kind = int(floor(vKind + 0.5));
if (kind == 0) {
color = vColor;
} else if (kind == 1 || kind == 2) {
int sampler = int(floor(vSampler + 0.5));
if (sampler == 0) {
color = vColor * texture2D(uSampler[0], vCoordinate);
} else if (sampler == 1) {
color = vColor * texture2D(uSampler[1], vCoordinate);
} else if (sampler == 2) {
color = vColor * texture2D(uSampler[2], vCoordinate);
} else if (sampler == 3) {
color = vColor * texture2D(uSampler[3], vCoordinate);
} else if (sampler == 4) {
color = vColor * texture2D(uSampler[4], vCoordinate);
} else if (sampler == 5) {
color = vColor * texture2D(uSampler[5], vCoordinate);
} else if (sampler == 6) {
color = vColor * texture2D(uSampler[6], vCoordinate);
} else if (sampler == 7) {
color = vColor * texture2D(uSampler[7], vCoordinate);
}
if (kind == 2) {
color = color * uColorMatrix + uColorVector;
}
} else {
color = vec4(1.0, 0.0, 0.0, 1.0);
}
// color.rgb *= color.a;
if (color.a < 0.01) {
discard;
}
gl_FragColor = color;
}

View File

@ -0,0 +1,22 @@
uniform vec2 uResolution;
uniform mat3 uTransformMatrix;
uniform mat4 uTransformMatrix3D;
attribute vec4 aPosition;
attribute vec4 aColor;
attribute vec2 aCoordinate;
attribute float aKind;
attribute float aSampler;
varying vec4 vColor;
varying vec2 vCoordinate;
varying float vKind;
varying float vSampler;
void main() {
gl_Position = uTransformMatrix3D * aPosition;
vColor = aColor;
vCoordinate = aCoordinate;
vKind = aKind;
vSampler = aSampler;
}

View File

@ -0,0 +1,18 @@
precision mediump float;
uniform vec4 uColor;
varying vec2 vTextureCoordinate;
void main() {
gl_FragColor = uColor;
gl_FragColor = vec4(vTextureCoordinate.x, vTextureCoordinate.y, 0, 0.5);
float u = vTextureCoordinate.x;
float v = vTextureCoordinate.y;
float r = u * u - v;
if (r < 0.0) {
gl_FragColor = vec4(1, 0, 0, 1);
} else {
gl_FragColor = vec4(1, 0, 0, 0.2);
}
}

View File

@ -0,0 +1,7 @@
precision mediump float;
varying vec4 vColor;
void main() {
gl_FragColor = vColor;
}

View File

@ -0,0 +1,15 @@
uniform vec2 uResolution;
uniform mat3 uTransformMatrix;
uniform float uZ;
attribute vec2 aPosition;
attribute vec4 aColor;
varying vec4 vColor;
void main() {
vec2 position = ((uTransformMatrix * vec3(aPosition, 1.0)).xy / uResolution) * 2.0 - 1.0;
position *= vec2(1.0, -1.0);
gl_Position = vec4(vec3(position, uZ), 1.0);
vColor = aColor;
}

View File

@ -0,0 +1,12 @@
precision mediump float;
varying vec4 vColor;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
void main() {
// gl_FragColor = vColor;
// gl_FragColor = vec4(vTextureCoordinate.x, vTextureCoordinate.y, 0, 0.5);
// gl_FragColor = gl_FragColor; // + texture2D(uSampler, vCoordinate);
gl_FragColor = texture2D(uSampler, vCoordinate);
}

View File

@ -0,0 +1,19 @@
uniform vec2 uResolution;
uniform mat3 uTransformMatrix;
uniform float uZ;
attribute vec2 aPosition;
attribute vec4 aColor;
attribute vec2 aCoordinate;
varying vec4 vColor;
varying vec2 vCoordinate;
void main() {
vec2 position = ((uTransformMatrix * vec3(aPosition, 1.0)).xy / uResolution) * 2.0 - 1.0;
position *= vec2(1.0, -1.0);
// position *= vec2(40.0, -4.0);
gl_Position = vec4(vec3(position, uZ), 1.0);
vColor = aColor;
vCoordinate = aCoordinate;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,2 @@
0.8.271
2717b0c
0.9.2697
25bfcc9

View File

@ -1,219 +0,0 @@
/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/*
* Copyright 2013 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Extenstion communication object
var FirefoxCom = (function FirefoxComClosure() {
return {
/**
* Creates an event that the extension is listening for and will
* synchronously respond to.
* NOTE: It is reccomended to use request() instead since one day we may not
* be able to synchronously reply.
* @param {String} action The action to trigger.
* @param {String} data Optional data to send.
* @return {*} The response.
*/
requestSync: function(action, data) {
var request = document.createTextNode('');
document.documentElement.appendChild(request);
var sender = document.createEvent('CustomEvent');
sender.initCustomEvent('shumway.message', true, false,
{action: action, data: data, sync: true});
request.dispatchEvent(sender);
var response = sender.detail.response;
document.documentElement.removeChild(request);
return response;
},
/**
* Creates an event that the extension is listening for and will
* asynchronously respond by calling the callback.
* @param {String} action The action to trigger.
* @param {String} data Optional data to send.
* @param {Function} callback Optional response callback that will be called
* with one data argument.
*/
request: function(action, data, callback) {
var request = document.createTextNode('');
request.setUserData('action', action, null);
request.setUserData('data', data, null);
request.setUserData('sync', false, null);
if (callback) {
request.setUserData('callback', callback, null);
document.addEventListener('shumway.response', function listener(event) {
var node = event.target,
response = event.detail.response;
document.documentElement.removeChild(node);
document.removeEventListener('shumway.response', listener, false);
return callback(response);
}, false);
}
document.documentElement.appendChild(request);
var sender = document.createEvent('CustomEvent');
sender.initCustomEvent('shumway.message', true, false,
{action: action, data: data, sync: false});
return request.dispatchEvent(sender);
}
};
})();
function fallback() {
FirefoxCom.requestSync('fallback', null)
}
var BYTES_TO_LOAD = 32768;
var BYTES_TO_PARSE = 32768;
function runSniffer() {
var flashParams = JSON.parse(FirefoxCom.requestSync('getPluginParams', null));
document.head.getElementsByTagName('base')[0].href = flashParams.baseUrl;
movieUrl = flashParams.url;
document.getElementById('playbutton').addEventListener('click', function() {
switchToFullMode();
});
document.getElementById('fullmode').addEventListener('click', function() {
switchToFullMode();
return false;
});
document.getElementById('fallback').addEventListener('click', function() {
fallback();
return false;
});
FirefoxCom.requestSync('loadFile', {url: movieUrl, sessionId: 0, limit: BYTES_TO_LOAD});
}
var subscription, movieUrl, buffers = [];;
addEventListener("message", function handlerMessage(e) {
var args = e.data;
switch (args.callback) {
case "loadFile":
if (args.sessionId != 0) {
return;
}
switch (args.topic) {
case "progress":
buffers.push(args.array);
break;
case "error":
console.error('Unable to download ' + movieUrl + ': ' + args.error);
break;
case "close":
parseSwf();
break;
}
break;
}
}, true);
function inflateData(bytes, outputLength) {
verifyDeflateHeader(bytes);
var stream = new Stream(bytes, 2);
var output = {
data: new Uint8Array(outputLength),
available: 0,
completed: false
};
var state = {};
// inflate while we can
try {
do {
inflateBlock(stream, output, state);
} while (!output.completed && stream.pos < stream.end
&& output.available < outputLength);
} catch (e) {
console.log('inflate aborted: ' + e);
}
return new Stream(output.data, 0, Math.min(output.available, outputLength));
}
function parseSwf() {
var sum = 0;
for (var i = 0; i < buffers.length; i++)
sum += buffers[i].length;
var data = new Uint8Array(sum), j = 0;
for (var i = 0; i < buffers.length; i++) {
data.set(buffers[i], j); j += buffers[i].length;
}
var backgroundColor;
try {
var magic1 = data[0];
var magic2 = data[1];
var magic3 = data[2];
if ((magic1 !== 70 && magic1 !== 67) || magic2 !== 87 || magic3 !== 83)
throw new Error('unsupported file format');
var compressed = magic1 === 67;
var stream = compressed ? inflateData(data.subarray(8), BYTES_TO_PARSE) :
new Stream(data, 8, data.length - 8);
var bytes = stream.bytes;
var SWF_TAG_CODE_SET_BACKGROUND_COLOR = 9;
var PREFETCH_SIZE = 17 /* RECT */ +
4 /* Frames rate and count */;;
stream.ensure(PREFETCH_SIZE);
var rectFieldSize = bytes[stream.pos] >> 3;
stream.pos += ((5 + 4 * rectFieldSize + 7) >> 3) + 4; // skipping other header fields
// for now just sniffing background color
while (stream.pos < stream.end &&
!backgroundColor) {
stream.ensure(2);
var tagCodeAndLength = stream.getUint16(stream.pos, true);
stream.pos += 2;
var tagCode = tagCodeAndLength >> 6;
var length = tagCodeAndLength & 0x3F;
if (length == 0x3F) {
stream.ensure(4);
length = stream.getInt32(stream.pos, true);
stream.pos += 4;
if (length < 0) throw new Error('invalid length');
}
stream.ensure(length);
switch (tagCode) {
case SWF_TAG_CODE_SET_BACKGROUND_COLOR:
backgroundColor = 'rgb(' + bytes[stream.pos] + ', ' +
bytes[stream.pos + 1] + ', ' +
bytes[stream.pos + 2] + ')';
break;
}
stream.pos += length;
}
} catch (e) {
console.log('parsing aborted: ' + e);
}
if (backgroundColor) {
document.body.style.backgroundColor = backgroundColor;
}
}
document.addEventListener('keydown', function (e) {
if (e.keyCode == 119 && e.ctrlKey) { // Ctrl+F8
window.location.replace("data:application/x-moz-playpreview;,application/x-shockwave-flash,full,paused=true");
}
}, false);
function switchToFullMode() {
window.location.replace("data:application/x-moz-playpreview;,application/x-shockwave-flash,full");
}

View File

@ -1,130 +0,0 @@
<!DOCTYPE html>
<!--
Copyright 2013 Mozilla Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<base href=""/>
<script src="../lib/DataView.js/DataView.js"></script>
<!-- Load SWF Dependencies -->
<script src="../flash/util.js"></script>
<script src="../swf/inflate.js"></script>
<script src="../swf/stream.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
background-color: rgba(128, 128, 128, 0.5);
font-family: sans-serif;
}
#screen {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
}
#container {
position: fixed;
top: 50%; left: 0; right: 0; bottom: 50%;
}
#content {
margin-top: -64px;
text-align: center;
}
#playbutton {
background-color: transparent;
background-image: url(chrome://global/skin/media/videoClickToPlayButton.svg);
border: none 0px;
width: 64px;
height: 64px;
display: block;
margin-left: auto;
margin-right: auto;
cursor: pointer;
margin-bottom: 10px;
}
#playbutton > span {
overflow: hidden;
width: 0px; height: 0px;
display: inline-block;
}
@media screen and (max-width: 160px) , screen and (max-height: 160px) {
#playbutton {
display: none;
}
#content {
margin-top: -6px;
}
}
#screen {
transition: opacity 0.5s linear;
-webkit-transition: opacity 0.5s linear;
opacity: 0.1;
}
#screen:hover {
opacity: 1.0;
}
.shumwayButton {
background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #f9f9f9), color-stop(1, #e9e9e9) );
background:-moz-linear-gradient( center top, #f9f9f9 5%, #e9e9e9 100% );
background-color:#f9f9f9;
-moz-border-radius:4px;
-webkit-border-radius:4px;
border-radius:4px;
border:1px solid #a0a0a0;
display:inline-block;
color:#666666;
font-family:arial;
font-size:11px;
font-weight:bold;
padding:6px 11px;
text-decoration:none;
text-shadow:1px 1px 0px #ffffff;
width: 70px;
cursor: pointer;
}
.shumwayButton:hover {
background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #e9e9e9), color-stop(1, #f9f9f9) );
background:-moz-linear-gradient( center top, #e9e9e9 5%, #f9f9f9 100% );
background-color:#e9e9e9;
}
.shumwayButton:active {
position:relative;
top:1px;
}
</style>
</head>
<body>
<div id='screen'>
<div id='container'>
<div id='content'>
<button id='playbutton'><span>Play</span></button>
<div id='toolbar'>
<a class="shumwayButton" id="fullmode">Shumway</a>
<a class="shumwayButton" id="fallback">Flash</a>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -31,7 +31,7 @@ limitations under the License.
background-color: transparent;
}
#viewer {
#stageContainer {
position:fixed !important;
left:0;top:0;bottom:0;right:0;
overflow: hidden;
@ -80,6 +80,12 @@ limitations under the License.
background-color: black;
}
#playerWindow {
position: absolute;
top: 0;
right: 0;
}
@media screen and (max-width: 100px), screen and (max-height: 40px) {
body.started #overlay {
display: none;
@ -89,7 +95,8 @@ limitations under the License.
</head>
<body contextmenu="shumwayMenu">
<div id="viewer"></div>
<iframe id="playerWindow" width="9" height="9" src=""></iframe>
<div id="stageContainer"></div>
<section>
<div id="overlay">
<a id="fallback" href="#">Shumway <span class="icon">&times;</span></a>
@ -97,7 +104,6 @@ limitations under the License.
</div>
<menu type="context" id="shumwayMenu">
<menuitem label="Show URL" id="showURLMenu"></menuitem>
<menuitem label="Copy Profile" id="copyProfileMenu"></menuitem>
<menuitem label="Open in Inspector" id="inspectorMenu"></menuitem>
<menuitem label="Report Problems" id="reportMenu"></menuitem>
<menuitem label="Fallback to Flash" id="fallbackMenu" hidden></menuitem>

View File

@ -1,5 +1,3 @@
/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/*
* Copyright 2013 Mozilla Foundation
*
@ -22,7 +20,7 @@ var FirefoxCom = (function FirefoxComClosure() {
/**
* Creates an event that the extension is listening for and will
* synchronously respond to.
* NOTE: It is reccomended to use request() instead since one day we may not
* NOTE: It is recommended to use request() instead since one day we may not
* be able to synchronously reply.
* @param {String} action The action to trigger.
* @param {String} data Optional data to send.
@ -80,14 +78,30 @@ function fallback() {
FirefoxCom.requestSync('fallback', null)
}
var playerglobalInfo = {
window.print = function(msg) {
console.log(msg);
};
var viewerPlayerglobalInfo = {
abcs: SHUMWAY_ROOT + "playerglobal/playerglobal.abcs",
catalog: SHUMWAY_ROOT + "playerglobal/playerglobal.json"
};
var builtinPath = SHUMWAY_ROOT + "avm2/generated/builtin/builtin.abc";
var avm1Path = SHUMWAY_ROOT + "avm2/generated/avm1lib/avm1lib.abc";
var playerWindow;
var playerWindowLoaded = new Promise(function(resolve) {
var playerWindowIframe = document.getElementById("playerWindow");
playerWindowIframe.addEventListener('load', function () {
playerWindow = playerWindowIframe.contentWindow;
resolve();
});
playerWindowIframe.src = 'resource://shumway/web/viewer.player.html';
});
function runViewer() {
var flashParams = JSON.parse(FirefoxCom.requestSync('getPluginParams', null));
FileLoadingService.setBaseUrl(flashParams.baseUrl);
movieUrl = flashParams.url;
if (!movieUrl) {
@ -110,7 +124,9 @@ function runViewer() {
}).join(',');
}
parseSwf(movieUrl, movieParams, objectParams);
playerWindowLoaded.then(function () {
parseSwf(movieUrl, movieParams, objectParams);
});
if (isOverlay) {
document.getElementById('overlay').className = 'enabled';
@ -134,8 +150,6 @@ function runViewer() {
inspectorMenu.addEventListener('click', showInInspector);
var reportMenu = document.getElementById('reportMenu');
reportMenu.addEventListener('click', reportIssue);
document.getElementById('copyProfileMenu').addEventListener('click', copyProfile);
}
function showURL() {
@ -171,152 +185,86 @@ function reportIssue() {
FirefoxCom.requestSync('reportIssue', JSON.stringify(prunedExceptions));
}
function copyProfile() {
function toArray(v) {
var array = [];
for (var i = 0; i < v.length; i++) {
array.push(v[i]);
}
return array;
}
var profile = {
loops: {counts: toArray($L), lines: $LL},
functions: {counts: toArray($F), lines: $FL},
allocations: {counts: toArray($A), lines: $AL}
};
FirefoxCom.request('unsafeSetClipboard', JSON.stringify(profile));
}
var movieUrl, movieParams, objectParams;
window.addEventListener("message", function handlerMessage(e) {
var args = e.data;
switch (args.callback) {
case "loadFile":
var session = FileLoadingService.sessions[args.sessionId];
if (session) {
session.notify(args);
}
case 'loadFile':
playerWindow.postMessage({
type: "loadFileResponse",
args: args
}, '*');
break;
case 'loadFileRequest':
FirefoxCom.request('loadFile', args.data, null);
break;
case 'reportTelemetry':
FirefoxCom.request('reportTelemetry', args.data, null);
break;
}
}, true);
var TelemetryService = {
reportTelemetry: function (data) {
FirefoxCom.request('reportTelemetry', data, null);
}
};
var easelHost;
var FileLoadingService = {
get baseUrl() { return movieUrl; },
nextSessionId: 1, // 0 - is reserved
sessions: [],
createSession: function () {
var sessionId = this.nextSessionId++;
return this.sessions[sessionId] = {
open: function (request) {
var self = this;
var path = FileLoadingService.resolveUrl(request.url);
console.log('Session #' + sessionId +': loading ' + path);
FirefoxCom.requestSync('loadFile', {url: path, method: request.method,
mimeType: request.mimeType, postData: request.data,
checkPolicyFile: request.checkPolicyFile, sessionId: sessionId});
},
notify: function (args) {
switch (args.topic) {
case "open": this.onopen(); break;
case "close":
this.onclose();
FileLoadingService.sessions[sessionId] = null;
console.log('Session #' + sessionId +': closed');
break;
case "error":
this.onerror && this.onerror(args.error);
break;
case "progress":
console.log('Session #' + sessionId + ': loaded ' + args.loaded + '/' + args.total);
this.onprogress && this.onprogress(args.array, {bytesLoaded: args.loaded, bytesTotal: args.total});
break;
}
}
};
},
setBaseUrl: function (url) {
var a = document.createElement('a');
a.href = url || '#';
a.setAttribute('style', 'display: none;');
document.body.appendChild(a);
FileLoadingService.baseUrl = a.href;
document.body.removeChild(a);
},
resolveUrl: function (url) {
if (url.indexOf('://') >= 0) return url;
var base = FileLoadingService.baseUrl;
base = base.lastIndexOf('/') >= 0 ? base.substring(0, base.lastIndexOf('/') + 1) : '';
if (url.indexOf('/') === 0) {
var m = /^[^:]+:\/\/[^\/]+/.exec(base);
if (m) base = m[0];
}
return base + url;
function processExternalCommand(command) {
switch (command.action) {
case 'isEnabled':
command.result = true;
break;
case 'initJS':
FirefoxCom.initJS(function (functionName, args) {
return easelHost.sendExernalCallback(functionName, args);
});
break;
default:
command.result = FirefoxCom.requestSync('externalCom', command);
break;
}
};
}
function parseSwf(url, movieParams, objectParams) {
var enableVerifier = Shumway.AVM2.Runtime.enableVerifier;
var EXECUTION_MODE = Shumway.AVM2.Runtime.EXECUTION_MODE;
var compilerSettings = JSON.parse(
FirefoxCom.requestSync('getCompilerSettings', null));
enableVerifier.value = compilerSettings.verifier;
// init misc preferences
turboMode.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.turboMode', def: false});
hud.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.hud', def: false});
forceHidpi.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.force_hidpi', def: false});
dummyAnimation.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.dummyMode', def: false});
var turboMode = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.turboMode', def: false});
Shumway.GFX.backend.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.webgl', def: false}) ? 1 : 0;
Shumway.GFX.hud.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.hud', def: false});
//forceHidpi.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.force_hidpi', def: false});
//dummyAnimation.value = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.dummyMode', def: false});
console.log("Compiler settings: " + JSON.stringify(compilerSettings));
console.log("Parsing " + url + "...");
console.info("Compiler settings: " + JSON.stringify(compilerSettings));
console.info("Parsing " + url + "...");
function loaded() {
FirefoxCom.request('endActivation', null);
}
createAVM2(builtinPath, playerglobalInfo, avm1Path,
compilerSettings.sysCompiler ? EXECUTION_MODE.COMPILE : EXECUTION_MODE.INTERPRET,
compilerSettings.appCompiler ? EXECUTION_MODE.COMPILE : EXECUTION_MODE.INTERPRET,
function (avm2) {
console.time("Initialize Renderer");
SWF.embed(url, document, document.getElementById("viewer"), {
url: url,
movieParams: movieParams,
objectParams: objectParams,
onComplete: loaded,
onBeforeFrame: frame
});
});
var easel = createEasel();
easelHost = new Shumway.GFX.Window.WindowEaselHost(easel, playerWindow, window);
easelHost.processExternalCommand = processExternalCommand;
var data = {
type: 'runSwf',
settings: Shumway.Settings.getSettings(),
flashParams: {
compilerSettings: compilerSettings,
movieParams: movieParams,
objectParams: objectParams,
turboMode: turboMode,
url: url,
baseUrl: url
}
};
playerWindow.postMessage(data, '*');
}
var pauseExecution = false;
var initializeFrameControl = true;
function frame(e) {
if (initializeFrameControl) {
// marking that movie is started
document.body.classList.add("started");
function createEasel() {
var Stage = Shumway.GFX.Stage;
var Easel = Shumway.GFX.Easel;
var Canvas2DStageRenderer = Shumway.GFX.Canvas2DStageRenderer;
TelemetryService.reportTelemetry({topic: "firstFrame"});
// skipping frame 0
initializeFrameControl = false;
return;
}
if (pauseExecution) {
e.cancel = true;
}
Shumway.GFX.WebGL.SHADER_ROOT = SHUMWAY_ROOT + "gfx/gl/shaders/";
var backend = Shumway.GFX.backend.value | 0;
return new Easel(document.getElementById("stageContainer"), backend);
}
document.addEventListener('keydown', function (e) {
if (e.keyCode == 119 && e.ctrlKey) { // Ctrl+F8
pauseExecution = !pauseExecution;
}
}, false);

View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<!--
Copyright 2013 Mozilla Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<base href=""/>
<script src="../shumway.player.js"></script>
<script src="viewerPlayer.js"></script>
</head>
<body>
Shumway Player
</body>
</html>

View File

@ -0,0 +1,174 @@
/*
* Copyright 2013 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var release = true;
var SHUMWAY_ROOT = "resource://shumway/";
var viewerPlayerglobalInfo = {
abcs: SHUMWAY_ROOT + "playerglobal/playerglobal.abcs",
catalog: SHUMWAY_ROOT + "playerglobal/playerglobal.json"
};
var avm2Root = SHUMWAY_ROOT + "avm2/";
var builtinPath = avm2Root + "generated/builtin/builtin.abc";
var avm1Path = avm2Root + "generated/avm1lib/avm1lib.abc";
window.print = function(msg) {
console.log(msg);
};
function runSwfPlayer(flashParams) {
var EXECUTION_MODE = Shumway.AVM2.Runtime.ExecutionMode;
var compilerSettings = flashParams.compilerSettings;
var sysMode = compilerSettings.sysCompiler ? EXECUTION_MODE.COMPILE : EXECUTION_MODE.INTERPRET;
var appMode = compilerSettings.appCompiler ? EXECUTION_MODE.COMPILE : EXECUTION_MODE.INTERPRET;
var asyncLoading = true;
var baseUrl = flashParams.baseUrl;
var movieParams = flashParams.movieParams;
var objectParams = flashParams.objectParams;
var movieUrl = flashParams.url;
Shumway.frameRateOption.value = flashParams.turboMode ? 60 : -1;
Shumway.AVM2.Verifier.enabled.value = compilerSettings.verifier;
Shumway.createAVM2(builtinPath, viewerPlayerglobalInfo, avm1Path, sysMode, appMode, function (avm2) {
function runSWF(file) {
var player = new Shumway.Player.Window.WindowPlayer(window, window.parent);
Shumway.ExternalInterfaceService.instance = player.createExternalInterfaceService();
player.load(file);
}
file = Shumway.FileLoadingService.instance.setBaseUrl(baseUrl);
if (asyncLoading) {
runSWF(movieUrl);
} else {
new Shumway.BinaryFileReader(movieUrl).readAll(null, function(buffer, error) {
if (!buffer) {
throw "Unable to open the file " + file + ": " + error;
}
runSWF(movieUrl, buffer);
});
}
});
}
var LOADER_WORKER_PATH = SHUMWAY_ROOT + 'web/worker.js';
function setupServices() {
Shumway.Telemetry.instance = {
reportTelemetry: function (data) {
window.parent.postMessage({
callback: 'reportTelemetry',
data: data
}, '*');
}
};
Shumway.FileLoadingService.instance = {
get baseUrl() {
return movieUrl;
},
nextSessionId: 1, // 0 - is reserved
sessions: [],
createSession: function () {
var sessionId = this.nextSessionId++;
return this.sessions[sessionId] = {
open: function (request) {
var self = this;
var path = Shumway.FileLoadingService.instance.resolveUrl(request.url);
console.log('Session #' + sessionId + ': loading ' + path);
window.parent.postMessage({
callback: 'loadFileRequest',
data: {url: path, method: request.method,
mimeType: request.mimeType, postData: request.data,
checkPolicyFile: request.checkPolicyFile, sessionId: sessionId}
}, '*');
},
notify: function (args) {
switch (args.topic) {
case "open":
this.onopen();
break;
case "close":
this.onclose();
Shumway.FileLoadingService.instance.sessions[sessionId] = null;
console.log('Session #' + sessionId + ': closed');
break;
case "error":
this.onerror && this.onerror(args.error);
break;
case "progress":
console.log('Session #' + sessionId + ': loaded ' + args.loaded + '/' + args.total);
this.onprogress && this.onprogress(args.array, {bytesLoaded: args.loaded, bytesTotal: args.total});
break;
}
}
};
},
setBaseUrl: function (url) {
var baseUrl;
if (typeof URL !== 'undefined') {
baseUrl = new URL(url, document.location.href).href;
} else {
var a = document.createElement('a');
a.href = url || '#';
a.setAttribute('style', 'display: none;');
document.body.appendChild(a);
baseUrl = a.href;
document.body.removeChild(a);
}
Shumway.FileLoadingService.instance.baseUrl = baseUrl;
return baseUrl;
},
resolveUrl: function (url) {
if (url.indexOf('://') >= 0) return url;
var base = Shumway.FileLoadingService.instance.baseUrl;
base = base.lastIndexOf('/') >= 0 ? base.substring(0, base.lastIndexOf('/') + 1) : '';
if (url.indexOf('/') === 0) {
var m = /^[^:]+:\/\/[^\/]+/.exec(base);
if (m) base = m[0];
}
return base + url;
}
};
}
window.addEventListener('message', function onWindowMessage(e) {
var data = e.data;
if (typeof data !== 'object' || data === null) {
return;
}
switch (data.type) {
case "loadFileResponse":
var args = data.args;
var session = Shumway.FileLoadingService.instance.sessions[args.sessionId];
if (session) {
session.notify(args);
}
break;
case "runSwf":
if (data.settings) {
Shumway.Settings.setSettings(data.settings);
}
setupServices();
runSwfPlayer(data.flashParams);
document.body.style.backgroundColor = 'green';
break;
}
}, true);

View File

@ -0,0 +1,19 @@
/*
* Copyright 2013 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
importScripts(['../shumway.parser.js']);
var loader = new Shumway.SWF.ResourceLoader(this, true);

View File

@ -51,6 +51,7 @@ pref("layers.componentalpha.enabled", false);
pref("apz.touch_start_tolerance", "0.1"); // dpi * tolerance = pixel threshold
pref("apz.pan_repaint_interval", 50); // prefer 20 fps
pref("apz.fling_repaint_interval", 50); // prefer 20 fps
pref("apz.smooth_scroll_repaint_interval", 50); // prefer 20 fps
pref("apz.fling_stopped_threshold", "0.2");
pref("apz.x_skate_size_multiplier", "2.5");
pref("apz.y_skate_size_multiplier", "2.5");

View File

@ -13,6 +13,7 @@ browser.jar:
skin/classic/browser/aboutCertError_sectionCollapsed.png
skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png
skin/classic/browser/aboutCertError_sectionExpanded.png
skin/classic/browser/aboutNetError.css (../shared/aboutNetError.css)
skin/classic/browser/aboutSocialError.css
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/aboutSyncTabs.css

View File

@ -5,6 +5,7 @@
browser.jar:
% skin browser classic/1.0 %skin/classic/browser/
skin/classic/browser/sanitizeDialog.css (sanitizeDialog.css)
skin/classic/browser/aboutNetError.css (../shared/aboutNetError.css)
* skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css)
skin/classic/browser/aboutSessionRestore-window-icon.png
skin/classic/browser/aboutWelcomeBack.css (../shared/aboutWelcomeBack.css)

View File

@ -0,0 +1 @@
/* This deliberately left empty for themes to use/override. */

View File

@ -15,6 +15,7 @@ browser.jar:
skin/classic/browser/aboutCertError_sectionCollapsed.png
skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png
skin/classic/browser/aboutCertError_sectionExpanded.png
skin/classic/browser/aboutNetError.css (../shared/aboutNetError.css)
skin/classic/browser/aboutSocialError.css
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/aboutSyncTabs.css
@ -428,6 +429,7 @@ browser.jar:
skin/classic/aero/browser/aboutCertError_sectionCollapsed.png
skin/classic/aero/browser/aboutCertError_sectionCollapsed-rtl.png
skin/classic/aero/browser/aboutCertError_sectionExpanded.png
skin/classic/aero/browser/aboutNetError.css (../shared/aboutNetError.css)
skin/classic/aero/browser/aboutSocialError.css
#ifdef MOZ_SERVICES_SYNC
skin/classic/aero/browser/aboutSyncTabs.css

View File

@ -0,0 +1,56 @@
dnl This Source Code Form is subject to the terms of the Mozilla Public
dnl License, v. 2.0. If a copy of the MPL was not distributed with this
dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
AC_DEFUN([MOZ_CONFIG_CLANG_PLUGIN], [
MOZ_ARG_ENABLE_BOOL(clang-plugin,
[ --enable-clang-plugin Enable building with the mozilla clang plugin ],
ENABLE_CLANG_PLUGIN=1,
ENABLE_CLANG_PLUGIN= )
if test -n "$ENABLE_CLANG_PLUGIN"; then
if test -z "$CLANG_CC"; then
AC_MSG_ERROR([Can't use clang plugin without clang.])
fi
AC_MSG_CHECKING([for llvm-config])
if test -z "$LLVMCONFIG"; then
LLVMCONFIG=`which llvm-config`
fi
if test -z "$LLVMCONFIG"; then
LLVMCONFIG=`$CXX -print-prog-name=llvm-config`
fi
if test ! -x "$LLVMCONFIG"; then
AC_MSG_RESULT([not found])
AC_MSG_ERROR([Cannot find an llvm-config binary for building a clang plugin])
fi
AC_MSG_RESULT([$LLVMCONFIG])
if test -z "$LLVMCONFIG"; then
AC_MSG_ERROR([Cannot find an llvm-config binary for building a clang plugin])
fi
LLVM_CXXFLAGS=`$LLVMCONFIG --cxxflags`
LLVM_LDFLAGS=`$LLVMCONFIG --ldflags --libs core mc analysis asmparser mcparser bitreader | xargs`
if test "${OS_ARCH}" = "Darwin"; then
CLANG_LDFLAGS="-lclangFrontend -lclangDriver -lclangSerialization"
CLANG_LDFLAGS="$CLANG_LDFLAGS -lclangParse -lclangSema -lclangAnalysis"
CLANG_LDFLAGS="$CLANG_LDFLAGS -lclangEdit -lclangAST -lclangLex"
CLANG_LDFLAGS="$CLANG_LDFLAGS -lclangBasic -lclangASTMatchers"
else
CLANG_LDFLAGS="-lclangASTMatchers"
fi
AC_DEFINE(MOZ_CLANG_PLUGIN)
fi
AC_SUBST(LLVM_CXXFLAGS)
AC_SUBST(LLVM_LDFLAGS)
AC_SUBST(CLANG_LDFLAGS)
AC_SUBST(ENABLE_CLANG_PLUGIN)
])

View File

@ -2,58 +2,16 @@
# 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/.
CXX := @CXX@
CXXFLAGS := @CXXFLAGS@
LDFLAGS := @LDFLAGS@
VPATH := @srcdir@
DSO_LDOPTS := @DSO_LDOPTS@
DLL_SUFFIX := @DLL_SUFFIX@
# LLVM_CXXFLAGS comes with its own optimization flags.
MOZ_OPTIMIZE =
# Helper for end
NULL :=
MOZ_GLUE_LDFLAGS =
CPPSRCS := \
clang-plugin.cpp \
$(NULL)
include $(topsrcdir)/config/config.mk
TESTSRCS := \
TestCustomHeap.cpp \
TestMustOverride.cpp \
TestNonHeapClass.cpp \
TestStackClass.cpp \
$(NULL)
OBJS := $(patsubst %.cpp,%.o,$(CPPSRCS))
TESTS := $(patsubst %.cpp,test-%,$(TESTSRCS))
PLUGIN := libclang-plugin.$(DLL_SUFFIX)
all: $(PLUGIN) $(TESTS)
$(OBJS): %.o: %.cpp Makefile
$(CXX) -o $@ -c $(CXXFLAGS) $<
$(PLUGIN): $(OBJS)
rm -f $@
$(CXX) $(DSO_LDOPTS) -o $@ $(CXXFLAGS) $(OBJS) $(LDFLAGS)
TESTFLAGS := -fsyntax-only -Xclang -verify \
-Xclang -load -Xclang $(CURDIR)/$(PLUGIN) \
-Xclang -add-plugin -Xclang moz-check
$(TESTS): test-%: tests/%.cpp $(PLUGIN)
$(CXX) $(TESTFLAGS) $<
compile libs export tools: all
distclean clean:
rm -f $(OBJS) $(TESTS) $(PLUGIN)
check:
libs: binaries
binaries: all
@touch $@
.PHONY: compile libs export tools distclean clean check
# In the current moz.build world, we need to override essentially every
# variable to limit ourselves to what we need to build the clang plugin.
OS_CXXFLAGS := $(LLVM_CXXFLAGS) -fno-rtti -fno-exceptions
OS_COMPILE_CXXFLAGS :=
OS_LDFLAGS := $(LLVM_LDFLAGS) $(CLANG_LDFLAGS)
DSO_LDOPTS := -shared

View File

@ -1,67 +0,0 @@
#!/bin/sh
PLATFORM=`uname`
# Default srcdir to this directory
srcdir=$(dirname $0)
for option; do
case "$option" in
-*=*) optarg=`echo "$option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
*) optarg= ;;
esac
case "$option" in
--srcdir=*) srcdir="$optarg";;
esac
done
if test -z "$CXX"; then
CXX=`which clang++`
fi
echo -n "checking for llvm-config... "
if test -z "$LLVMCONFIG"; then
LLVMCONFIG=`which llvm-config`
fi
if test -z "$LLVMCONFIG"; then
LLVMCONFIG=`dirname $CXX`/llvm-config
fi
if test ! -x "$LLVMCONFIG"; then
echo "configure: error: Cannot find an llvm-config binary for building a clang plugin" 1>&2
exit 1
fi
echo "$LLVMCONFIG"
LLVMLIBS="core mc analysis asmparser mcparser bitreader"
LLVMCXXFLAGS=`$LLVMCONFIG --cxxflags`
LLVMLDFLAGS=`$LLVMCONFIG --ldflags`
LLVMLDFLAGS="$LLVMLDFLAGS `$LLVMCONFIG --libs $LLVMLIBS`"
if [ $PLATFORM == Darwin ]; then
DSO_LDOPTS="-dynamiclib -shared"
CLANGLDFLAGS="-lclangFrontend -lclangDriver -lclangSerialization \
-lclangParse -lclangSema -lclangAnalysis -lclangEdit -lclangAST \
-lclangLex -lclangBasic -lclangASTMatchers"
DLL_SUFFIX="dylib"
else
DSO_LDOPTS="-shared"
CLANGLDFLAGS=-lclangASTMatchers
DLL_SUFFIX="so"
fi
CXXFLAGS="$CXXFLAGS $LLVMCXXFLAGS -fno-rtti -fno-exceptions"
LDFLAGS="$LDFLAGS $LLVMLDFLAGS $CLANGLDFLAGS"
cat $srcdir/Makefile.in | sed \
-e "s%@CXX@%$CXX%" \
-e "s%@CXXFLAGS@%$CXXFLAGS%" \
-e "s%@LDFLAGS@%$LDFLAGS%" \
-e "s%@srcdir@%$srcdir%" \
-e "s%@DSO_LDOPTS@%$DSO_LDOPTS%" \
-e "s%@DLL_SUFFIX@%$DLL_SUFFIX%" \
> Makefile

View File

@ -0,0 +1,18 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
SharedLibrary('clang-plugin')
SOURCES += [
'clang-plugin.cpp',
]
DISABLE_STL_WRAPPING = True
NO_VISIBILITY_FLAGS = True
DIRS += [
'tests',
]

View File

@ -0,0 +1,14 @@
# 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/.
# Build without any warning flags, and with clang verify flag for a
# syntax-only build (no codegen).
OS_CXXFLAGS := $(filter-out -W%,$(OS_CXXFLAGS)) -fsyntax-only -Xclang -verify
include $(topsrcdir)/config/rules.mk
target:: $(OBJS)
# We don't actually build anything.
.PHONY: $(OBJS)

View File

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

View File

@ -43,6 +43,8 @@ SEARCH_PATHS = [
'xpcom/idl-parser',
'testing',
'testing/xpcshell',
'testing/web-platform',
'testing/web-platform/harness',
'testing/marionette/client',
'testing/marionette/client/marionette',
'testing/marionette/transport',
@ -80,6 +82,7 @@ MACH_MODULES = [
'testing/mochitest/mach_commands.py',
'testing/xpcshell/mach_commands.py',
'testing/talos/mach_commands.py',
'testing/web-platform/mach_commands.py',
'testing/xpcshell/mach_commands.py',
'tools/docs/mach_commands.py',
'tools/mercurial/mach_commands.py',

View File

@ -3,6 +3,7 @@
# You can obtain one at http://mozilla.org/MPL/2.0/.
NO_EXPAND_LIBS = 1
ENABLE_CLANG_PLUGIN :=
include $(topsrcdir)/config/rules.mk

View File

@ -75,16 +75,14 @@ endif
# TIERS (like for js/src).
CURRENT_DIRS := $($(CURRENT_TIER)_dirs)
# The compile tier has different rules from other tiers.
ifeq ($(CURRENT_TIER),compile)
# Need a list of compile targets because we can't use pattern rules:
# https://savannah.gnu.org/bugs/index.php?42833
.PHONY: $(compile_targets)
$(compile_targets):
$(call SUBMAKE,$(@F),$(@D))
else
# The compile tier has different rules from other tiers.
ifneq ($(CURRENT_TIER),compile)
# Recursion rule for all directories traversed for all subtiers in the
# current tier.
@ -108,11 +106,6 @@ $(addsuffix /$(CURRENT_TIER),$(filter-out config,$(CURRENT_DIRS))): config/$(CUR
# nsinstall.py there.
ifneq (,$(filter config/host, $(compile_targets)))
$(addsuffix /$(CURRENT_TIER),$(CURRENT_DIRS)): config/host
# Ensure rules for config/host and its possible dependencies.
.PHONY: $(filter %/host, $(compile_targets))
$(filter %/host, $(compile_targets)):
$(call SUBMAKE,host,$(@D))
endif
endif
@ -163,3 +156,32 @@ endif # ifeq (.,$(DEPTH))
recurse:
@$(RECURSED_COMMAND)
$(LOOP_OVER_DIRS)
ifeq (.,$(DEPTH))
# Interdependencies for parallel export.
js/xpconnect/src/export: dom/bindings/export xpcom/xpidl/export
accessible/xpcom/export: xpcom/xpidl/export
ifdef ENABLE_CLANG_PLUGIN
$(filter-out build/clang-plugin/%,$(compile_targets)): build/clang-plugin/target build/clang-plugin/tests/target
build/clang-plugin/tests/target: build/clang-plugin/target
endif
# Interdependencies that moz.build world don't know about yet for compilation.
# Note some others are hardcoded or "guessed" in recursivemake.py and emitter.py
ifeq ($(MOZ_WIDGET_TOOLKIT),gtk3)
toolkit/library/target: widget/gtk/mozgtk/gtk3/target
endif
ifdef MOZ_LDAP_XPCOM
ldap/target: config/external/nss/target mozglue/build/target
toolkit/library/target: ldap/target
endif
ifndef MOZ_FOLD_LIBS
ifndef MOZ_NATIVE_SQLITE
config/external/nss/target: db/sqlite3/src/target
endif
endif
ifeq ($(MOZ_REPLACE_MALLOC_LINKAGE),dummy library)
mozglue/build/target: memory/replace/dummy/target
endif
endif

View File

@ -7250,18 +7250,7 @@ dnl ========================================================
dnl = Enable using the clang plugin to build
dnl ========================================================
MOZ_ARG_ENABLE_BOOL(clang-plugin,
[ --enable-clang-plugin Enable building with the mozilla clang plugin ],
ENABLE_CLANG_PLUGIN=1,
ENABLE_CLANG_PLUGIN= )
if test -n "$ENABLE_CLANG_PLUGIN"; then
if test -z "$CLANG_CC"; then
AC_MSG_ERROR([Can't use clang plugin without clang.])
fi
AC_DEFINE(MOZ_CLANG_PLUGIN)
fi
AC_SUBST(ENABLE_CLANG_PLUGIN)
MOZ_CONFIG_CLANG_PLUGIN
dnl ========================================================
dnl = Enable stripping of libs & executables
@ -9112,11 +9101,6 @@ HOST_CXXFLAGS="$_SUBDIR_HOST_CXXFLAGS"
HOST_LDFLAGS="$_SUBDIR_HOST_LDFLAGS"
RC=
if test -n "$ENABLE_CLANG_PLUGIN"; then
ac_configure_args="$_SUBDIR_CONFIG_ARGS"
AC_OUTPUT_SUBDIRS(build/clang-plugin)
fi
# Run the SpiderMonkey 'configure' script.
dist=$MOZ_BUILD_ROOT/dist
ac_configure_args="$_SUBDIR_CONFIG_ARGS"

View File

@ -6396,10 +6396,11 @@ nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(aDocument->GetWindow()))) {
return true;
}
jsapi.Init();
JSContext* cx = jsapi.cx();
// We can use the junk scope here, because we're just using it for
// regexp evaluation, not actual script execution.
JSAutoCompartment ac(cx, xpc::UnprivilegedJunkScope());
// The pattern has to match the entire value.
aPattern.Insert(NS_LITERAL_STRING("^(?:"), 0);

View File

@ -2004,6 +2004,7 @@ GK_ATOM(ondataavailable, "ondataavailable")
GK_ATOM(onwarning, "onwarning")
GK_ATOM(onstart, "onstart")
GK_ATOM(onstop, "onstop")
GK_ATOM(onphoto, "onphoto")
#ifdef MOZ_GAMEPAD
GK_ATOM(ongamepadbuttondown, "ongamepadbuttondown")
GK_ATOM(ongamepadbuttonup, "ongamepadbuttonup")

View File

@ -1111,8 +1111,7 @@ nsXMLHttpRequest::Status()
return 0;
}
uint16_t readyState;
GetReadyState(&readyState);
uint16_t readyState = ReadyState();
if (readyState == UNSENT || readyState == OPENED) {
return 0;
}
@ -1136,14 +1135,8 @@ nsXMLHttpRequest::Status()
nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
if (!httpChannel) {
// Let's simulate the http protocol for jar/app requests:
nsCOMPtr<nsIJARChannel> jarChannel = GetCurrentJARChannel();
if (jarChannel) {
return 200; // Ok
}
return 0;
// Pretend like we got a 200 response, since our load was successful
return 200;
}
uint32_t status;
@ -1159,32 +1152,34 @@ IMPL_CSTRING_GETTER(GetStatusText)
void
nsXMLHttpRequest::GetStatusText(nsCString& aStatusText)
{
nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
// Return an empty status text on all error loads.
aStatusText.Truncate();
if (!httpChannel) {
return;
}
// Make sure we don't leak status information from denied cross-site
// requests.
if (IsDeniedCrossSiteRequest()) {
return;
}
// Check the current XHR state to see if it is valid to obtain the statusText
// value. This check is to prevent the status text for redirects from being
// available before all the redirects have been followed and HTTP headers have
// been received.
uint16_t readyState;
GetReadyState(&readyState);
if (readyState != OPENED && readyState != UNSENT) {
httpChannel->GetResponseStatusText(aStatusText);
uint16_t readyState = ReadyState();
if (readyState == UNSENT || readyState == OPENED) {
return;
}
if (mErrorLoad) {
return;
}
nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
if (httpChannel) {
httpChannel->GetResponseStatusText(aStatusText);
} else {
aStatusText.AssignLiteral("OK");
}
}
void

View File

@ -23,7 +23,7 @@ function testFile(file, contents, test) {
// Load file using URL.createObjectURL and XMLHttpRequest
var xhr = new XMLHttpRequest;
xhr.open("GET", URL.createObjectURL(file));
xhr.onload = getXHRLoadHandler(contents, contents.length, false,
xhr.onload = getXHRLoadHandler(contents, contents.length,
"XMLHttpRequest load of " + test);
xhr.overrideMimeType('text/plain; charset=x-user-defined');
xhr.send();
@ -59,7 +59,7 @@ function testFile(file, contents, test) {
"request content-length in XMLHttpRequest send of " + test);
};
xhr.addEventListener("load",
getXHRLoadHandler(contents, contents.length, true,
getXHRLoadHandler(contents, contents.length,
"XMLHttpRequest send of " + test),
false);
xhr.overrideMimeType('text/plain; charset=x-user-defined');
@ -88,18 +88,12 @@ function getFileReaderLoadHandler(expectedResult, expectedLength, testName) {
}
}
function getXHRLoadHandler(expectedResult, expectedLength, statusWorking, testName) {
function getXHRLoadHandler(expectedResult, expectedLength, testName) {
return function (event) {
is(event.target.readyState, 4,
"[XHR] readyState in test " + testName);
if (statusWorking) {
is(event.target.status, 200,
"[XHR] no error in test " + testName);
}
else {
todo_is(event.target.status, 200,
"[XHR] no error in test " + testName);
}
is(event.target.status, 200,
"[XHR] no error in test " + testName);
// Do not use |is(convertXHRBinary(event.target.responseText), expectedResult, "...");| that may output raw binary data.
var convertedData = convertXHRBinary(event.target.responseText);
is(convertedData.length, expectedResult.length,

View File

@ -558,6 +558,7 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e1
run-if = os == 'linux'
[test_bug1008126.html]
run-if = os == 'linux'
[test_bug1057176.html]
[test_caretPositionFromPoint.html]
[test_classList.html]
# This test fails on the Mac for some reason

View File

@ -22,23 +22,33 @@ function runTests() {
var path = "/tests/content/base/test/";
var passFiles = [['file_XHR_pass1.xml', 'GET', 200, 'text/xml'],
['file_XHR_pass2.txt', 'GET', 200, 'text/plain'],
['file_XHR_pass3.txt', 'GET', 200, 'text/plain'],
['data:text/xml,%3Cres%3Ehello%3C/res%3E%0A', 'GET', 0, 'text/xml'],
['data:text/plain,hello%20pass%0A', 'GET', 0, 'text/plain'],
['data:,foo', 'GET', 0, 'text/plain;charset=US-ASCII', 'foo'],
['data:text/plain;base64,Zm9v', 'GET', 0, 'text/plain', 'foo'],
['data:text/plain,foo#bar', 'GET', 0, 'text/plain', 'foo'],
['data:text/plain,foo%23bar', 'GET', 0, 'text/plain', 'foo#bar'],
var passFiles = [['file_XHR_pass1.xml', 'GET', 200, 'OK', 'text/xml'],
['file_XHR_pass2.txt', 'GET', 200, 'OK', 'text/plain'],
['file_XHR_pass3.txt', 'GET', 200, 'OK', 'text/plain'],
['data:text/xml,%3Cres%3Ehello%3C/res%3E%0A', 'GET', 200, 'OK', 'text/xml'],
['data:text/plain,hello%20pass%0A', 'GET', 200, 'OK', 'text/plain'],
['data:,foo', 'GET', 200, 'OK', 'text/plain;charset=US-ASCII', 'foo'],
['data:text/plain;base64,Zm9v', 'GET', 200, 'OK', 'text/plain', 'foo'],
['data:text/plain,foo#bar', 'GET', 200, 'OK', 'text/plain', 'foo'],
['data:text/plain,foo%23bar', 'GET', 200, 'OK', 'text/plain', 'foo#bar'],
];
var blob = new Blob(["foo"], { type: "text/plain" });
var blobURL = URL.createObjectURL(blob);
passFiles.push([blobURL, 'GET', 200, 'OK', 'text/plain', 'foo']);
var failFiles = [['//example.com' + path + 'file_XHR_pass1.xml', 'GET'],
['ftp://localhost' + path + 'file_XHR_pass1.xml', 'GET'],
['file_XHR_fail1.txt', 'GET'],
];
for (i = 0; i < passFiles.length; ++i) {
// Function to give our hacked is() a scope
(function(oldIs) {
function is(actual, expected, message) {
oldIs(actual, expected, message + " for " + passFiles[i][0]);
}
xhr = new XMLHttpRequest();
is(xhr.getResponseHeader("Content-Type"), null, "should be null");
is(xhr.getAllResponseHeaders(), "", "should be empty string");
@ -46,21 +56,25 @@ for (i = 0; i < passFiles.length; ++i) {
xhr.open(passFiles[i][1], passFiles[i][0], false);
xhr.send(null);
is(xhr.status, passFiles[i][2], "wrong status");
is(xhr.getResponseHeader("Content-Type"), passFiles[i][3], "wrong content type");
is(xhr.statusText, passFiles[i][3], "wrong statusText");
is(xhr.getResponseHeader("Content-Type"), passFiles[i][4], "wrong content type");
var headers = xhr.getAllResponseHeaders();
ok(/(?:^|\n)Content-Type:\s*([^\r\n]*)\r\n/i.test(headers) &&
RegExp.$1 === passFiles[i][3], "wrong response headers");
RegExp.$1 === passFiles[i][4], "wrong response headers");
if (xhr.responseXML) {
is((new XMLSerializer()).serializeToString(xhr.responseXML.documentElement),
passFiles[i][4] || "<res>hello</res>", "wrong responseXML");
is(xhr.response, passFiles[i][4] || "<res>hello</res>\n", "wrong response");
passFiles[i][5] || "<res>hello</res>", "wrong responseXML");
is(xhr.response, passFiles[i][5] || "<res>hello</res>\n", "wrong response");
}
else {
is(xhr.responseText, passFiles[i][4] || "hello pass\n", "wrong responseText");
is(xhr.response, passFiles[i][4] || "hello pass\n", "wrong response");
is(xhr.responseText, passFiles[i][5] || "hello pass\n", "wrong responseText");
is(xhr.response, passFiles[i][5] || "hello pass\n", "wrong response");
}
})(is);
}
URL.revokeObjectURL(blobURL);
for (i = 0; i < failFiles.length; ++i) {
xhr = new XMLHttpRequest();
var didthrow = false;

View File

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1057176
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1057176</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Bug 1057176 **/
var doc = document.implementation.createDocument(null, null);
var elem = doc.createElementNS("http://www.w3.org/1999/xhtml", "input");
elem.pattern = "abc";
elem.value = "def";
ok(!elem.validity.valid, '"def" should not match the pattern "abc"');
elem.value = "abc";
ok(elem.validity.valid, '"abc" should match the pattern "abc"');
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1057176">Mozilla Bug 1057176</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -541,17 +541,49 @@ HTMLCanvasElement::ToBlob(JSContext* aCx,
mCurrentContext->GetImageBuffer(&imageBuffer, &format);
}
// Encoder callback when encoding is complete.
class EncodeCallback : public EncodeCompleteCallback
{
public:
EncodeCallback(nsIGlobalObject* aGlobal, FileCallback* aCallback)
: mGlobal(aGlobal)
, mFileCallback(aCallback) {}
// This is called on main thread.
nsresult ReceiveBlob(already_AddRefed<DOMFile> aBlob)
{
nsRefPtr<DOMFile> blob = aBlob;
uint64_t size;
nsresult rv = blob->GetSize(&size);
if (NS_SUCCEEDED(rv)) {
AutoJSAPI jsapi;
jsapi.Init(mGlobal);
JS_updateMallocCounter(jsapi.cx(), size);
}
mozilla::ErrorResult error;
mFileCallback->Call(blob, error);
mGlobal = nullptr;
mFileCallback = nullptr;
return error.ErrorCode();
}
nsCOMPtr<nsIGlobalObject> mGlobal;
nsRefPtr<FileCallback> mFileCallback;
};
nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
MOZ_ASSERT(global);
nsRefPtr<EncodeCompleteCallback> callback = new EncodeCallback(global, &aCallback);
aRv = ImageEncoder::ExtractDataAsync(type,
params,
usingCustomParseOptions,
imageBuffer,
format,
GetSize(),
mCurrentContext,
global,
aCallback);
callback);
}
already_AddRefed<nsIDOMFile>

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