mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 04:27:37 +00:00
Merge m-c to b2g-inbound. a=merge
This commit is contained in:
commit
622e513dbf
@ -1207,6 +1207,7 @@ pref("security.sandbox.windows.log", false);
|
|||||||
// 3 - the strongest settings we seem to be able to use without breaking
|
// 3 - the strongest settings we seem to be able to use without breaking
|
||||||
// everything, but will probably cause some functionality restrictions
|
// everything, but will probably cause some functionality restrictions
|
||||||
pref("dom.ipc.plugins.sandbox-level.default", 0);
|
pref("dom.ipc.plugins.sandbox-level.default", 0);
|
||||||
|
pref("dom.ipc.plugins.sandbox-level.flash", 0);
|
||||||
|
|
||||||
#if defined(MOZ_CONTENT_SANDBOX)
|
#if defined(MOZ_CONTENT_SANDBOX)
|
||||||
// This controls the strength of the Windows content process sandbox for testing
|
// This controls the strength of the Windows content process sandbox for testing
|
||||||
@ -1409,8 +1410,8 @@ pref("devtools.inspector.activeSidebar", "ruleview");
|
|||||||
// Enable the markup preview
|
// Enable the markup preview
|
||||||
pref("devtools.inspector.markupPreview", false);
|
pref("devtools.inspector.markupPreview", false);
|
||||||
pref("devtools.inspector.remote", false);
|
pref("devtools.inspector.remote", false);
|
||||||
// Expand pseudo-elements by default in the rule-view
|
// Collapse pseudo-elements by default in the rule-view
|
||||||
pref("devtools.inspector.show_pseudo_elements", true);
|
pref("devtools.inspector.show_pseudo_elements", false);
|
||||||
// The default size for image preview tooltips in the rule-view/computed-view/markup-view
|
// The default size for image preview tooltips in the rule-view/computed-view/markup-view
|
||||||
pref("devtools.inspector.imagePreviewTooltipSize", 300);
|
pref("devtools.inspector.imagePreviewTooltipSize", 300);
|
||||||
// Enable user agent style inspection in rule-view
|
// Enable user agent style inspection in rule-view
|
||||||
@ -1836,6 +1837,9 @@ pref("identity.fxaccounts.remote.profile.uri", "https://profile.accounts.firefox
|
|||||||
// The remote URL of the FxA OAuth Server
|
// The remote URL of the FxA OAuth Server
|
||||||
pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1");
|
pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1");
|
||||||
|
|
||||||
|
// Whether we display profile images in the UI or not.
|
||||||
|
pref("identity.fxaccounts.profile_image.enabled", true);
|
||||||
|
|
||||||
// Migrate any existing Firefox Account data from the default profile to the
|
// Migrate any existing Firefox Account data from the default profile to the
|
||||||
// Developer Edition profile.
|
// Developer Edition profile.
|
||||||
#ifdef MOZ_DEV_EDITION
|
#ifdef MOZ_DEV_EDITION
|
||||||
|
@ -90,6 +90,12 @@ var FullScreen = {
|
|||||||
// This is needed if they use the context menu to quit fullscreen
|
// This is needed if they use the context menu to quit fullscreen
|
||||||
this._isPopupOpen = false;
|
this._isPopupOpen = false;
|
||||||
this.cleanup();
|
this.cleanup();
|
||||||
|
// In TabsInTitlebar._update(), we cancel the appearance update on
|
||||||
|
// resize event for exiting fullscreen, since that happens before we
|
||||||
|
// change the UI here in the "fullscreen" event. Hence we need to
|
||||||
|
// call it here to ensure the appearance is properly updated. See
|
||||||
|
// TabsInTitlebar._update() and bug 1173768.
|
||||||
|
TabsInTitlebar.updateAppearance(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -5066,7 +5066,15 @@ var TabsInTitlebar = {
|
|||||||
if (this._lastSizeMode == sizemode) {
|
if (this._lastSizeMode == sizemode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let oldSizeMode = this._lastSizeMode;
|
||||||
this._lastSizeMode = sizemode;
|
this._lastSizeMode = sizemode;
|
||||||
|
// Don't update right now if we are leaving fullscreen, since the UI is
|
||||||
|
// still changing in the consequent "fullscreen" event. Code there will
|
||||||
|
// call this function again when everything is ready.
|
||||||
|
// See browser-fullScreen.js: FullScreen.toggle and bug 1173768.
|
||||||
|
if (oldSizeMode == "fullscreen") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let something in this._disallowed) {
|
for (let something in this._disallowed) {
|
||||||
|
@ -501,6 +501,16 @@ input[type=button] {
|
|||||||
transform: translate(-30px, -20px) scale(0) translate(30px, 20px);
|
transform: translate(-30px, -20px) scale(0) translate(30px, 20px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#newtab-customize-panel:-moz-locale-dir(rtl) {
|
||||||
|
transform-origin: 40px top 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#newtab-customize-panel:-moz-locale-dir(rtl),
|
||||||
|
#newtab-customize-panel-anchor:-moz-locale-dir(rtl) {
|
||||||
|
left: 15px;
|
||||||
|
right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
#newtab-customize-panel[open="true"] {
|
#newtab-customize-panel[open="true"] {
|
||||||
transform: translate(-30px, -20px) scale(1) translate(30px, 20px);
|
transform: translate(-30px, -20px) scale(1) translate(30px, 20px);
|
||||||
}
|
}
|
||||||
|
@ -145,9 +145,8 @@ SearchSuggestionUIController.prototype = {
|
|||||||
this["_on" + event.type[0].toUpperCase() + event.type.substr(1)](event);
|
this["_on" + event.type[0].toUpperCase() + event.type.substr(1)](event);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onInput: function () {
|
_onInput: function (event) {
|
||||||
if (this._ignoreInputEvent) {
|
if (this._ignoreInputEvent) {
|
||||||
this._ignoreInputEvent = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.input.value) {
|
if (this.input.value) {
|
||||||
@ -157,7 +156,7 @@ SearchSuggestionUIController.prototype = {
|
|||||||
this._stickyInputValue = "";
|
this._stickyInputValue = "";
|
||||||
this._hideSuggestions();
|
this._hideSuggestions();
|
||||||
}
|
}
|
||||||
this.selectAndUpdateInput(-1);
|
this.selectedIndex = -1;
|
||||||
},
|
},
|
||||||
|
|
||||||
_onKeypress: function (event) {
|
_onKeypress: function (event) {
|
||||||
@ -242,15 +241,12 @@ SearchSuggestionUIController.prototype = {
|
|||||||
let suggestion = this.suggestionAtIndex(idx);
|
let suggestion = this.suggestionAtIndex(idx);
|
||||||
this._stickyInputValue = suggestion;
|
this._stickyInputValue = suggestion;
|
||||||
|
|
||||||
// Commit composition string forcibly, because setting input value does not
|
// Setting value commits composition string forcibly. While IME commits
|
||||||
// work if input has composition string (see bug 1115616 and bug 632744).
|
// composition, this needs to ignore input event at committed composition
|
||||||
// Ignore input event for composition end to avoid getting suggestion again.
|
// string which will be overwritten by the suggestion.
|
||||||
this._ignoreInputEvent = true;
|
this._ignoreInputEvent = true;
|
||||||
this.input.blur();
|
|
||||||
this.input.focus();
|
|
||||||
this._ignoreInputEvent = false;
|
|
||||||
|
|
||||||
this.input.value = suggestion;
|
this.input.value = suggestion;
|
||||||
|
this._ignoreInputEvent = false;
|
||||||
this.input.setAttribute("selection-index", idx);
|
this.input.setAttribute("selection-index", idx);
|
||||||
this.input.setAttribute("selection-kind", "mouse");
|
this.input.setAttribute("selection-kind", "mouse");
|
||||||
this._hideSuggestions();
|
this._hideSuggestions();
|
||||||
|
@ -337,7 +337,7 @@ skip-if = buildapp == 'mulet'
|
|||||||
[browser_parsable_css.js]
|
[browser_parsable_css.js]
|
||||||
skip-if = e10s
|
skip-if = e10s
|
||||||
[browser_parsable_script.js]
|
[browser_parsable_script.js]
|
||||||
skip-if = asan # Disabled because it takes a long time (see test for more information)
|
skip-if = asan || (os == 'linux' && !debug && (bits == 32)) # disabled on asan because of timeouts, and bug 1172468 for the linux 32-bit pgo issue.
|
||||||
|
|
||||||
[browser_pinnedTabs.js]
|
[browser_pinnedTabs.js]
|
||||||
[browser_plainTextLinks.js]
|
[browser_plainTextLinks.js]
|
||||||
|
@ -8,7 +8,7 @@ let suggestedLink = {
|
|||||||
imageURI: "data:image/png;base64,helloWORLD3",
|
imageURI: "data:image/png;base64,helloWORLD3",
|
||||||
title: "title2",
|
title: "title2",
|
||||||
type: "affiliate",
|
type: "affiliate",
|
||||||
frecent_sites: ["classroom.google.com", "codecademy.com", "elearning.ut.ac.id", "khanacademy.org", "learn.jquery.com", "teamtreehouse.com", "tutorialspoint.com", "udacity.com", "w3cschool.cc", "w3schools.com"]
|
frecent_sites: ["classroom.google.com", "codeacademy.org", "codecademy.com", "codeschool.com", "codeyear.com", "elearning.ut.ac.id", "how-to-build-websites.com", "htmlcodetutorial.com", "htmldog.com", "htmlplayground.com", "learn.jquery.com", "quackit.com", "roseindia.net", "teamtreehouse.com", "tizag.com", "tutorialspoint.com", "udacity.com", "w3schools.com", "webdevelopersnotes.com"]
|
||||||
};
|
};
|
||||||
|
|
||||||
gDirectorySource = "data:application/json," + JSON.stringify({
|
gDirectorySource = "data:application/json," + JSON.stringify({
|
||||||
@ -139,7 +139,7 @@ function runTests() {
|
|||||||
is(type, "affiliate", "suggested link is affiliate");
|
is(type, "affiliate", "suggested link is affiliate");
|
||||||
is(enhanced, "", "suggested link has no enhanced image");
|
is(enhanced, "", "suggested link has no enhanced image");
|
||||||
is(title, "title2");
|
is(title, "title2");
|
||||||
ok(suggested.indexOf("Suggested for <strong> Web Education </strong> visitors") > -1, "Suggested for 'Web Education'");
|
ok(suggested.indexOf("Suggested for <strong> webdev education </strong> visitors") > -1, "Suggested for 'webdev education'");
|
||||||
|
|
||||||
// Enhanced history link shows up second
|
// Enhanced history link shows up second
|
||||||
({type, enhanced, title, suggested} = getData(1));
|
({type, enhanced, title, suggested} = getData(1));
|
||||||
@ -176,7 +176,7 @@ function runTests() {
|
|||||||
ok(suggested.indexOf("Suggested for <strong> Technology </strong> enthusiasts who visit sites like <strong> classroom.google.com </strong>") > -1, "Suggested for 'Technology' enthusiasts");
|
ok(suggested.indexOf("Suggested for <strong> Technology </strong> enthusiasts who visit sites like <strong> classroom.google.com </strong>") > -1, "Suggested for 'Technology' enthusiasts");
|
||||||
|
|
||||||
|
|
||||||
// Test server provided explanation string without category override.
|
// Test server provided explanation string without category override.
|
||||||
delete suggestedLink.adgroup_name;
|
delete suggestedLink.adgroup_name;
|
||||||
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE,
|
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE,
|
||||||
"data:application/json," + encodeURIComponent(JSON.stringify({"suggested": [suggestedLink]})));
|
"data:application/json," + encodeURIComponent(JSON.stringify({"suggested": [suggestedLink]})));
|
||||||
@ -185,5 +185,31 @@ function runTests() {
|
|||||||
yield addNewTabPageTab();
|
yield addNewTabPageTab();
|
||||||
({type, enhanced, title, suggested} = getData(0));
|
({type, enhanced, title, suggested} = getData(0));
|
||||||
Cu.reportError("SUGGEST " + suggested);
|
Cu.reportError("SUGGEST " + suggested);
|
||||||
ok(suggested.indexOf("Suggested for <strong> Web Education </strong> enthusiasts who visit sites like <strong> classroom.google.com </strong>") > -1, "Suggested for 'Web Education' enthusiasts");
|
ok(suggested.indexOf("Suggested for <strong> webdev education </strong> enthusiasts who visit sites like <strong> classroom.google.com </strong>") > -1, "Suggested for 'webdev education' enthusiasts");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Test with xml entities in category name
|
||||||
|
suggestedLink.url = "http://example1.com/3";
|
||||||
|
suggestedLink.adgroup_name = ">angles< & \"quotes\'";
|
||||||
|
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE,
|
||||||
|
"data:application/json," + encodeURIComponent(JSON.stringify({"suggested": [suggestedLink]})));
|
||||||
|
yield watchLinksChangeOnce().then(TestRunner.next);
|
||||||
|
|
||||||
|
yield addNewTabPageTab();
|
||||||
|
({type, enhanced, title, suggested} = getData(0));
|
||||||
|
Cu.reportError("SUGGEST " + suggested);
|
||||||
|
ok(suggested.indexOf("Suggested for <strong> >angles< & \"quotes\' </strong> enthusiasts who visit sites like <strong> classroom.google.com </strong>") > -1, "Suggested for 'xml entities' enthusiasts");
|
||||||
|
|
||||||
|
|
||||||
|
// Test with xml entities in explanation.
|
||||||
|
suggestedLink.explanation = "Testing junk explanation &<>\"'";
|
||||||
|
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE,
|
||||||
|
"data:application/json," + encodeURIComponent(JSON.stringify({"suggested": [suggestedLink]})));
|
||||||
|
yield watchLinksChangeOnce().then(TestRunner.next);
|
||||||
|
|
||||||
|
yield addNewTabPageTab();
|
||||||
|
({type, enhanced, title, suggested} = getData(0));
|
||||||
|
Cu.reportError("SUGGEST " + suggested);
|
||||||
|
ok(suggested.indexOf("Testing junk explanation &<>\"'") > -1, "Junk test");
|
||||||
}
|
}
|
||||||
|
@ -730,6 +730,7 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
var description = metadata.title || metadata.description;
|
var description = metadata.title || metadata.description;
|
||||||
var url = metadata.url;
|
var url = metadata.url;
|
||||||
this.setState({
|
this.setState({
|
||||||
|
checked: false,
|
||||||
previewImage: previewImage,
|
previewImage: previewImage,
|
||||||
description: description,
|
description: description,
|
||||||
url: url
|
url: url
|
||||||
@ -775,7 +776,8 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
return (
|
return (
|
||||||
React.createElement("div", {className: "new-room-view"},
|
React.createElement("div", {className: "new-room-view"},
|
||||||
React.createElement("div", {className: contextClasses},
|
React.createElement("div", {className: contextClasses},
|
||||||
React.createElement(Checkbox, {label: mozL10n.get("context_inroom_label"),
|
React.createElement(Checkbox, {checked: this.state.checked,
|
||||||
|
label: mozL10n.get("context_inroom_label"),
|
||||||
onChange: this.onCheckboxChange}),
|
onChange: this.onCheckboxChange}),
|
||||||
React.createElement(sharedViews.ContextUrlView, {
|
React.createElement(sharedViews.ContextUrlView, {
|
||||||
allowClick: false,
|
allowClick: false,
|
||||||
|
@ -730,6 +730,7 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
var description = metadata.title || metadata.description;
|
var description = metadata.title || metadata.description;
|
||||||
var url = metadata.url;
|
var url = metadata.url;
|
||||||
this.setState({
|
this.setState({
|
||||||
|
checked: false,
|
||||||
previewImage: previewImage,
|
previewImage: previewImage,
|
||||||
description: description,
|
description: description,
|
||||||
url: url
|
url: url
|
||||||
@ -775,7 +776,8 @@ loop.panel = (function(_, mozL10n) {
|
|||||||
return (
|
return (
|
||||||
<div className="new-room-view">
|
<div className="new-room-view">
|
||||||
<div className={contextClasses}>
|
<div className={contextClasses}>
|
||||||
<Checkbox label={mozL10n.get("context_inroom_label")}
|
<Checkbox checked={this.state.checked}
|
||||||
|
label={mozL10n.get("context_inroom_label")}
|
||||||
onChange={this.onCheckboxChange} />
|
onChange={this.onCheckboxChange} />
|
||||||
<sharedViews.ContextUrlView
|
<sharedViews.ContextUrlView
|
||||||
allowClick={false}
|
allowClick={false}
|
||||||
|
@ -664,14 +664,21 @@ loop.roomViews = (function(mozL10n) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case ROOM_STATES.READY:
|
||||||
|
case ROOM_STATES.INIT:
|
||||||
|
case ROOM_STATES.JOINING:
|
||||||
case ROOM_STATES.SESSION_CONNECTED:
|
case ROOM_STATES.SESSION_CONNECTED:
|
||||||
case ROOM_STATES.JOINED:
|
case ROOM_STATES.JOINED:
|
||||||
|
case ROOM_STATES.MEDIA_WAIT:
|
||||||
// this case is so that we don't show an avatar while waiting for
|
// this case is so that we don't show an avatar while waiting for
|
||||||
// the other party to connect
|
// the other party to connect
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case ROOM_STATES.CLOSING:
|
||||||
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.warn("StandaloneRoomView.shouldRenderRemoteVideo:" +
|
console.warn("DesktopRoomConversationView.shouldRenderRemoteVideo:" +
|
||||||
" unexpected roomState: ", this.state.roomState);
|
" unexpected roomState: ", this.state.roomState);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -664,14 +664,21 @@ loop.roomViews = (function(mozL10n) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case ROOM_STATES.READY:
|
||||||
|
case ROOM_STATES.INIT:
|
||||||
|
case ROOM_STATES.JOINING:
|
||||||
case ROOM_STATES.SESSION_CONNECTED:
|
case ROOM_STATES.SESSION_CONNECTED:
|
||||||
case ROOM_STATES.JOINED:
|
case ROOM_STATES.JOINED:
|
||||||
|
case ROOM_STATES.MEDIA_WAIT:
|
||||||
// this case is so that we don't show an avatar while waiting for
|
// this case is so that we don't show an avatar while waiting for
|
||||||
// the other party to connect
|
// the other party to connect
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case ROOM_STATES.CLOSING:
|
||||||
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.warn("StandaloneRoomView.shouldRenderRemoteVideo:" +
|
console.warn("DesktopRoomConversationView.shouldRenderRemoteVideo:" +
|
||||||
" unexpected roomState: ", this.state.roomState);
|
" unexpected roomState: ", this.state.roomState);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -260,8 +260,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.standalone .local-stream,
|
.standalone .local-stream {
|
||||||
.standalone .remote-inset-stream {
|
|
||||||
/* required to have it superimposed to the control toolbar */
|
/* required to have it superimposed to the control toolbar */
|
||||||
z-index: 1001;
|
z-index: 1001;
|
||||||
}
|
}
|
||||||
@ -554,7 +553,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.local .avatar {
|
.conversation .local .avatar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
@ -647,16 +646,15 @@ html, .fx-embedded, #main,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* We use 641px rather than 640, as min-width and max-width are inclusive */
|
/* We use 641px rather than 640, as min-width and max-width are inclusive */
|
||||||
@media screen and (min-width:641px) {
|
@media screen and (min-width: 641px) {
|
||||||
.standalone .conversation-toolbar {
|
.standalone .conversation .conversation-toolbar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.standalone .local-stream,
|
.standalone .conversation .local-stream {
|
||||||
.standalone .remote-inset-stream {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 15px;
|
right: 15px;
|
||||||
bottom: 15px;
|
bottom: 15px;
|
||||||
@ -701,11 +699,7 @@ html, .fx-embedded, #main,
|
|||||||
min-height: 500px;
|
min-height: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.standalone .remote-inset-stream {
|
.standalone .conversation .local-stream {
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.standalone .local-stream {
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
min-height: 150px;
|
min-height: 150px;
|
||||||
@ -753,29 +747,42 @@ html, .fx-embedded, #main,
|
|||||||
background: #000;
|
background: #000;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
width: 75%;
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[dir="rtl"] .room-conversation-wrapper header {
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-conversation-wrapper header h1 {
|
.room-conversation-wrapper header h1 {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
text-indent: 60px;
|
text-indent: 40px;
|
||||||
background-image: url("../img/firefox-logo.png");
|
background-image: url("../img/firefox-logo.png");
|
||||||
background-size: 30px;
|
background-size: 30px;
|
||||||
background-position: 20px;
|
background-position: 0 center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[dir="rtl"] .room-conversation-wrapper header h1 {
|
||||||
|
background-position: 100% center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.room-conversation-wrapper header a {
|
.room-conversation-wrapper header a {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html[dir="rtl"] .room-conversation-wrapper header a {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
.room-conversation-wrapper header .icon-help {
|
.room-conversation-wrapper header .icon-help {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
margin-top: 20px;
|
margin-top: 15px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
background: transparent url("../img/svg/glyph-help-16x16.svg") no-repeat;
|
background: transparent url("../img/svg/glyph-help-16x16.svg") no-repeat;
|
||||||
@ -885,6 +892,7 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
|||||||
padding-left: 1em;
|
padding-left: 1em;
|
||||||
padding-right: 1em;
|
padding-right: 1em;
|
||||||
padding-bottom: 0.5em;
|
padding-bottom: 0.5em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
background-color: #E8F6FE;
|
background-color: #E8F6FE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1080,6 +1088,198 @@ html[dir="rtl"] .room-context-btn-edit {
|
|||||||
left: 20px;
|
left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media-layout {
|
||||||
|
/* 50px is the header, 3em is the footer. */
|
||||||
|
height: calc(100% - 50px - 3em);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-layout > .media-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column wrap;
|
||||||
|
/* 64px for .conversation-toolbar */
|
||||||
|
height: calc(100% - 64px);
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper > .focus-stream {
|
||||||
|
/* We want this to be the width, minus 200px which is for the right-side text
|
||||||
|
chat and video displays. */
|
||||||
|
width: calc(100% - 200px);
|
||||||
|
/* 100% height to fill up media-layout, thus forcing other elements into the
|
||||||
|
second column that's 200px wide */
|
||||||
|
height: 100%;
|
||||||
|
background-color: #4E4E4E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper > .remote {
|
||||||
|
/* Works around an issue with object-fit: cover in Google Chrome - it doesn't
|
||||||
|
currently crop but overlaps the surrounding elements.
|
||||||
|
https://code.google.com/p/chromium/issues/detail?id=400829 */
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper > .remote > .remote-video {
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: we can't use flex for the text-chat-view as this lets it overflow
|
||||||
|
the expected column heights, and we ca't fix its height. */
|
||||||
|
.media-wrapper > .text-chat-view {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
/* Text chat is a fixed 200px width for normal displays. */
|
||||||
|
width: 200px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper.showing-local-streams > .text-chat-view {
|
||||||
|
/* When we're displaying the local streams, then we need to make the text
|
||||||
|
chat view a bit shorter to give room. */
|
||||||
|
height: calc(100% - 150px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper.showing-local-streams.receiving-screen-share > .text-chat-view {
|
||||||
|
/* When we're displaying the local streams, then we need to make the text
|
||||||
|
chat view a bit shorter to give room. */
|
||||||
|
height: calc(100% - 300px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper > .text-chat-view > .text-chat-entries {
|
||||||
|
/* 40px is the height of .text-chat-box. */
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper > .local {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
width: 200px;
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper.receiving-screen-share > .screen {
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper.receiving-screen-share > .text-chat-view {
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper.receiving-screen-share > .remote {
|
||||||
|
order: 3;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
width: 200px;
|
||||||
|
height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper.receiving-screen-share > .local {
|
||||||
|
order: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width:640px) {
|
||||||
|
.media-layout {
|
||||||
|
/* 50px is height of header, 25px is height of footer. */
|
||||||
|
height: calc(100% - 50px - 25px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-layout > .media-wrapper {
|
||||||
|
flex-direction: row;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
/* conversation toolbar is 38px in narrow mode */
|
||||||
|
height: calc(100% - 38px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper > .focus-stream {
|
||||||
|
width: 100%;
|
||||||
|
/* A reasonable height */
|
||||||
|
height: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper.receiving-screen-share > .focus-stream {
|
||||||
|
height: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper > .text-chat-view > .text-chat-entries {
|
||||||
|
/* 40px is the height of .text-chat-box. */
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper > .local {
|
||||||
|
/* Position over the remote video */
|
||||||
|
position: absolute;
|
||||||
|
/* Make sure its on top */
|
||||||
|
z-index: 1001;
|
||||||
|
margin: 3px;
|
||||||
|
right: 0;
|
||||||
|
/* 29px is (30% of 50px high header) + (height toolbar (38px) +
|
||||||
|
height footer (25px) - height header (50px)) */
|
||||||
|
bottom: calc(30% + 29px);
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[dir="rtl"] .media-wrapper > .local {
|
||||||
|
right: auto;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper > .text-chat-view {
|
||||||
|
order: 3;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper > .text-chat-view,
|
||||||
|
.media-wrapper.showing-local-streams > .text-chat-view,
|
||||||
|
.media-wrapper.showing-local-streams.receiving-screen-share > .text-chat-view {
|
||||||
|
/* The remaining 30% that the .focus-stream doesn't use. */
|
||||||
|
height: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper.receiving-screen-share > .screen {
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper.receiving-screen-share > .remote {
|
||||||
|
/* Screen shares have remote & local video side-by-side on narrow screens */
|
||||||
|
order: 2;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
height: 20%;
|
||||||
|
/* Ensure no previously specified widths take effect, and we take up no more
|
||||||
|
than half the width. */
|
||||||
|
width: auto;
|
||||||
|
max-width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper.receiving-screen-share > .remote > .remote-video {
|
||||||
|
/* Reset the object-fit for this. */
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper.receiving-screen-share > .local {
|
||||||
|
/* Screen shares have remote & local video side-by-side on narrow screens */
|
||||||
|
order: 3;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
height: 20%;
|
||||||
|
/* Ensure no previously specified widths take effect, and we take up no more
|
||||||
|
than half the width. */
|
||||||
|
width: auto;
|
||||||
|
max-width: 50%;
|
||||||
|
/* This cancels out the absolute positioning when it's just remote video. */
|
||||||
|
position: relative;
|
||||||
|
bottom: auto;
|
||||||
|
right: auto;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-wrapper.receiving-screen-share > .text-chat-view {
|
||||||
|
order: 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.standalone > #main > .room-conversation-wrapper > .media-layout > .conversation-toolbar {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Standalone rooms */
|
/* Standalone rooms */
|
||||||
|
|
||||||
.standalone .room-conversation-wrapper {
|
.standalone .room-conversation-wrapper {
|
||||||
@ -1108,6 +1308,11 @@ html[dir="rtl"] .room-context-btn-edit {
|
|||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html[dir="rtl"] .standalone .room-conversation-wrapper .room-inner-info-area {
|
||||||
|
right: 25%;
|
||||||
|
left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.standalone .prompt-media-message {
|
.standalone .prompt-media-message {
|
||||||
padding-top: 136px; /* Fallback for browsers that don't support calc() */
|
padding-top: 136px; /* Fallback for browsers that don't support calc() */
|
||||||
/* 122px is 2x the intrinsic height of the background-image, and
|
/* 122px is 2x the intrinsic height of the background-image, and
|
||||||
@ -1153,23 +1358,6 @@ html[dir="rtl"] .room-context-btn-edit {
|
|||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.standalone-room-info {
|
|
||||||
position: absolute;
|
|
||||||
display: block;
|
|
||||||
top: 0;
|
|
||||||
right: 10px;
|
|
||||||
/* 20px is 10px for left and right margins. */
|
|
||||||
width: calc(25% - 20px);
|
|
||||||
z-index: 2000000;
|
|
||||||
font-size: 1.2em;
|
|
||||||
padding: .4em;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.standalone-room-info > h2 {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.standalone-context-url {
|
.standalone-context-url {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
/* Try and keep clear of local video */
|
/* Try and keep clear of local video */
|
||||||
@ -1243,7 +1431,7 @@ html[dir="rtl"] .room-context-btn-edit {
|
|||||||
padding: .7em .5em 0;
|
padding: .7em .5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fx-embedded .text-chat-box {
|
.text-chat-box {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
max-height: 40px;
|
max-height: 40px;
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
@ -1309,20 +1497,6 @@ html[dir="rtl"] .room-context-btn-edit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width:640px) {
|
@media screen and (max-width:640px) {
|
||||||
.standalone-room-info {
|
|
||||||
/* This isn't perfect, we just center the heading for now. Bug 1141493
|
|
||||||
should fix this. */
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
right: 0px;
|
|
||||||
|
|
||||||
/* Override the 100% specified in the .standalone-room-info selector
|
|
||||||
block so that this div doesn't take over the _whole_ screen and
|
|
||||||
transparently occlude UI widgetry (like the Join button), making
|
|
||||||
it unusable. */
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.standalone-context-url {
|
.standalone-context-url {
|
||||||
/* XXX We haven't got UX for standalone yet, so temporarily not displaying
|
/* XXX We haven't got UX for standalone yet, so temporarily not displaying
|
||||||
on narrow window widths. See bug 1153827. */
|
on narrow window widths. See bug 1153827. */
|
||||||
@ -1333,9 +1507,7 @@ html[dir="rtl"] .room-context-btn-edit {
|
|||||||
.standalone .room-conversation {
|
.standalone .room-conversation {
|
||||||
background: #000;
|
background: #000;
|
||||||
}
|
}
|
||||||
.room-conversation-wrapper header {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.standalone .room-conversation-wrapper .room-inner-info-area {
|
.standalone .room-conversation-wrapper .room-inner-info-area {
|
||||||
right: 0;
|
right: 0;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
@ -1357,7 +1529,7 @@ html[dir="rtl"] .room-context-btn-edit {
|
|||||||
height: 38px;
|
height: 38px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
.standalone .focus-stream {
|
.standalone .conversation .focus-stream {
|
||||||
/* Set at maximum height, minus height of conversation toolbar */
|
/* Set at maximum height, minus height of conversation toolbar */
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
@ -1418,8 +1590,6 @@ html[dir="rtl"] .room-context-btn-edit {
|
|||||||
.remote-video {
|
.remote-video {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.screen-share-video {
|
.screen-share-video {
|
||||||
|
@ -169,6 +169,7 @@ loop.shared.actions = (function() {
|
|||||||
* Used to notify that the session has a data channel available.
|
* Used to notify that the session has a data channel available.
|
||||||
*/
|
*/
|
||||||
DataChannelsAvailable: Action.define("dataChannelsAvailable", {
|
DataChannelsAvailable: Action.define("dataChannelsAvailable", {
|
||||||
|
available: Boolean
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -287,16 +287,8 @@ loop.shared.mixins = (function() {
|
|||||||
* elements and handling updates of the media containers.
|
* elements and handling updates of the media containers.
|
||||||
*/
|
*/
|
||||||
var MediaSetupMixin = {
|
var MediaSetupMixin = {
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
this.resetDimensionsCache();
|
this.resetDimensionsCache();
|
||||||
rootObject.addEventListener("orientationchange", this.updateVideoContainer);
|
|
||||||
rootObject.addEventListener("resize", this.updateVideoContainer);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
rootObject.removeEventListener("orientationchange", this.updateVideoContainer);
|
|
||||||
rootObject.removeEventListener("resize", this.updateVideoContainer);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -274,6 +274,10 @@ loop.OTSdkDriver = (function() {
|
|||||||
disconnectSession: function() {
|
disconnectSession: function() {
|
||||||
this.endScreenShare();
|
this.endScreenShare();
|
||||||
|
|
||||||
|
this.dispatcher.dispatch(new sharedActions.DataChannelsAvailable({
|
||||||
|
available: false
|
||||||
|
}));
|
||||||
|
|
||||||
if (this.session) {
|
if (this.session) {
|
||||||
this.session.off("sessionDisconnected streamCreated streamDestroyed connectionCreated connectionDestroyed streamPropertyChanged");
|
this.session.off("sessionDisconnected streamCreated streamDestroyed connectionCreated connectionDestroyed streamPropertyChanged");
|
||||||
this.session.disconnect();
|
this.session.disconnect();
|
||||||
@ -295,6 +299,8 @@ loop.OTSdkDriver = (function() {
|
|||||||
delete this._publishedLocalStream;
|
delete this._publishedLocalStream;
|
||||||
delete this._subscribedRemoteStream;
|
delete this._subscribedRemoteStream;
|
||||||
delete this._mockPublisherEl;
|
delete this._mockPublisherEl;
|
||||||
|
delete this._publisherChannel;
|
||||||
|
delete this._subscriberChannel;
|
||||||
this.connections = {};
|
this.connections = {};
|
||||||
this._setTwoWayMediaStartTime(this.CONNECTION_START_TIME_UNINITIALIZED);
|
this._setTwoWayMediaStartTime(this.CONNECTION_START_TIME_UNINITIALIZED);
|
||||||
},
|
},
|
||||||
@ -723,7 +729,9 @@ loop.OTSdkDriver = (function() {
|
|||||||
*/
|
*/
|
||||||
_checkDataChannelsAvailable: function() {
|
_checkDataChannelsAvailable: function() {
|
||||||
if (this._publisherChannel && this._subscriberChannel) {
|
if (this._publisherChannel && this._subscriberChannel) {
|
||||||
this.dispatcher.dispatch(new sharedActions.DataChannelsAvailable());
|
this.dispatcher.dispatch(new sharedActions.DataChannelsAvailable({
|
||||||
|
available: true
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -821,6 +829,10 @@ loop.OTSdkDriver = (function() {
|
|||||||
this._notifyMetricsEvent("Session.streamDestroyed");
|
this._notifyMetricsEvent("Session.streamDestroyed");
|
||||||
|
|
||||||
if (event.stream.videoType !== "screen") {
|
if (event.stream.videoType !== "screen") {
|
||||||
|
this.dispatcher.dispatch(new sharedActions.DataChannelsAvailable({
|
||||||
|
available: false
|
||||||
|
}));
|
||||||
|
delete this._subscriberChannel;
|
||||||
delete this._mockSubscribeEl;
|
delete this._mockSubscribeEl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -839,6 +851,10 @@ loop.OTSdkDriver = (function() {
|
|||||||
*/
|
*/
|
||||||
_onLocalStreamDestroyed: function() {
|
_onLocalStreamDestroyed: function() {
|
||||||
this._notifyMetricsEvent("Publisher.streamDestroyed");
|
this._notifyMetricsEvent("Publisher.streamDestroyed");
|
||||||
|
this.dispatcher.dispatch(new sharedActions.DataChannelsAvailable({
|
||||||
|
available: false
|
||||||
|
}));
|
||||||
|
delete this._publisherChannel;
|
||||||
delete this._mockPublisherEl;
|
delete this._mockPublisherEl;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
var loop = loop || {};
|
var loop = loop || {};
|
||||||
loop.store = loop.store || {};
|
loop.store = loop.store || {};
|
||||||
|
|
||||||
loop.store.TextChatStore = (function(mozL10n) {
|
loop.store.TextChatStore = (function() {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var sharedActions = loop.shared.actions;
|
var sharedActions = loop.shared.actions;
|
||||||
@ -69,10 +69,15 @@ loop.store.TextChatStore = (function(mozL10n) {
|
|||||||
/**
|
/**
|
||||||
* Handles information for when data channels are available - enables
|
* Handles information for when data channels are available - enables
|
||||||
* text chat.
|
* text chat.
|
||||||
|
*
|
||||||
|
* @param {sharedActions.DataChannelsAvailable} actionData
|
||||||
*/
|
*/
|
||||||
dataChannelsAvailable: function() {
|
dataChannelsAvailable: function(actionData) {
|
||||||
this.setStoreState({ textChatEnabled: true });
|
this.setStoreState({ textChatEnabled: actionData.available });
|
||||||
window.dispatchEvent(new CustomEvent("LoopChatEnabled"));
|
|
||||||
|
if (actionData.available) {
|
||||||
|
window.dispatchEvent(new CustomEvent("LoopChatEnabled"));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,13 +142,15 @@ loop.store.TextChatStore = (function(mozL10n) {
|
|||||||
updateRoomInfo: function(actionData) {
|
updateRoomInfo: function(actionData) {
|
||||||
// XXX When we add special messages to desktop, we'll need to not post
|
// XXX When we add special messages to desktop, we'll need to not post
|
||||||
// multiple changes of room name, only the first. Bug 1171940 should fix this.
|
// multiple changes of room name, only the first. Bug 1171940 should fix this.
|
||||||
this._appendTextChatMessage(CHAT_MESSAGE_TYPES.SPECIAL, {
|
if (actionData.roomName) {
|
||||||
contentType: CHAT_CONTENT_TYPES.ROOM_NAME,
|
this._appendTextChatMessage(CHAT_MESSAGE_TYPES.SPECIAL, {
|
||||||
message: mozL10n.get("rooms_welcome_title", {conversationName: actionData.roomName})
|
contentType: CHAT_CONTENT_TYPES.ROOM_NAME,
|
||||||
});
|
message: actionData.roomName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Append the context if we have any.
|
// Append the context if we have any.
|
||||||
if ("urls" in actionData && actionData.urls.length) {
|
if (("urls" in actionData) && actionData.urls && actionData.urls.length) {
|
||||||
// We only support the first url at the moment.
|
// We only support the first url at the moment.
|
||||||
var urlData = actionData.urls[0];
|
var urlData = actionData.urls[0];
|
||||||
|
|
||||||
@ -160,4 +167,4 @@ loop.store.TextChatStore = (function(mozL10n) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return TextChatStore;
|
return TextChatStore;
|
||||||
})(navigator.mozL10n || window.mozL10n);
|
})();
|
||||||
|
@ -39,6 +39,22 @@ loop.shared.views.TextChatView = (function(mozL10n) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var TextChatRoomName = React.createClass({displayName: "TextChatRoomName",
|
||||||
|
mixins: [React.addons.PureRenderMixin],
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
message: React.PropTypes.string.isRequired
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
React.createElement("div", {className: "text-chat-entry special room-name"},
|
||||||
|
React.createElement("p", null, mozL10n.get("rooms_welcome_title", {conversationName: this.props.message}))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the text entries in the chat entries view. This is split out from
|
* Manages the text entries in the chat entries view. This is split out from
|
||||||
* TextChatView so that scrolling can be managed more efficiently - this
|
* TextChatView so that scrolling can be managed more efficiently - this
|
||||||
@ -81,21 +97,28 @@ loop.shared.views.TextChatView = (function(mozL10n) {
|
|||||||
React.createElement("div", {className: "text-chat-scroller"},
|
React.createElement("div", {className: "text-chat-scroller"},
|
||||||
|
|
||||||
this.props.messageList.map(function(entry, i) {
|
this.props.messageList.map(function(entry, i) {
|
||||||
if (entry.type === CHAT_MESSAGE_TYPES.SPECIAL &&
|
if (entry.type === CHAT_MESSAGE_TYPES.SPECIAL) {
|
||||||
entry.contentType === CHAT_CONTENT_TYPES.CONTEXT) {
|
switch (entry.contentType) {
|
||||||
return (
|
case CHAT_CONTENT_TYPES.ROOM_NAME:
|
||||||
React.createElement("div", {className: "context-url-view-wrapper"},
|
return React.createElement(TextChatRoomName, {message: entry.message});
|
||||||
React.createElement(sharedViews.ContextUrlView, {
|
case CHAT_CONTENT_TYPES.CONTEXT:
|
||||||
allowClick: true,
|
return (
|
||||||
description: entry.message,
|
React.createElement("div", {className: "context-url-view-wrapper"},
|
||||||
dispatcher: this.props.dispatcher,
|
React.createElement(sharedViews.ContextUrlView, {
|
||||||
key: i,
|
allowClick: true,
|
||||||
showContextTitle: true,
|
description: entry.message,
|
||||||
thumbnail: entry.extraData.thumbnail,
|
dispatcher: this.props.dispatcher,
|
||||||
url: entry.extraData.location,
|
key: i,
|
||||||
useDesktopPaths: false})
|
showContextTitle: true,
|
||||||
)
|
thumbnail: entry.extraData.thumbnail,
|
||||||
);
|
url: entry.extraData.location,
|
||||||
|
useDesktopPaths: false})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
console.error("Unsupported contentType", entry.contentType);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -158,6 +181,11 @@ loop.shared.views.TextChatView = (function(mozL10n) {
|
|||||||
handleFormSubmit: function(event) {
|
handleFormSubmit: function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
// Don't send empty messages.
|
||||||
|
if (!this.state.messageDetail) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.props.dispatcher.dispatch(new sharedActions.SendTextChatMessage({
|
this.props.dispatcher.dispatch(new sharedActions.SendTextChatMessage({
|
||||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||||
message: this.state.messageDetail
|
message: this.state.messageDetail
|
||||||
|
@ -39,6 +39,22 @@ loop.shared.views.TextChatView = (function(mozL10n) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var TextChatRoomName = React.createClass({
|
||||||
|
mixins: [React.addons.PureRenderMixin],
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
message: React.PropTypes.string.isRequired
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<div className="text-chat-entry special room-name">
|
||||||
|
<p>{mozL10n.get("rooms_welcome_title", {conversationName: this.props.message})}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the text entries in the chat entries view. This is split out from
|
* Manages the text entries in the chat entries view. This is split out from
|
||||||
* TextChatView so that scrolling can be managed more efficiently - this
|
* TextChatView so that scrolling can be managed more efficiently - this
|
||||||
@ -81,21 +97,28 @@ loop.shared.views.TextChatView = (function(mozL10n) {
|
|||||||
<div className="text-chat-scroller">
|
<div className="text-chat-scroller">
|
||||||
{
|
{
|
||||||
this.props.messageList.map(function(entry, i) {
|
this.props.messageList.map(function(entry, i) {
|
||||||
if (entry.type === CHAT_MESSAGE_TYPES.SPECIAL &&
|
if (entry.type === CHAT_MESSAGE_TYPES.SPECIAL) {
|
||||||
entry.contentType === CHAT_CONTENT_TYPES.CONTEXT) {
|
switch (entry.contentType) {
|
||||||
return (
|
case CHAT_CONTENT_TYPES.ROOM_NAME:
|
||||||
<div className="context-url-view-wrapper">
|
return <TextChatRoomName message={entry.message}/>;
|
||||||
<sharedViews.ContextUrlView
|
case CHAT_CONTENT_TYPES.CONTEXT:
|
||||||
allowClick={true}
|
return (
|
||||||
description={entry.message}
|
<div className="context-url-view-wrapper">
|
||||||
dispatcher={this.props.dispatcher}
|
<sharedViews.ContextUrlView
|
||||||
key={i}
|
allowClick={true}
|
||||||
showContextTitle={true}
|
description={entry.message}
|
||||||
thumbnail={entry.extraData.thumbnail}
|
dispatcher={this.props.dispatcher}
|
||||||
url={entry.extraData.location}
|
key={i}
|
||||||
useDesktopPaths={false} />
|
showContextTitle={true}
|
||||||
</div>
|
thumbnail={entry.extraData.thumbnail}
|
||||||
);
|
url={entry.extraData.location}
|
||||||
|
useDesktopPaths={false} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
console.error("Unsupported contentType", entry.contentType);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -158,6 +181,11 @@ loop.shared.views.TextChatView = (function(mozL10n) {
|
|||||||
handleFormSubmit: function(event) {
|
handleFormSubmit: function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
// Don't send empty messages.
|
||||||
|
if (!this.state.messageDetail) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.props.dispatcher.dispatch(new sharedActions.SendTextChatMessage({
|
this.props.dispatcher.dispatch(new sharedActions.SendTextChatMessage({
|
||||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||||
message: this.state.messageDetail
|
message: this.state.messageDetail
|
||||||
|
@ -633,6 +633,15 @@ loop.shared.views = (function(_, l10n) {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
componentWillReceiveProps: function(nextProps) {
|
||||||
|
// Only change the state if the prop has changed, and if it is also
|
||||||
|
// different from the state.
|
||||||
|
if (this.props.checked !== nextProps.checked &&
|
||||||
|
this.state.checked !== nextProps.checked) {
|
||||||
|
this.setState({ checked: nextProps.checked });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
checked: this.props.checked,
|
checked: this.props.checked,
|
||||||
|
@ -633,6 +633,15 @@ loop.shared.views = (function(_, l10n) {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
componentWillReceiveProps: function(nextProps) {
|
||||||
|
// Only change the state if the prop has changed, and if it is also
|
||||||
|
// different from the state.
|
||||||
|
if (this.props.checked !== nextProps.checked &&
|
||||||
|
this.state.checked !== nextProps.checked) {
|
||||||
|
this.setState({ checked: nextProps.checked });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
checked: this.props.checked,
|
checked: this.props.checked,
|
||||||
|
@ -129,7 +129,7 @@ body,
|
|||||||
|
|
||||||
.rooms-footer {
|
.rooms-footer {
|
||||||
background: #000;
|
background: #000;
|
||||||
margin: 0 20px;
|
margin: 0 10px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
height: 3em;
|
height: 3em;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -354,34 +354,16 @@ p.standalone-btn-label {
|
|||||||
right: 35%;
|
right: 35%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html[dir="rtl"] .standalone .room-conversation-wrapper .ended-conversation .feedback {
|
||||||
|
right: auto;
|
||||||
|
left: 35%;
|
||||||
|
}
|
||||||
|
|
||||||
.standalone .ended-conversation .local-stream {
|
.standalone .ended-conversation .local-stream {
|
||||||
/* Hide local media stream when feedback form is shown. */
|
/* Hide local media stream when feedback form is shown. */
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The .text-chat-* styles are very temporarily whilst we work on text chat
|
|
||||||
* (bug 1108892 and dependencies).
|
|
||||||
*/
|
|
||||||
.text-chat-view {
|
|
||||||
height: 60px;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-chat-entries {
|
|
||||||
/* XXX Should use flex, this is just for the initial implementation. */
|
|
||||||
height: calc(100% - 2em);
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-chat-box {
|
|
||||||
width: 30%;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-chat-box > form > input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width:640px) {
|
@media screen and (max-width:640px) {
|
||||||
.standalone .ended-conversation .feedback {
|
.standalone .ended-conversation .feedback {
|
||||||
width: 92%;
|
width: 92%;
|
||||||
|
@ -229,106 +229,12 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var StandaloneRoomContextItem = React.createClass({displayName: "StandaloneRoomContextItem",
|
|
||||||
propTypes: {
|
|
||||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
|
||||||
receivingScreenShare: React.PropTypes.bool,
|
|
||||||
roomContextUrl: React.PropTypes.object
|
|
||||||
},
|
|
||||||
|
|
||||||
recordClick: function() {
|
|
||||||
this.props.dispatcher.dispatch(new sharedActions.RecordClick({
|
|
||||||
linkInfo: "Shared URL"
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
if (!this.props.roomContextUrl ||
|
|
||||||
!this.props.roomContextUrl.location) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var locationInfo = sharedUtils.formatURL(this.props.roomContextUrl.location);
|
|
||||||
if (!locationInfo) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cx = React.addons.classSet;
|
|
||||||
|
|
||||||
var classes = cx({
|
|
||||||
"standalone-context-url": true,
|
|
||||||
"screen-share-active": this.props.receivingScreenShare
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
React.createElement("div", {className: classes},
|
|
||||||
React.createElement("img", {src: this.props.roomContextUrl.thumbnail || "shared/img/icons-16x16.svg#globe"}),
|
|
||||||
React.createElement("div", {className: "standalone-context-url-description-wrapper"},
|
|
||||||
this.props.roomContextUrl.description,
|
|
||||||
React.createElement("br", null), React.createElement("a", {href: locationInfo.location,
|
|
||||||
onClick: this.recordClick,
|
|
||||||
target: "_blank",
|
|
||||||
title: locationInfo.location}, locationInfo.hostname)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var StandaloneRoomContextView = React.createClass({displayName: "StandaloneRoomContextView",
|
|
||||||
propTypes: {
|
|
||||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
|
||||||
receivingScreenShare: React.PropTypes.bool.isRequired,
|
|
||||||
roomContextUrls: React.PropTypes.array,
|
|
||||||
roomName: React.PropTypes.string,
|
|
||||||
roomInfoFailure: React.PropTypes.string
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
failureLogged: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
_logFailure: function(message) {
|
|
||||||
if (!this.state.failureLogged) {
|
|
||||||
console.error(mozL10n.get(message));
|
|
||||||
this.state.failureLogged = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
// For failures, we currently just log the messages - UX doesn't want them
|
|
||||||
// displayed on primary UI at the moment.
|
|
||||||
if (this.props.roomInfoFailure === ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED) {
|
|
||||||
this._logFailure("room_information_failure_unsupported_browser");
|
|
||||||
return null;
|
|
||||||
} else if (this.props.roomInfoFailure) {
|
|
||||||
this._logFailure("room_information_failure_not_available");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We only support one item in the context Urls array for now.
|
|
||||||
var roomContextUrl = (this.props.roomContextUrls &&
|
|
||||||
this.props.roomContextUrls.length > 0) ?
|
|
||||||
this.props.roomContextUrls[0] : null;
|
|
||||||
return (
|
|
||||||
React.createElement("div", {className: "standalone-room-info"},
|
|
||||||
React.createElement("h2", {className: "room-name"}, this.props.roomName),
|
|
||||||
React.createElement(StandaloneRoomContextItem, {
|
|
||||||
dispatcher: this.props.dispatcher,
|
|
||||||
receivingScreenShare: this.props.receivingScreenShare,
|
|
||||||
roomContextUrl: roomContextUrl})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var StandaloneRoomView = React.createClass({displayName: "StandaloneRoomView",
|
var StandaloneRoomView = React.createClass({displayName: "StandaloneRoomView",
|
||||||
mixins: [
|
mixins: [
|
||||||
Backbone.Events,
|
Backbone.Events,
|
||||||
sharedMixins.MediaSetupMixin,
|
sharedMixins.MediaSetupMixin,
|
||||||
sharedMixins.RoomsAudioMixin
|
sharedMixins.RoomsAudioMixin,
|
||||||
|
loop.store.StoreMixin("activeRoomStore")
|
||||||
],
|
],
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
@ -352,32 +258,11 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
|
||||||
this.listenTo(this.props.activeRoomStore, "change",
|
|
||||||
this._onActiveRoomStateChanged);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles a "change" event on the roomStore, and updates this.state
|
|
||||||
* to match the store.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_onActiveRoomStateChanged: function() {
|
|
||||||
var state = this.props.activeRoomStore.getStoreState();
|
|
||||||
this.updateVideoDimensions(state.localVideoDimensions, state.remoteVideoDimensions);
|
|
||||||
this.setState(state);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
// Adding a class to the document body element from here to ease styling it.
|
// Adding a class to the document body element from here to ease styling it.
|
||||||
document.body.classList.add("is-standalone-room");
|
document.body.classList.add("is-standalone-room");
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
this.stopListening(this.props.activeRoomStore);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Watches for when we transition to MEDIA_WAIT room state, so we can request
|
* Watches for when we transition to MEDIA_WAIT room state, so we can request
|
||||||
* user media access.
|
* user media access.
|
||||||
@ -393,30 +278,16 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.roomState !== ROOM_STATES.JOINED &&
|
// UX don't want to surface these errors (as they would imply the user
|
||||||
nextState.roomState === ROOM_STATES.JOINED) {
|
// needs to do something to fix them, when if they're having a conversation
|
||||||
// This forces the video size to update - creating the publisher
|
// they just need to connect). However, we do want there to be somewhere to
|
||||||
// first, and then connecting to the session doesn't seem to set the
|
// find reasonably easily, in case there's issues raised.
|
||||||
// initial size correctly.
|
if (!this.state.roomInfoFailure && nextState.roomInfoFailure) {
|
||||||
this.updateVideoContainer();
|
if (nextState.roomInfoFailure === ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED) {
|
||||||
}
|
console.error(mozL10n.get("room_information_failure_unsupported_browser"));
|
||||||
|
} else {
|
||||||
if (nextState.roomState === ROOM_STATES.INIT ||
|
console.error(mozL10n.get("room_information_failure_not_available"));
|
||||||
nextState.roomState === ROOM_STATES.GATHER ||
|
}
|
||||||
nextState.roomState === ROOM_STATES.READY) {
|
|
||||||
this.resetDimensionsCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
// When screen sharing stops.
|
|
||||||
if (this.state.receivingScreenShare && !nextState.receivingScreenShare) {
|
|
||||||
// Remove the custom screenshare styles on the remote camera.
|
|
||||||
var node = this._getElement(".remote");
|
|
||||||
node.removeAttribute("style");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.receivingScreenShare != nextState.receivingScreenShare ||
|
|
||||||
this.state.remoteVideoEnabled != nextState.remoteVideoEnabled) {
|
|
||||||
this.updateVideoContainer();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -428,32 +299,6 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
this.props.dispatcher.dispatch(new sharedActions.LeaveRoom());
|
this.props.dispatcher.dispatch(new sharedActions.LeaveRoom());
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper for window.matchMedia so that we use an appropriate version
|
|
||||||
* for the ui-showcase, which puts views inside of their own iframes.
|
|
||||||
*
|
|
||||||
* Currently, we use an icky hack, and the showcase conspires with
|
|
||||||
* react-frame-component to set iframe.contentWindow.matchMedia onto
|
|
||||||
* activeRoomStore. Once React context matures a bit (somewhere between
|
|
||||||
* 0.14 and 1.0, apparently):
|
|
||||||
*
|
|
||||||
* https://facebook.github.io/react/blog/2015/02/24/streamlining-react-elements.html#solution-make-context-parent-based-instead-of-owner-based
|
|
||||||
*
|
|
||||||
* we should be able to use those to clean this up.
|
|
||||||
*
|
|
||||||
* @param queryString
|
|
||||||
* @returns {MediaQueryList|null}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_matchMedia: function(queryString) {
|
|
||||||
if ("matchMedia" in this.state) {
|
|
||||||
return this.state.matchMedia(queryString);
|
|
||||||
} else if ("matchMedia" in window) {
|
|
||||||
return window.matchMedia(queryString);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles streaming status for a given stream type.
|
* Toggles streaming status for a given stream type.
|
||||||
*
|
*
|
||||||
@ -467,131 +312,6 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifically updates the local camera stream size and position, depending
|
|
||||||
* on the size and position of the remote video stream.
|
|
||||||
* This method gets called from `updateVideoContainer`, which is defined in
|
|
||||||
* the `MediaSetupMixin`.
|
|
||||||
*
|
|
||||||
* @param {Object} ratio Aspect ratio of the local camera stream
|
|
||||||
*/
|
|
||||||
updateLocalCameraPosition: function(ratio) {
|
|
||||||
// The local stream is a quarter of the remote stream.
|
|
||||||
var LOCAL_STREAM_SIZE = 0.25;
|
|
||||||
// The local stream overlaps the remote stream by a quarter of the local stream.
|
|
||||||
var LOCAL_STREAM_OVERLAP = 0.25;
|
|
||||||
// The minimum size of video height/width allowed by the sdk css.
|
|
||||||
var SDK_MIN_SIZE = 48;
|
|
||||||
|
|
||||||
var node = this._getElement(".local");
|
|
||||||
var targetWidth;
|
|
||||||
|
|
||||||
node.style.right = "auto";
|
|
||||||
if (this._matchMedia("screen and (max-width:640px)").matches) {
|
|
||||||
// For reduced screen widths, we just go for a fixed size and no overlap.
|
|
||||||
targetWidth = 180;
|
|
||||||
node.style.width = (targetWidth * ratio.width) + "px";
|
|
||||||
node.style.height = (targetWidth * ratio.height) + "px";
|
|
||||||
node.style.left = "auto";
|
|
||||||
} else {
|
|
||||||
// The local camera view should be a quarter of the size of the remote stream
|
|
||||||
// and positioned to overlap with the remote stream at a quarter of its width.
|
|
||||||
|
|
||||||
// Now position the local camera view correctly with respect to the remote
|
|
||||||
// video stream or the screen share stream.
|
|
||||||
var remoteVideoDimensions;
|
|
||||||
var isScreenShare = this.state.receivingScreenShare;
|
|
||||||
var videoDisplayed = isScreenShare ?
|
|
||||||
this.state.screenShareVideoObject || this.props.screenSharePosterUrl :
|
|
||||||
this.state.remoteSrcVideoObject || this.props.remotePosterUrl;
|
|
||||||
|
|
||||||
if ((isScreenShare || this.shouldRenderRemoteVideo()) && videoDisplayed) {
|
|
||||||
remoteVideoDimensions = this.getRemoteVideoDimensions(
|
|
||||||
isScreenShare ? "screen" : "camera");
|
|
||||||
} else {
|
|
||||||
var remoteElement = this.getDOMNode().querySelector(".remote.focus-stream");
|
|
||||||
if (!remoteElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
remoteVideoDimensions = {
|
|
||||||
streamWidth: remoteElement.offsetWidth,
|
|
||||||
offsetX: remoteElement.offsetLeft
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
targetWidth = remoteVideoDimensions.streamWidth * LOCAL_STREAM_SIZE;
|
|
||||||
|
|
||||||
var realWidth = targetWidth * ratio.width;
|
|
||||||
var realHeight = targetWidth * ratio.height;
|
|
||||||
|
|
||||||
// If we've hit the min size limits, then limit at the minimum.
|
|
||||||
if (realWidth < SDK_MIN_SIZE) {
|
|
||||||
realWidth = SDK_MIN_SIZE;
|
|
||||||
realHeight = realWidth / ratio.width * ratio.height;
|
|
||||||
}
|
|
||||||
if (realHeight < SDK_MIN_SIZE) {
|
|
||||||
realHeight = SDK_MIN_SIZE;
|
|
||||||
realWidth = realHeight / ratio.height * ratio.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
var offsetX = (remoteVideoDimensions.streamWidth + remoteVideoDimensions.offsetX);
|
|
||||||
// The horizontal offset of the stream, and the width of the resulting
|
|
||||||
// pillarbox, is determined by the height exponent of the aspect ratio.
|
|
||||||
// Therefore we multiply the width of the local camera view by the height
|
|
||||||
// ratio.
|
|
||||||
node.style.left = (offsetX - (realWidth * LOCAL_STREAM_OVERLAP)) + "px";
|
|
||||||
node.style.width = realWidth + "px";
|
|
||||||
node.style.height = realHeight + "px";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifically updates the remote camera stream size and position, if
|
|
||||||
* a screen share is being received. It is slaved from the position of the
|
|
||||||
* local stream.
|
|
||||||
* This method gets called from `updateVideoContainer`, which is defined in
|
|
||||||
* the `MediaSetupMixin`.
|
|
||||||
*
|
|
||||||
* @param {Object} ratio Aspect ratio of the remote camera stream
|
|
||||||
*/
|
|
||||||
updateRemoteCameraPosition: function(ratio) {
|
|
||||||
// Nothing to do for screenshare
|
|
||||||
if (!this.state.receivingScreenShare) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// XXX For the time being, if we're a narrow screen, aka mobile, we don't display
|
|
||||||
// the remote media (bug 1133534).
|
|
||||||
if (this._matchMedia("screen and (max-width:640px)").matches) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 10px separation between the two streams.
|
|
||||||
var LOCAL_REMOTE_SEPARATION = 10;
|
|
||||||
|
|
||||||
var node = this._getElement(".remote");
|
|
||||||
var localNode = this._getElement(".local");
|
|
||||||
|
|
||||||
// Match the width to the local video.
|
|
||||||
node.style.width = localNode.offsetWidth + "px";
|
|
||||||
|
|
||||||
// The height is then determined from the aspect ratio
|
|
||||||
var height = ((localNode.offsetWidth / ratio.width) * ratio.height);
|
|
||||||
node.style.height = height + "px";
|
|
||||||
|
|
||||||
node.style.right = "auto";
|
|
||||||
node.style.bottom = "auto";
|
|
||||||
|
|
||||||
// Now position the local camera view correctly with respect to the remote
|
|
||||||
// video stream.
|
|
||||||
|
|
||||||
// The top is measured from the top of the element down the screen,
|
|
||||||
// so subtract the height of the video and the separation distance.
|
|
||||||
node.style.top = (localNode.offsetTop - height - LOCAL_REMOTE_SEPARATION) + "px";
|
|
||||||
|
|
||||||
// Match the left-hand sides.
|
|
||||||
node.style.left = localNode.offsetLeft + "px";
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if current room is active.
|
* Checks if current room is active.
|
||||||
*
|
*
|
||||||
@ -647,34 +367,29 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var localStreamClasses = React.addons.classSet({
|
var displayScreenShare = this.state.receivingScreenShare ||
|
||||||
local: true,
|
this.props.screenSharePosterUrl;
|
||||||
"local-stream": true,
|
|
||||||
"local-stream-audio": this.state.videoMuted
|
|
||||||
});
|
|
||||||
|
|
||||||
var remoteStreamClasses = React.addons.classSet({
|
var remoteStreamClasses = React.addons.classSet({
|
||||||
"video_inner": true,
|
|
||||||
"remote": true,
|
"remote": true,
|
||||||
"focus-stream": !this.state.receivingScreenShare,
|
"focus-stream": !displayScreenShare
|
||||||
"remote-inset-stream": this.state.receivingScreenShare
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var screenShareStreamClasses = React.addons.classSet({
|
var screenShareStreamClasses = React.addons.classSet({
|
||||||
"screen": true,
|
"screen": true,
|
||||||
"focus-stream": this.state.receivingScreenShare,
|
"focus-stream": displayScreenShare
|
||||||
hide: !this.state.receivingScreenShare
|
});
|
||||||
|
|
||||||
|
var mediaWrapperClasses = React.addons.classSet({
|
||||||
|
"media-wrapper": true,
|
||||||
|
"receiving-screen-share": displayScreenShare,
|
||||||
|
"showing-local-streams": this.state.localSrcVideoObject ||
|
||||||
|
this.props.localPosterUrl
|
||||||
});
|
});
|
||||||
|
|
||||||
// XXX Temporarily showAlways = showRoomName = false for TextChatView
|
|
||||||
// until bug 1168829 is completed.
|
|
||||||
return (
|
return (
|
||||||
React.createElement("div", {className: "room-conversation-wrapper"},
|
React.createElement("div", {className: "room-conversation-wrapper"},
|
||||||
React.createElement("div", {className: "beta-logo"}),
|
React.createElement("div", {className: "beta-logo"}),
|
||||||
React.createElement(sharedViews.TextChatView, {
|
|
||||||
dispatcher: this.props.dispatcher,
|
|
||||||
showAlways: false,
|
|
||||||
showRoomName: false}),
|
|
||||||
React.createElement(StandaloneRoomHeader, {dispatcher: this.props.dispatcher}),
|
React.createElement(StandaloneRoomHeader, {dispatcher: this.props.dispatcher}),
|
||||||
React.createElement(StandaloneRoomInfoArea, {roomState: this.state.roomState,
|
React.createElement(StandaloneRoomInfoArea, {roomState: this.state.roomState,
|
||||||
failureReason: this.state.failureReason,
|
failureReason: this.state.failureReason,
|
||||||
@ -682,50 +397,44 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
isFirefox: this.props.isFirefox,
|
isFirefox: this.props.isFirefox,
|
||||||
activeRoomStore: this.props.activeRoomStore,
|
activeRoomStore: this.props.activeRoomStore,
|
||||||
roomUsed: this.state.used}),
|
roomUsed: this.state.used}),
|
||||||
React.createElement("div", {className: "video-layout-wrapper"},
|
React.createElement("div", {className: "media-layout"},
|
||||||
React.createElement("div", {className: "conversation room-conversation"},
|
React.createElement("div", {className: mediaWrapperClasses},
|
||||||
React.createElement(StandaloneRoomContextView, {
|
React.createElement("span", {className: "self-view-hidden-message"},
|
||||||
dispatcher: this.props.dispatcher,
|
mozL10n.get("self_view_hidden_message")
|
||||||
receivingScreenShare: this.state.receivingScreenShare,
|
|
||||||
roomContextUrls: this.state.roomContextUrls,
|
|
||||||
roomName: this.state.roomName,
|
|
||||||
roomInfoFailure: this.state.roomInfoFailure}),
|
|
||||||
React.createElement("div", {className: "media nested"},
|
|
||||||
React.createElement("span", {className: "self-view-hidden-message"},
|
|
||||||
mozL10n.get("self_view_hidden_message")
|
|
||||||
),
|
|
||||||
React.createElement("div", {className: "video_wrapper remote_wrapper"},
|
|
||||||
React.createElement("div", {className: remoteStreamClasses},
|
|
||||||
React.createElement(sharedViews.MediaView, {displayAvatar: !this.shouldRenderRemoteVideo(),
|
|
||||||
posterUrl: this.props.remotePosterUrl,
|
|
||||||
mediaType: "remote",
|
|
||||||
srcVideoObject: this.state.remoteSrcVideoObject})
|
|
||||||
),
|
|
||||||
React.createElement("div", {className: screenShareStreamClasses},
|
|
||||||
React.createElement(sharedViews.MediaView, {displayAvatar: false,
|
|
||||||
posterUrl: this.props.screenSharePosterUrl,
|
|
||||||
mediaType: "screen-share",
|
|
||||||
srcVideoObject: this.state.screenShareVideoObject})
|
|
||||||
)
|
|
||||||
),
|
|
||||||
React.createElement("div", {className: localStreamClasses},
|
|
||||||
React.createElement(sharedViews.MediaView, {displayAvatar: this.state.videoMuted,
|
|
||||||
posterUrl: this.props.localPosterUrl,
|
|
||||||
mediaType: "local",
|
|
||||||
srcVideoObject: this.state.localSrcVideoObject})
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
React.createElement(sharedViews.ConversationToolbar, {
|
React.createElement("div", {className: remoteStreamClasses},
|
||||||
|
React.createElement(sharedViews.MediaView, {displayAvatar: !this.shouldRenderRemoteVideo(),
|
||||||
|
posterUrl: this.props.remotePosterUrl,
|
||||||
|
mediaType: "remote",
|
||||||
|
srcVideoObject: this.state.remoteSrcVideoObject})
|
||||||
|
),
|
||||||
|
React.createElement("div", {className: screenShareStreamClasses},
|
||||||
|
React.createElement(sharedViews.MediaView, {displayAvatar: false,
|
||||||
|
posterUrl: this.props.screenSharePosterUrl,
|
||||||
|
mediaType: "screen-share",
|
||||||
|
srcVideoObject: this.state.screenShareVideoObject})
|
||||||
|
),
|
||||||
|
React.createElement(sharedViews.TextChatView, {
|
||||||
dispatcher: this.props.dispatcher,
|
dispatcher: this.props.dispatcher,
|
||||||
video: {enabled: !this.state.videoMuted,
|
showAlways: true,
|
||||||
visible: this._roomIsActive()},
|
showRoomName: true}),
|
||||||
audio: {enabled: !this.state.audioMuted,
|
React.createElement("div", {className: "local"},
|
||||||
visible: this._roomIsActive()},
|
React.createElement(sharedViews.MediaView, {displayAvatar: this.state.videoMuted,
|
||||||
publishStream: this.publishStream,
|
posterUrl: this.props.localPosterUrl,
|
||||||
hangup: this.leaveRoom,
|
mediaType: "local",
|
||||||
hangupButtonLabel: mozL10n.get("rooms_leave_button_label"),
|
srcVideoObject: this.state.localSrcVideoObject})
|
||||||
enableHangup: this._roomIsActive()})
|
)
|
||||||
)
|
),
|
||||||
|
React.createElement(sharedViews.ConversationToolbar, {
|
||||||
|
dispatcher: this.props.dispatcher,
|
||||||
|
video: {enabled: !this.state.videoMuted,
|
||||||
|
visible: this._roomIsActive()},
|
||||||
|
audio: {enabled: !this.state.audioMuted,
|
||||||
|
visible: this._roomIsActive()},
|
||||||
|
publishStream: this.publishStream,
|
||||||
|
hangup: this.leaveRoom,
|
||||||
|
hangupButtonLabel: mozL10n.get("rooms_leave_button_label"),
|
||||||
|
enableHangup: this._roomIsActive()})
|
||||||
),
|
),
|
||||||
React.createElement(loop.fxOSMarketplaceViews.FxOSHiddenMarketplaceView, {
|
React.createElement(loop.fxOSMarketplaceViews.FxOSHiddenMarketplaceView, {
|
||||||
marketplaceSrc: this.state.marketplaceSrc,
|
marketplaceSrc: this.state.marketplaceSrc,
|
||||||
@ -737,7 +446,6 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
StandaloneRoomContextView: StandaloneRoomContextView,
|
|
||||||
StandaloneRoomFooter: StandaloneRoomFooter,
|
StandaloneRoomFooter: StandaloneRoomFooter,
|
||||||
StandaloneRoomHeader: StandaloneRoomHeader,
|
StandaloneRoomHeader: StandaloneRoomHeader,
|
||||||
StandaloneRoomView: StandaloneRoomView
|
StandaloneRoomView: StandaloneRoomView
|
||||||
|
@ -229,106 +229,12 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var StandaloneRoomContextItem = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
|
||||||
receivingScreenShare: React.PropTypes.bool,
|
|
||||||
roomContextUrl: React.PropTypes.object
|
|
||||||
},
|
|
||||||
|
|
||||||
recordClick: function() {
|
|
||||||
this.props.dispatcher.dispatch(new sharedActions.RecordClick({
|
|
||||||
linkInfo: "Shared URL"
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
if (!this.props.roomContextUrl ||
|
|
||||||
!this.props.roomContextUrl.location) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var locationInfo = sharedUtils.formatURL(this.props.roomContextUrl.location);
|
|
||||||
if (!locationInfo) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cx = React.addons.classSet;
|
|
||||||
|
|
||||||
var classes = cx({
|
|
||||||
"standalone-context-url": true,
|
|
||||||
"screen-share-active": this.props.receivingScreenShare
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={classes}>
|
|
||||||
<img src={this.props.roomContextUrl.thumbnail || "shared/img/icons-16x16.svg#globe"} />
|
|
||||||
<div className="standalone-context-url-description-wrapper">
|
|
||||||
{this.props.roomContextUrl.description}
|
|
||||||
<br /><a href={locationInfo.location}
|
|
||||||
onClick={this.recordClick}
|
|
||||||
target="_blank"
|
|
||||||
title={locationInfo.location}>{locationInfo.hostname}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var StandaloneRoomContextView = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
|
||||||
receivingScreenShare: React.PropTypes.bool.isRequired,
|
|
||||||
roomContextUrls: React.PropTypes.array,
|
|
||||||
roomName: React.PropTypes.string,
|
|
||||||
roomInfoFailure: React.PropTypes.string
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
failureLogged: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
_logFailure: function(message) {
|
|
||||||
if (!this.state.failureLogged) {
|
|
||||||
console.error(mozL10n.get(message));
|
|
||||||
this.state.failureLogged = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
// For failures, we currently just log the messages - UX doesn't want them
|
|
||||||
// displayed on primary UI at the moment.
|
|
||||||
if (this.props.roomInfoFailure === ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED) {
|
|
||||||
this._logFailure("room_information_failure_unsupported_browser");
|
|
||||||
return null;
|
|
||||||
} else if (this.props.roomInfoFailure) {
|
|
||||||
this._logFailure("room_information_failure_not_available");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We only support one item in the context Urls array for now.
|
|
||||||
var roomContextUrl = (this.props.roomContextUrls &&
|
|
||||||
this.props.roomContextUrls.length > 0) ?
|
|
||||||
this.props.roomContextUrls[0] : null;
|
|
||||||
return (
|
|
||||||
<div className="standalone-room-info">
|
|
||||||
<h2 className="room-name">{this.props.roomName}</h2>
|
|
||||||
<StandaloneRoomContextItem
|
|
||||||
dispatcher={this.props.dispatcher}
|
|
||||||
receivingScreenShare={this.props.receivingScreenShare}
|
|
||||||
roomContextUrl={roomContextUrl} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var StandaloneRoomView = React.createClass({
|
var StandaloneRoomView = React.createClass({
|
||||||
mixins: [
|
mixins: [
|
||||||
Backbone.Events,
|
Backbone.Events,
|
||||||
sharedMixins.MediaSetupMixin,
|
sharedMixins.MediaSetupMixin,
|
||||||
sharedMixins.RoomsAudioMixin
|
sharedMixins.RoomsAudioMixin,
|
||||||
|
loop.store.StoreMixin("activeRoomStore")
|
||||||
],
|
],
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
@ -352,32 +258,11 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
|
||||||
this.listenTo(this.props.activeRoomStore, "change",
|
|
||||||
this._onActiveRoomStateChanged);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles a "change" event on the roomStore, and updates this.state
|
|
||||||
* to match the store.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_onActiveRoomStateChanged: function() {
|
|
||||||
var state = this.props.activeRoomStore.getStoreState();
|
|
||||||
this.updateVideoDimensions(state.localVideoDimensions, state.remoteVideoDimensions);
|
|
||||||
this.setState(state);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
// Adding a class to the document body element from here to ease styling it.
|
// Adding a class to the document body element from here to ease styling it.
|
||||||
document.body.classList.add("is-standalone-room");
|
document.body.classList.add("is-standalone-room");
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
this.stopListening(this.props.activeRoomStore);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Watches for when we transition to MEDIA_WAIT room state, so we can request
|
* Watches for when we transition to MEDIA_WAIT room state, so we can request
|
||||||
* user media access.
|
* user media access.
|
||||||
@ -393,30 +278,16 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.roomState !== ROOM_STATES.JOINED &&
|
// UX don't want to surface these errors (as they would imply the user
|
||||||
nextState.roomState === ROOM_STATES.JOINED) {
|
// needs to do something to fix them, when if they're having a conversation
|
||||||
// This forces the video size to update - creating the publisher
|
// they just need to connect). However, we do want there to be somewhere to
|
||||||
// first, and then connecting to the session doesn't seem to set the
|
// find reasonably easily, in case there's issues raised.
|
||||||
// initial size correctly.
|
if (!this.state.roomInfoFailure && nextState.roomInfoFailure) {
|
||||||
this.updateVideoContainer();
|
if (nextState.roomInfoFailure === ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED) {
|
||||||
}
|
console.error(mozL10n.get("room_information_failure_unsupported_browser"));
|
||||||
|
} else {
|
||||||
if (nextState.roomState === ROOM_STATES.INIT ||
|
console.error(mozL10n.get("room_information_failure_not_available"));
|
||||||
nextState.roomState === ROOM_STATES.GATHER ||
|
}
|
||||||
nextState.roomState === ROOM_STATES.READY) {
|
|
||||||
this.resetDimensionsCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
// When screen sharing stops.
|
|
||||||
if (this.state.receivingScreenShare && !nextState.receivingScreenShare) {
|
|
||||||
// Remove the custom screenshare styles on the remote camera.
|
|
||||||
var node = this._getElement(".remote");
|
|
||||||
node.removeAttribute("style");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.receivingScreenShare != nextState.receivingScreenShare ||
|
|
||||||
this.state.remoteVideoEnabled != nextState.remoteVideoEnabled) {
|
|
||||||
this.updateVideoContainer();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -428,32 +299,6 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
this.props.dispatcher.dispatch(new sharedActions.LeaveRoom());
|
this.props.dispatcher.dispatch(new sharedActions.LeaveRoom());
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper for window.matchMedia so that we use an appropriate version
|
|
||||||
* for the ui-showcase, which puts views inside of their own iframes.
|
|
||||||
*
|
|
||||||
* Currently, we use an icky hack, and the showcase conspires with
|
|
||||||
* react-frame-component to set iframe.contentWindow.matchMedia onto
|
|
||||||
* activeRoomStore. Once React context matures a bit (somewhere between
|
|
||||||
* 0.14 and 1.0, apparently):
|
|
||||||
*
|
|
||||||
* https://facebook.github.io/react/blog/2015/02/24/streamlining-react-elements.html#solution-make-context-parent-based-instead-of-owner-based
|
|
||||||
*
|
|
||||||
* we should be able to use those to clean this up.
|
|
||||||
*
|
|
||||||
* @param queryString
|
|
||||||
* @returns {MediaQueryList|null}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_matchMedia: function(queryString) {
|
|
||||||
if ("matchMedia" in this.state) {
|
|
||||||
return this.state.matchMedia(queryString);
|
|
||||||
} else if ("matchMedia" in window) {
|
|
||||||
return window.matchMedia(queryString);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles streaming status for a given stream type.
|
* Toggles streaming status for a given stream type.
|
||||||
*
|
*
|
||||||
@ -467,131 +312,6 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifically updates the local camera stream size and position, depending
|
|
||||||
* on the size and position of the remote video stream.
|
|
||||||
* This method gets called from `updateVideoContainer`, which is defined in
|
|
||||||
* the `MediaSetupMixin`.
|
|
||||||
*
|
|
||||||
* @param {Object} ratio Aspect ratio of the local camera stream
|
|
||||||
*/
|
|
||||||
updateLocalCameraPosition: function(ratio) {
|
|
||||||
// The local stream is a quarter of the remote stream.
|
|
||||||
var LOCAL_STREAM_SIZE = 0.25;
|
|
||||||
// The local stream overlaps the remote stream by a quarter of the local stream.
|
|
||||||
var LOCAL_STREAM_OVERLAP = 0.25;
|
|
||||||
// The minimum size of video height/width allowed by the sdk css.
|
|
||||||
var SDK_MIN_SIZE = 48;
|
|
||||||
|
|
||||||
var node = this._getElement(".local");
|
|
||||||
var targetWidth;
|
|
||||||
|
|
||||||
node.style.right = "auto";
|
|
||||||
if (this._matchMedia("screen and (max-width:640px)").matches) {
|
|
||||||
// For reduced screen widths, we just go for a fixed size and no overlap.
|
|
||||||
targetWidth = 180;
|
|
||||||
node.style.width = (targetWidth * ratio.width) + "px";
|
|
||||||
node.style.height = (targetWidth * ratio.height) + "px";
|
|
||||||
node.style.left = "auto";
|
|
||||||
} else {
|
|
||||||
// The local camera view should be a quarter of the size of the remote stream
|
|
||||||
// and positioned to overlap with the remote stream at a quarter of its width.
|
|
||||||
|
|
||||||
// Now position the local camera view correctly with respect to the remote
|
|
||||||
// video stream or the screen share stream.
|
|
||||||
var remoteVideoDimensions;
|
|
||||||
var isScreenShare = this.state.receivingScreenShare;
|
|
||||||
var videoDisplayed = isScreenShare ?
|
|
||||||
this.state.screenShareVideoObject || this.props.screenSharePosterUrl :
|
|
||||||
this.state.remoteSrcVideoObject || this.props.remotePosterUrl;
|
|
||||||
|
|
||||||
if ((isScreenShare || this.shouldRenderRemoteVideo()) && videoDisplayed) {
|
|
||||||
remoteVideoDimensions = this.getRemoteVideoDimensions(
|
|
||||||
isScreenShare ? "screen" : "camera");
|
|
||||||
} else {
|
|
||||||
var remoteElement = this.getDOMNode().querySelector(".remote.focus-stream");
|
|
||||||
if (!remoteElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
remoteVideoDimensions = {
|
|
||||||
streamWidth: remoteElement.offsetWidth,
|
|
||||||
offsetX: remoteElement.offsetLeft
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
targetWidth = remoteVideoDimensions.streamWidth * LOCAL_STREAM_SIZE;
|
|
||||||
|
|
||||||
var realWidth = targetWidth * ratio.width;
|
|
||||||
var realHeight = targetWidth * ratio.height;
|
|
||||||
|
|
||||||
// If we've hit the min size limits, then limit at the minimum.
|
|
||||||
if (realWidth < SDK_MIN_SIZE) {
|
|
||||||
realWidth = SDK_MIN_SIZE;
|
|
||||||
realHeight = realWidth / ratio.width * ratio.height;
|
|
||||||
}
|
|
||||||
if (realHeight < SDK_MIN_SIZE) {
|
|
||||||
realHeight = SDK_MIN_SIZE;
|
|
||||||
realWidth = realHeight / ratio.height * ratio.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
var offsetX = (remoteVideoDimensions.streamWidth + remoteVideoDimensions.offsetX);
|
|
||||||
// The horizontal offset of the stream, and the width of the resulting
|
|
||||||
// pillarbox, is determined by the height exponent of the aspect ratio.
|
|
||||||
// Therefore we multiply the width of the local camera view by the height
|
|
||||||
// ratio.
|
|
||||||
node.style.left = (offsetX - (realWidth * LOCAL_STREAM_OVERLAP)) + "px";
|
|
||||||
node.style.width = realWidth + "px";
|
|
||||||
node.style.height = realHeight + "px";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifically updates the remote camera stream size and position, if
|
|
||||||
* a screen share is being received. It is slaved from the position of the
|
|
||||||
* local stream.
|
|
||||||
* This method gets called from `updateVideoContainer`, which is defined in
|
|
||||||
* the `MediaSetupMixin`.
|
|
||||||
*
|
|
||||||
* @param {Object} ratio Aspect ratio of the remote camera stream
|
|
||||||
*/
|
|
||||||
updateRemoteCameraPosition: function(ratio) {
|
|
||||||
// Nothing to do for screenshare
|
|
||||||
if (!this.state.receivingScreenShare) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// XXX For the time being, if we're a narrow screen, aka mobile, we don't display
|
|
||||||
// the remote media (bug 1133534).
|
|
||||||
if (this._matchMedia("screen and (max-width:640px)").matches) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 10px separation between the two streams.
|
|
||||||
var LOCAL_REMOTE_SEPARATION = 10;
|
|
||||||
|
|
||||||
var node = this._getElement(".remote");
|
|
||||||
var localNode = this._getElement(".local");
|
|
||||||
|
|
||||||
// Match the width to the local video.
|
|
||||||
node.style.width = localNode.offsetWidth + "px";
|
|
||||||
|
|
||||||
// The height is then determined from the aspect ratio
|
|
||||||
var height = ((localNode.offsetWidth / ratio.width) * ratio.height);
|
|
||||||
node.style.height = height + "px";
|
|
||||||
|
|
||||||
node.style.right = "auto";
|
|
||||||
node.style.bottom = "auto";
|
|
||||||
|
|
||||||
// Now position the local camera view correctly with respect to the remote
|
|
||||||
// video stream.
|
|
||||||
|
|
||||||
// The top is measured from the top of the element down the screen,
|
|
||||||
// so subtract the height of the video and the separation distance.
|
|
||||||
node.style.top = (localNode.offsetTop - height - LOCAL_REMOTE_SEPARATION) + "px";
|
|
||||||
|
|
||||||
// Match the left-hand sides.
|
|
||||||
node.style.left = localNode.offsetLeft + "px";
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if current room is active.
|
* Checks if current room is active.
|
||||||
*
|
*
|
||||||
@ -647,34 +367,29 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var localStreamClasses = React.addons.classSet({
|
var displayScreenShare = this.state.receivingScreenShare ||
|
||||||
local: true,
|
this.props.screenSharePosterUrl;
|
||||||
"local-stream": true,
|
|
||||||
"local-stream-audio": this.state.videoMuted
|
|
||||||
});
|
|
||||||
|
|
||||||
var remoteStreamClasses = React.addons.classSet({
|
var remoteStreamClasses = React.addons.classSet({
|
||||||
"video_inner": true,
|
|
||||||
"remote": true,
|
"remote": true,
|
||||||
"focus-stream": !this.state.receivingScreenShare,
|
"focus-stream": !displayScreenShare
|
||||||
"remote-inset-stream": this.state.receivingScreenShare
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var screenShareStreamClasses = React.addons.classSet({
|
var screenShareStreamClasses = React.addons.classSet({
|
||||||
"screen": true,
|
"screen": true,
|
||||||
"focus-stream": this.state.receivingScreenShare,
|
"focus-stream": displayScreenShare
|
||||||
hide: !this.state.receivingScreenShare
|
});
|
||||||
|
|
||||||
|
var mediaWrapperClasses = React.addons.classSet({
|
||||||
|
"media-wrapper": true,
|
||||||
|
"receiving-screen-share": displayScreenShare,
|
||||||
|
"showing-local-streams": this.state.localSrcVideoObject ||
|
||||||
|
this.props.localPosterUrl
|
||||||
});
|
});
|
||||||
|
|
||||||
// XXX Temporarily showAlways = showRoomName = false for TextChatView
|
|
||||||
// until bug 1168829 is completed.
|
|
||||||
return (
|
return (
|
||||||
<div className="room-conversation-wrapper">
|
<div className="room-conversation-wrapper">
|
||||||
<div className="beta-logo" />
|
<div className="beta-logo" />
|
||||||
<sharedViews.TextChatView
|
|
||||||
dispatcher={this.props.dispatcher}
|
|
||||||
showAlways={false}
|
|
||||||
showRoomName={false} />
|
|
||||||
<StandaloneRoomHeader dispatcher={this.props.dispatcher} />
|
<StandaloneRoomHeader dispatcher={this.props.dispatcher} />
|
||||||
<StandaloneRoomInfoArea roomState={this.state.roomState}
|
<StandaloneRoomInfoArea roomState={this.state.roomState}
|
||||||
failureReason={this.state.failureReason}
|
failureReason={this.state.failureReason}
|
||||||
@ -682,50 +397,44 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
isFirefox={this.props.isFirefox}
|
isFirefox={this.props.isFirefox}
|
||||||
activeRoomStore={this.props.activeRoomStore}
|
activeRoomStore={this.props.activeRoomStore}
|
||||||
roomUsed={this.state.used} />
|
roomUsed={this.state.used} />
|
||||||
<div className="video-layout-wrapper">
|
<div className="media-layout">
|
||||||
<div className="conversation room-conversation">
|
<div className={mediaWrapperClasses}>
|
||||||
<StandaloneRoomContextView
|
<span className="self-view-hidden-message">
|
||||||
dispatcher={this.props.dispatcher}
|
{mozL10n.get("self_view_hidden_message")}
|
||||||
receivingScreenShare={this.state.receivingScreenShare}
|
</span>
|
||||||
roomContextUrls={this.state.roomContextUrls}
|
<div className={remoteStreamClasses}>
|
||||||
roomName={this.state.roomName}
|
<sharedViews.MediaView displayAvatar={!this.shouldRenderRemoteVideo()}
|
||||||
roomInfoFailure={this.state.roomInfoFailure} />
|
posterUrl={this.props.remotePosterUrl}
|
||||||
<div className="media nested">
|
mediaType="remote"
|
||||||
<span className="self-view-hidden-message">
|
srcVideoObject={this.state.remoteSrcVideoObject} />
|
||||||
{mozL10n.get("self_view_hidden_message")}
|
|
||||||
</span>
|
|
||||||
<div className="video_wrapper remote_wrapper">
|
|
||||||
<div className={remoteStreamClasses}>
|
|
||||||
<sharedViews.MediaView displayAvatar={!this.shouldRenderRemoteVideo()}
|
|
||||||
posterUrl={this.props.remotePosterUrl}
|
|
||||||
mediaType="remote"
|
|
||||||
srcVideoObject={this.state.remoteSrcVideoObject} />
|
|
||||||
</div>
|
|
||||||
<div className={screenShareStreamClasses}>
|
|
||||||
<sharedViews.MediaView displayAvatar={false}
|
|
||||||
posterUrl={this.props.screenSharePosterUrl}
|
|
||||||
mediaType="screen-share"
|
|
||||||
srcVideoObject={this.state.screenShareVideoObject} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={localStreamClasses}>
|
|
||||||
<sharedViews.MediaView displayAvatar={this.state.videoMuted}
|
|
||||||
posterUrl={this.props.localPosterUrl}
|
|
||||||
mediaType="local"
|
|
||||||
srcVideoObject={this.state.localSrcVideoObject} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<sharedViews.ConversationToolbar
|
<div className={screenShareStreamClasses}>
|
||||||
|
<sharedViews.MediaView displayAvatar={false}
|
||||||
|
posterUrl={this.props.screenSharePosterUrl}
|
||||||
|
mediaType="screen-share"
|
||||||
|
srcVideoObject={this.state.screenShareVideoObject} />
|
||||||
|
</div>
|
||||||
|
<sharedViews.TextChatView
|
||||||
dispatcher={this.props.dispatcher}
|
dispatcher={this.props.dispatcher}
|
||||||
video={{enabled: !this.state.videoMuted,
|
showAlways={true}
|
||||||
visible: this._roomIsActive()}}
|
showRoomName={true} />
|
||||||
audio={{enabled: !this.state.audioMuted,
|
<div className="local">
|
||||||
visible: this._roomIsActive()}}
|
<sharedViews.MediaView displayAvatar={this.state.videoMuted}
|
||||||
publishStream={this.publishStream}
|
posterUrl={this.props.localPosterUrl}
|
||||||
hangup={this.leaveRoom}
|
mediaType="local"
|
||||||
hangupButtonLabel={mozL10n.get("rooms_leave_button_label")}
|
srcVideoObject={this.state.localSrcVideoObject} />
|
||||||
enableHangup={this._roomIsActive()} />
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<sharedViews.ConversationToolbar
|
||||||
|
dispatcher={this.props.dispatcher}
|
||||||
|
video={{enabled: !this.state.videoMuted,
|
||||||
|
visible: this._roomIsActive()}}
|
||||||
|
audio={{enabled: !this.state.audioMuted,
|
||||||
|
visible: this._roomIsActive()}}
|
||||||
|
publishStream={this.publishStream}
|
||||||
|
hangup={this.leaveRoom}
|
||||||
|
hangupButtonLabel={mozL10n.get("rooms_leave_button_label")}
|
||||||
|
enableHangup={this._roomIsActive()} />
|
||||||
</div>
|
</div>
|
||||||
<loop.fxOSMarketplaceViews.FxOSHiddenMarketplaceView
|
<loop.fxOSMarketplaceViews.FxOSHiddenMarketplaceView
|
||||||
marketplaceSrc={this.state.marketplaceSrc}
|
marketplaceSrc={this.state.marketplaceSrc}
|
||||||
@ -737,7 +446,6 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
StandaloneRoomContextView: StandaloneRoomContextView,
|
|
||||||
StandaloneRoomFooter: StandaloneRoomFooter,
|
StandaloneRoomFooter: StandaloneRoomFooter,
|
||||||
StandaloneRoomHeader: StandaloneRoomHeader,
|
StandaloneRoomHeader: StandaloneRoomHeader,
|
||||||
StandaloneRoomView: StandaloneRoomView
|
StandaloneRoomView: StandaloneRoomView
|
||||||
|
@ -1094,6 +1094,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
loop.store.StoreMixin.register({
|
loop.store.StoreMixin.register({
|
||||||
|
activeRoomStore: activeRoomStore,
|
||||||
feedbackStore: feedbackStore,
|
feedbackStore: feedbackStore,
|
||||||
// This isn't used in any views, but is saved here to ensure it
|
// This isn't used in any views, but is saved here to ensure it
|
||||||
// is kept alive.
|
// is kept alive.
|
||||||
|
@ -1094,6 +1094,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
loop.store.StoreMixin.register({
|
loop.store.StoreMixin.register({
|
||||||
|
activeRoomStore: activeRoomStore,
|
||||||
feedbackStore: feedbackStore,
|
feedbackStore: feedbackStore,
|
||||||
// This isn't used in any views, but is saved here to ensure it
|
// This isn't used in any views, but is saved here to ensure it
|
||||||
// is kept alive.
|
// is kept alive.
|
||||||
|
@ -850,6 +850,25 @@ describe("loop.panel", function() {
|
|||||||
expect(contextContent).to.not.equal(null);
|
expect(contextContent).to.not.equal(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should cancel the checkbox when a new URL is available", function() {
|
||||||
|
fakeMozLoop.getSelectedTabMetadata = function (callback) {
|
||||||
|
callback({
|
||||||
|
url: "https://www.example.com",
|
||||||
|
description: "fake description",
|
||||||
|
previews: [""]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var view = createTestComponent();
|
||||||
|
|
||||||
|
view.setState({ checked: true });
|
||||||
|
|
||||||
|
// Simulate being visible
|
||||||
|
view.onDocumentVisible();
|
||||||
|
|
||||||
|
expect(view.state.checked).eql(false);
|
||||||
|
});
|
||||||
|
|
||||||
it("should show a default favicon when none is available", function() {
|
it("should show a default favicon when none is available", function() {
|
||||||
fakeMozLoop.getSelectedTabMetadata = function (callback) {
|
fakeMozLoop.getSelectedTabMetadata = function (callback) {
|
||||||
callback({
|
callback({
|
||||||
|
@ -411,6 +411,16 @@ describe("loop.OTSdkDriver", function () {
|
|||||||
sinon.assert.calledOnce(session.disconnect);
|
sinon.assert.calledOnce(session.disconnect);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should dispatch a DataChannelsAvailable action with available = false", function() {
|
||||||
|
driver.disconnectSession();
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||||
|
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||||
|
new sharedActions.DataChannelsAvailable({
|
||||||
|
available: false
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
it("should destroy the publisher", function() {
|
it("should destroy the publisher", function() {
|
||||||
driver.publisher = publisher;
|
driver.publisher = publisher;
|
||||||
|
|
||||||
@ -1005,7 +1015,7 @@ describe("loop.OTSdkDriver", function () {
|
|||||||
|
|
||||||
publisher.trigger("streamDestroyed");
|
publisher.trigger("streamDestroyed");
|
||||||
|
|
||||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
sinon.assert.calledTwice(dispatcher.dispatch);
|
||||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||||
new sharedActions.ConnectionStatus({
|
new sharedActions.ConnectionStatus({
|
||||||
event: "Publisher.streamDestroyed",
|
event: "Publisher.streamDestroyed",
|
||||||
@ -1015,6 +1025,16 @@ describe("loop.OTSdkDriver", function () {
|
|||||||
sendStreams: 0
|
sendStreams: 0
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should dispatch a DataChannelsAvailable action", function() {
|
||||||
|
publisher.trigger("streamDestroyed");
|
||||||
|
|
||||||
|
sinon.assert.calledTwice(dispatcher.dispatch);
|
||||||
|
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||||
|
new sharedActions.DataChannelsAvailable({
|
||||||
|
available: false
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("streamDestroyed: session/remote", function() {
|
describe("streamDestroyed: session/remote", function() {
|
||||||
@ -1054,7 +1074,7 @@ describe("loop.OTSdkDriver", function () {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not dispatch an action if the videoType is camera", function() {
|
it("should not dispatch a ConnectionStatus action if the videoType is camera", function() {
|
||||||
stream.videoType = "camera";
|
stream.videoType = "camera";
|
||||||
|
|
||||||
session.trigger("streamDestroyed", { stream: stream });
|
session.trigger("streamDestroyed", { stream: stream });
|
||||||
@ -1062,6 +1082,25 @@ describe("loop.OTSdkDriver", function () {
|
|||||||
sinon.assert.neverCalledWithMatch(dispatcher.dispatch,
|
sinon.assert.neverCalledWithMatch(dispatcher.dispatch,
|
||||||
sinon.match.hasOwn("name", "receivingScreenShare"));
|
sinon.match.hasOwn("name", "receivingScreenShare"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should dispatch a DataChannelsAvailable action for videoType = camera", function() {
|
||||||
|
stream.videoType = "camera";
|
||||||
|
|
||||||
|
session.trigger("streamDestroyed", { stream: stream });
|
||||||
|
|
||||||
|
sinon.assert.calledTwice(dispatcher.dispatch);
|
||||||
|
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||||
|
new sharedActions.DataChannelsAvailable({
|
||||||
|
available: false
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not dispatch a DataChannelsAvailable action for videoType = screen", function() {
|
||||||
|
session.trigger("streamDestroyed", { stream: stream });
|
||||||
|
|
||||||
|
sinon.assert.neverCalledWithMatch(dispatcher.dispatch,
|
||||||
|
sinon.match.hasOwn("name", "dataChannelsAvailable"));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("streamPropertyChanged", function() {
|
describe("streamPropertyChanged", function() {
|
||||||
@ -1297,7 +1336,9 @@ describe("loop.OTSdkDriver", function () {
|
|||||||
|
|
||||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||||
new sharedActions.DataChannelsAvailable());
|
new sharedActions.DataChannelsAvailable({
|
||||||
|
available: true
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should dispatch `ReceivedTextChatMessage` when a text message is received", function() {
|
it("should dispatch `ReceivedTextChatMessage` when a text message is received", function() {
|
||||||
|
@ -37,19 +37,31 @@ describe("loop.store.TextChatStore", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("#dataChannelsAvailable", function() {
|
describe("#dataChannelsAvailable", function() {
|
||||||
it("should set textChatEnabled to true", function() {
|
it("should set textChatEnabled to the supplied state", function() {
|
||||||
store.dataChannelsAvailable();
|
store.dataChannelsAvailable(new sharedActions.DataChannelsAvailable({
|
||||||
|
available: true
|
||||||
|
}));
|
||||||
|
|
||||||
expect(store.getStoreState("textChatEnabled")).eql(true);
|
expect(store.getStoreState("textChatEnabled")).eql(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should dispatch a LoopChatEnabled event", function() {
|
it("should dispatch a LoopChatEnabled event", function() {
|
||||||
store.dataChannelsAvailable();
|
store.dataChannelsAvailable(new sharedActions.DataChannelsAvailable({
|
||||||
|
available: true
|
||||||
|
}));
|
||||||
|
|
||||||
sinon.assert.calledOnce(window.dispatchEvent);
|
sinon.assert.calledOnce(window.dispatchEvent);
|
||||||
sinon.assert.calledWithExactly(window.dispatchEvent,
|
sinon.assert.calledWithExactly(window.dispatchEvent,
|
||||||
new CustomEvent("LoopChatEnabled"));
|
new CustomEvent("LoopChatEnabled"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should not dispatch a LoopChatEnabled event if available is false", function() {
|
||||||
|
store.dataChannelsAvailable(new sharedActions.DataChannelsAvailable({
|
||||||
|
available: false
|
||||||
|
}));
|
||||||
|
|
||||||
|
sinon.assert.notCalled(window.dispatchEvent);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#receivedTextChatMessage", function() {
|
describe("#receivedTextChatMessage", function() {
|
||||||
@ -133,8 +145,6 @@ describe("loop.store.TextChatStore", function () {
|
|||||||
|
|
||||||
describe("#updateRoomInfo", function() {
|
describe("#updateRoomInfo", function() {
|
||||||
it("should add the room name to the list", function() {
|
it("should add the room name to the list", function() {
|
||||||
sandbox.stub(navigator.mozL10n, "get").returns("Let's really share!");
|
|
||||||
|
|
||||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||||
roomName: "Let's share!",
|
roomName: "Let's share!",
|
||||||
roomOwner: "Mark",
|
roomOwner: "Mark",
|
||||||
@ -144,14 +154,12 @@ describe("loop.store.TextChatStore", function () {
|
|||||||
expect(store.getStoreState("messageList")).eql([{
|
expect(store.getStoreState("messageList")).eql([{
|
||||||
type: CHAT_MESSAGE_TYPES.SPECIAL,
|
type: CHAT_MESSAGE_TYPES.SPECIAL,
|
||||||
contentType: CHAT_CONTENT_TYPES.ROOM_NAME,
|
contentType: CHAT_CONTENT_TYPES.ROOM_NAME,
|
||||||
message: "Let's really share!",
|
message: "Let's share!",
|
||||||
extraData: undefined
|
extraData: undefined
|
||||||
}]);
|
}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should add the context to the list", function() {
|
it("should add the context to the list", function() {
|
||||||
sandbox.stub(navigator.mozL10n, "get").returns("Let's really share!");
|
|
||||||
|
|
||||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||||
roomName: "Let's share!",
|
roomName: "Let's share!",
|
||||||
roomOwner: "Mark",
|
roomOwner: "Mark",
|
||||||
@ -167,7 +175,7 @@ describe("loop.store.TextChatStore", function () {
|
|||||||
{
|
{
|
||||||
type: CHAT_MESSAGE_TYPES.SPECIAL,
|
type: CHAT_MESSAGE_TYPES.SPECIAL,
|
||||||
contentType: CHAT_CONTENT_TYPES.ROOM_NAME,
|
contentType: CHAT_CONTENT_TYPES.ROOM_NAME,
|
||||||
message: "Let's really share!",
|
message: "Let's share!",
|
||||||
extraData: undefined
|
extraData: undefined
|
||||||
}, {
|
}, {
|
||||||
type: CHAT_MESSAGE_TYPES.SPECIAL,
|
type: CHAT_MESSAGE_TYPES.SPECIAL,
|
||||||
@ -182,8 +190,6 @@ describe("loop.store.TextChatStore", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should not dispatch a LoopChatMessageAppended event", function() {
|
it("should not dispatch a LoopChatMessageAppended event", function() {
|
||||||
sandbox.stub(navigator.mozL10n, "get").returns("Let's really share!");
|
|
||||||
|
|
||||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||||
roomName: "Let's share!",
|
roomName: "Let's share!",
|
||||||
roomOwner: "Mark",
|
roomOwner: "Mark",
|
||||||
|
@ -174,5 +174,18 @@ describe("loop.shared.views.TextChatView", function () {
|
|||||||
message: "Hello!"
|
message: "Hello!"
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should not dispatch SendTextChatMessage when the message is empty", function() {
|
||||||
|
view = mountTestComponent();
|
||||||
|
|
||||||
|
var entryNode = view.getDOMNode().querySelector(".text-chat-box > form > input");
|
||||||
|
|
||||||
|
TestUtils.Simulate.keyDown(entryNode, {
|
||||||
|
key: "Enter",
|
||||||
|
which: 13
|
||||||
|
});
|
||||||
|
|
||||||
|
sinon.assert.notCalled(dispatcher.dispatch);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -765,6 +765,26 @@ describe("loop.shared.views", function() {
|
|||||||
expect(node.classList.contains("disabled")).to.eql(true);
|
expect(node.classList.contains("disabled")).to.eql(true);
|
||||||
expect(node.hasAttribute("disabled")).to.eql(true);
|
expect(node.hasAttribute("disabled")).to.eql(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render the checkbox as checked when the prop is set", function() {
|
||||||
|
view = mountTestComponent({
|
||||||
|
checked: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var checkbox = view.getDOMNode().querySelector(".checkbox");
|
||||||
|
expect(checkbox.classList.contains("checked")).eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should alter the render state when the props are changed", function() {
|
||||||
|
view = mountTestComponent({
|
||||||
|
checked: true
|
||||||
|
});
|
||||||
|
|
||||||
|
view.setProps({checked: false});
|
||||||
|
|
||||||
|
var checkbox = view.getDOMNode().querySelector(".checkbox");
|
||||||
|
expect(checkbox.classList.contains("checked")).eql(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#_handleClick", function() {
|
describe("#_handleClick", function() {
|
||||||
|
@ -31,6 +31,7 @@ describe("loop.standaloneRoomViews", function() {
|
|||||||
feedbackClient: {}
|
feedbackClient: {}
|
||||||
});
|
});
|
||||||
loop.store.StoreMixin.register({
|
loop.store.StoreMixin.register({
|
||||||
|
activeRoomStore: activeRoomStore,
|
||||||
feedbackStore: feedbackStore,
|
feedbackStore: feedbackStore,
|
||||||
textChatStore: textChatStore
|
textChatStore: textChatStore
|
||||||
});
|
});
|
||||||
@ -45,126 +46,6 @@ describe("loop.standaloneRoomViews", function() {
|
|||||||
sandbox.restore();
|
sandbox.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("StandaloneRoomContextView", function() {
|
|
||||||
beforeEach(function() {
|
|
||||||
sandbox.stub(navigator.mozL10n, "get").returnsArg(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
function mountTestComponent(extraProps) {
|
|
||||||
var props = _.extend({
|
|
||||||
dispatcher: dispatcher,
|
|
||||||
receivingScreenShare: false
|
|
||||||
}, extraProps);
|
|
||||||
return TestUtils.renderIntoDocument(
|
|
||||||
React.createElement(
|
|
||||||
loop.standaloneRoomViews.StandaloneRoomContextView, props));
|
|
||||||
}
|
|
||||||
|
|
||||||
it("should display the room name if no failures are known", function() {
|
|
||||||
var view = mountTestComponent({
|
|
||||||
roomName: "Mike's room",
|
|
||||||
receivingScreenShare: false
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(view.getDOMNode().textContent).eql("Mike's room");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should log an unsupported browser message if crypto is unsupported", function() {
|
|
||||||
var view = mountTestComponent({
|
|
||||||
roomName: "Mark's room",
|
|
||||||
roomInfoFailure: ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED
|
|
||||||
});
|
|
||||||
|
|
||||||
sinon.assert.called(console.error);
|
|
||||||
sinon.assert.calledWithMatch(console.error, sinon.match("unsupported"));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should display a general error message for any other failure", function() {
|
|
||||||
var view = mountTestComponent({
|
|
||||||
roomName: "Mark's room",
|
|
||||||
roomInfoFailure: ROOM_INFO_FAILURES.NO_DATA
|
|
||||||
});
|
|
||||||
|
|
||||||
sinon.assert.called(console.error);
|
|
||||||
sinon.assert.calledWithMatch(console.error, sinon.match("not_available"));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should display context information if a url is supplied", function() {
|
|
||||||
var view = mountTestComponent({
|
|
||||||
roomName: "Mike's room",
|
|
||||||
roomContextUrls: [{
|
|
||||||
description: "Mark's super page",
|
|
||||||
location: "http://invalid.com",
|
|
||||||
thumbnail: "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(view.getDOMNode().querySelector(".standalone-context-url")).not.eql(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should format the url for display", function() {
|
|
||||||
sandbox.stub(sharedUtils, "formatURL").returns({
|
|
||||||
location: "location",
|
|
||||||
hostname: "hostname"
|
|
||||||
});
|
|
||||||
|
|
||||||
var view = mountTestComponent({
|
|
||||||
roomName: "Mike's room",
|
|
||||||
roomContextUrls: [{
|
|
||||||
description: "Mark's super page",
|
|
||||||
location: "http://invalid.com",
|
|
||||||
thumbnail: "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(view.getDOMNode()
|
|
||||||
.querySelector(".standalone-context-url-description-wrapper > a").textContent)
|
|
||||||
.eql("hostname");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not display context information if no urls are supplied", function() {
|
|
||||||
var view = mountTestComponent({
|
|
||||||
roomName: "Mike's room"
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(view.getDOMNode().querySelector(".standalone-context-url")).eql(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should dispatch a RecordClick action when the link is clicked", function() {
|
|
||||||
var view = mountTestComponent({
|
|
||||||
roomName: "Mark's room",
|
|
||||||
roomContextUrls: [{
|
|
||||||
description: "Mark's super page",
|
|
||||||
location: "http://invalid.com",
|
|
||||||
thumbnail: "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
TestUtils.Simulate.click(view.getDOMNode()
|
|
||||||
.querySelector(".standalone-context-url-description-wrapper > a"));
|
|
||||||
|
|
||||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
|
||||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
|
||||||
new sharedActions.RecordClick({
|
|
||||||
linkInfo: "Shared URL"
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should display the default favicon when no thumbnail is available", function() {
|
|
||||||
var view = mountTestComponent({
|
|
||||||
roomName: "Mike's room",
|
|
||||||
roomContextUrls: [{
|
|
||||||
description: "Mark's super page",
|
|
||||||
location: "http://invalid.com",
|
|
||||||
thumbnail: ""
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(view.getDOMNode().querySelector(".standalone-context-url > img").src)
|
|
||||||
.to.match(/shared\/img\/icons-16x16.svg#globe$/);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("StandaloneRoomHeader", function() {
|
describe("StandaloneRoomHeader", function() {
|
||||||
function mountTestComponent() {
|
function mountTestComponent() {
|
||||||
return TestUtils.renderIntoDocument(
|
return TestUtils.renderIntoDocument(
|
||||||
@ -224,43 +105,6 @@ describe("loop.standaloneRoomViews", function() {
|
|||||||
|
|
||||||
expectActionDispatched(view);
|
expectActionDispatched(view);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should updateVideoContainer when the JOINED state is entered", function() {
|
|
||||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
|
|
||||||
|
|
||||||
var view = mountTestComponent();
|
|
||||||
|
|
||||||
sandbox.stub(view, "updateVideoContainer");
|
|
||||||
|
|
||||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
|
|
||||||
|
|
||||||
sinon.assert.calledOnce(view.updateVideoContainer);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should updateVideoContainer when the JOINED state is re-entered", function() {
|
|
||||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED});
|
|
||||||
|
|
||||||
var view = mountTestComponent();
|
|
||||||
|
|
||||||
sandbox.stub(view, "updateVideoContainer");
|
|
||||||
|
|
||||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
|
|
||||||
|
|
||||||
sinon.assert.calledOnce(view.updateVideoContainer);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should reset the video dimensions cache when the gather state is entered", function() {
|
|
||||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.SESSION_CONNECTED});
|
|
||||||
|
|
||||||
var view = mountTestComponent();
|
|
||||||
|
|
||||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.GATHER});
|
|
||||||
|
|
||||||
expect(view._videoDimensionsCache).eql({
|
|
||||||
local: {},
|
|
||||||
remote: {}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#publishStream", function() {
|
describe("#publishStream", function() {
|
||||||
@ -297,252 +141,6 @@ describe("loop.standaloneRoomViews", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Local Stream Size Position", function() {
|
|
||||||
var view, localElement;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
sandbox.stub(window, "matchMedia").returns({
|
|
||||||
matches: false
|
|
||||||
});
|
|
||||||
activeRoomStore.setStoreState({
|
|
||||||
remoteSrcVideoObject: {},
|
|
||||||
remoteVideoEnabled: true
|
|
||||||
});
|
|
||||||
view = mountTestComponent();
|
|
||||||
localElement = view._getElement(".local");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should be a quarter of the width of the main stream", function() {
|
|
||||||
sandbox.stub(view, "getRemoteVideoDimensions").returns({
|
|
||||||
streamWidth: 640,
|
|
||||||
offsetX: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
view.updateLocalCameraPosition({
|
|
||||||
width: 1,
|
|
||||||
height: 0.75
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(localElement.style.width).eql("160px");
|
|
||||||
expect(localElement.style.height).eql("120px");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should be a quarter of the width of the remote view element when there is no stream", function() {
|
|
||||||
activeRoomStore.setStoreState({
|
|
||||||
remoteSrcVideoObject: null,
|
|
||||||
remoteVideoEnabled: false
|
|
||||||
});
|
|
||||||
|
|
||||||
sandbox.stub(view, "getDOMNode").returns({
|
|
||||||
querySelector: function(selector) {
|
|
||||||
if (selector === ".local") {
|
|
||||||
return localElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
offsetWidth: 640,
|
|
||||||
offsetLeft: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
view.updateLocalCameraPosition({
|
|
||||||
width: 1,
|
|
||||||
height: 0.75
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(localElement.style.width).eql("160px");
|
|
||||||
expect(localElement.style.height).eql("120px");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should be a quarter of the width reduced for aspect ratio", function() {
|
|
||||||
sandbox.stub(view, "getRemoteVideoDimensions").returns({
|
|
||||||
streamWidth: 640,
|
|
||||||
offsetX: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
view.updateLocalCameraPosition({
|
|
||||||
width: 0.75,
|
|
||||||
height: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(localElement.style.width).eql("120px");
|
|
||||||
expect(localElement.style.height).eql("160px");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should ensure the height is a minimum of 48px", function() {
|
|
||||||
sandbox.stub(view, "getRemoteVideoDimensions").returns({
|
|
||||||
streamWidth: 180,
|
|
||||||
offsetX: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
view.updateLocalCameraPosition({
|
|
||||||
width: 1,
|
|
||||||
height: 0.75
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(localElement.style.width).eql("64px");
|
|
||||||
expect(localElement.style.height).eql("48px");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should ensure the width is a minimum of 48px", function() {
|
|
||||||
sandbox.stub(view, "getRemoteVideoDimensions").returns({
|
|
||||||
streamWidth: 180,
|
|
||||||
offsetX: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
view.updateLocalCameraPosition({
|
|
||||||
width: 0.75,
|
|
||||||
height: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(localElement.style.width).eql("48px");
|
|
||||||
expect(localElement.style.height).eql("64px");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should position the stream to overlap the main stream by a quarter", function() {
|
|
||||||
sandbox.stub(view, "getRemoteVideoDimensions").returns({
|
|
||||||
streamWidth: 640,
|
|
||||||
offsetX: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
view.updateLocalCameraPosition({
|
|
||||||
width: 1,
|
|
||||||
height: 0.75
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(localElement.style.width).eql("160px");
|
|
||||||
expect(localElement.style.left).eql("600px");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should position the stream to overlap the remote view element when there is no stream", function() {
|
|
||||||
activeRoomStore.setStoreState({
|
|
||||||
remoteSrcVideoObject: null,
|
|
||||||
remoteVideoEnabled: false
|
|
||||||
});
|
|
||||||
|
|
||||||
sandbox.stub(view, "getDOMNode").returns({
|
|
||||||
querySelector: function(selector) {
|
|
||||||
if (selector === ".local") {
|
|
||||||
return localElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
offsetWidth: 640,
|
|
||||||
offsetLeft: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
view.updateLocalCameraPosition({
|
|
||||||
width: 1,
|
|
||||||
height: 0.75
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(localElement.style.width).eql("160px");
|
|
||||||
expect(localElement.style.left).eql("600px");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should position the stream to overlap the main stream by a quarter when the aspect ratio is vertical", function() {
|
|
||||||
sandbox.stub(view, "getRemoteVideoDimensions").returns({
|
|
||||||
streamWidth: 640,
|
|
||||||
offsetX: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
view.updateLocalCameraPosition({
|
|
||||||
width: 0.75,
|
|
||||||
height: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(localElement.style.width).eql("120px");
|
|
||||||
expect(localElement.style.left).eql("610px");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Remote Stream Size Position", function() {
|
|
||||||
var view, localElement, remoteElement;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
sandbox.stub(window, "matchMedia").returns({
|
|
||||||
matches: false
|
|
||||||
});
|
|
||||||
view = mountTestComponent();
|
|
||||||
|
|
||||||
localElement = {
|
|
||||||
style: {}
|
|
||||||
};
|
|
||||||
remoteElement = {
|
|
||||||
style: {},
|
|
||||||
removeAttribute: sinon.spy()
|
|
||||||
};
|
|
||||||
|
|
||||||
sandbox.stub(view, "_getElement", function(className) {
|
|
||||||
return className === ".local" ? localElement : remoteElement;
|
|
||||||
});
|
|
||||||
|
|
||||||
view.setState({"receivingScreenShare": true});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should do nothing if not receiving screenshare", function() {
|
|
||||||
view.setState({"receivingScreenShare": false});
|
|
||||||
remoteElement.style.width = "10px";
|
|
||||||
|
|
||||||
view.updateRemoteCameraPosition({
|
|
||||||
width: 1,
|
|
||||||
height: 0.75
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(remoteElement.style.width).eql("10px");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should be the same width as the local video", function() {
|
|
||||||
localElement.offsetWidth = 100;
|
|
||||||
|
|
||||||
view.updateRemoteCameraPosition({
|
|
||||||
width: 1,
|
|
||||||
height: 0.75
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(remoteElement.style.width).eql("100px");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should be the same left edge as the local video", function() {
|
|
||||||
localElement.offsetLeft = 50;
|
|
||||||
|
|
||||||
view.updateRemoteCameraPosition({
|
|
||||||
width: 1,
|
|
||||||
height: 0.75
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(remoteElement.style.left).eql("50px");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have a height determined by the aspect ratio", function() {
|
|
||||||
localElement.offsetWidth = 100;
|
|
||||||
|
|
||||||
view.updateRemoteCameraPosition({
|
|
||||||
width: 1,
|
|
||||||
height: 0.75
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(remoteElement.style.height).eql("75px");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have the top be set such that the bottom is 10px above the local video", function() {
|
|
||||||
localElement.offsetWidth = 100;
|
|
||||||
localElement.offsetTop = 200;
|
|
||||||
|
|
||||||
view.updateRemoteCameraPosition({
|
|
||||||
width: 1,
|
|
||||||
height: 0.75
|
|
||||||
});
|
|
||||||
|
|
||||||
// 200 (top) - 75 (height) - 10 (spacing) = 115
|
|
||||||
expect(remoteElement.style.top).eql("115px");
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("#render", function() {
|
describe("#render", function() {
|
||||||
var view;
|
var view;
|
||||||
|
|
||||||
@ -827,14 +425,14 @@ describe("loop.standaloneRoomViews", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("Mute", function() {
|
describe("Mute", function() {
|
||||||
it("should render local media as audio-only if video is muted",
|
it("should render a local avatar if video is muted",
|
||||||
function() {
|
function() {
|
||||||
activeRoomStore.setStoreState({
|
activeRoomStore.setStoreState({
|
||||||
roomState: ROOM_STATES.SESSION_CONNECTED,
|
roomState: ROOM_STATES.SESSION_CONNECTED,
|
||||||
videoMuted: true
|
videoMuted: true
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(view.getDOMNode().querySelector(".local-stream-audio"))
|
expect(view.getDOMNode().querySelector(".local .avatar"))
|
||||||
.not.eql(null);
|
.not.eql(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -270,6 +270,7 @@
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
loop.store.StoreMixin.register({
|
loop.store.StoreMixin.register({
|
||||||
|
activeRoomStore: activeRoomStore,
|
||||||
conversationStore: conversationStore,
|
conversationStore: conversationStore,
|
||||||
feedbackStore: feedbackStore,
|
feedbackStore: feedbackStore,
|
||||||
textChatStore: textChatStore
|
textChatStore: textChatStore
|
||||||
@ -971,6 +972,21 @@
|
|||||||
localPosterUrl: "sample-img/video-screen-local.png",
|
localPosterUrl: "sample-img/video-screen-local.png",
|
||||||
remotePosterUrl: "sample-img/video-screen-remote.png"})
|
remotePosterUrl: "sample-img/video-screen-remote.png"})
|
||||||
)
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
React.createElement(FramedExample, {width: 600, height: 480,
|
||||||
|
onContentsRendered: updatingSharingRoomStore.forcedUpdate,
|
||||||
|
summary: "Standalone room convo (has-participants, receivingScreenShare, 600x480)"},
|
||||||
|
React.createElement("div", {className: "standalone", cssClass: "standalone"},
|
||||||
|
React.createElement(StandaloneRoomView, {
|
||||||
|
dispatcher: dispatcher,
|
||||||
|
activeRoomStore: updatingSharingRoomStore,
|
||||||
|
roomState: ROOM_STATES.HAS_PARTICIPANTS,
|
||||||
|
isFirefox: true,
|
||||||
|
localPosterUrl: "sample-img/video-screen-local.png",
|
||||||
|
remotePosterUrl: "sample-img/video-screen-remote.png",
|
||||||
|
screenSharePosterUrl: "sample-img/video-screen-terminal.png"})
|
||||||
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
|
@ -270,6 +270,7 @@
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
loop.store.StoreMixin.register({
|
loop.store.StoreMixin.register({
|
||||||
|
activeRoomStore: activeRoomStore,
|
||||||
conversationStore: conversationStore,
|
conversationStore: conversationStore,
|
||||||
feedbackStore: feedbackStore,
|
feedbackStore: feedbackStore,
|
||||||
textChatStore: textChatStore
|
textChatStore: textChatStore
|
||||||
@ -972,6 +973,21 @@
|
|||||||
remotePosterUrl="sample-img/video-screen-remote.png" />
|
remotePosterUrl="sample-img/video-screen-remote.png" />
|
||||||
</div>
|
</div>
|
||||||
</FramedExample>
|
</FramedExample>
|
||||||
|
|
||||||
|
<FramedExample width={600} height={480}
|
||||||
|
onContentsRendered={updatingSharingRoomStore.forcedUpdate}
|
||||||
|
summary="Standalone room convo (has-participants, receivingScreenShare, 600x480)">
|
||||||
|
<div className="standalone" cssClass="standalone">
|
||||||
|
<StandaloneRoomView
|
||||||
|
dispatcher={dispatcher}
|
||||||
|
activeRoomStore={updatingSharingRoomStore}
|
||||||
|
roomState={ROOM_STATES.HAS_PARTICIPANTS}
|
||||||
|
isFirefox={true}
|
||||||
|
localPosterUrl="sample-img/video-screen-local.png"
|
||||||
|
remotePosterUrl="sample-img/video-screen-remote.png"
|
||||||
|
screenSharePosterUrl="sample-img/video-screen-terminal.png" />
|
||||||
|
</div>
|
||||||
|
</FramedExample>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section name="TextChatView (standalone)">
|
<Section name="TextChatView (standalone)">
|
||||||
|
@ -14,6 +14,7 @@ let state = {windows:[{tabs:[
|
|||||||
|
|
||||||
function test() {
|
function test() {
|
||||||
waitForExplicitFinish();
|
waitForExplicitFinish();
|
||||||
|
requestLongerTimeout(2);
|
||||||
|
|
||||||
registerCleanupFunction(function () {
|
registerCleanupFunction(function () {
|
||||||
Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs");
|
Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs");
|
||||||
|
@ -71,6 +71,7 @@ support-files =
|
|||||||
[browser_perf-loading-01.js]
|
[browser_perf-loading-01.js]
|
||||||
[browser_perf-loading-02.js]
|
[browser_perf-loading-02.js]
|
||||||
[browser_perf-marker-details-01.js]
|
[browser_perf-marker-details-01.js]
|
||||||
|
skip-if = os == 'linux' # Bug 1172120
|
||||||
[browser_perf-options-01.js]
|
[browser_perf-options-01.js]
|
||||||
[browser_perf-options-02.js]
|
[browser_perf-options-02.js]
|
||||||
[browser_perf-options-03.js]
|
[browser_perf-options-03.js]
|
||||||
|
@ -31,10 +31,10 @@ body {
|
|||||||
|
|
||||||
#pseudo-class-panel {
|
#pseudo-class-panel {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -1px;
|
margin-top: -1px;
|
||||||
|
margin-bottom: -1px;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
max-height: 24px;
|
max-height: 24px;
|
||||||
justify-content: space-around;
|
|
||||||
transition-property: max-height;
|
transition-property: max-height;
|
||||||
transition-duration: 150ms;
|
transition-duration: 150ms;
|
||||||
transition-timing-function: ease;
|
transition-timing-function: ease;
|
||||||
@ -46,6 +46,7 @@ body {
|
|||||||
|
|
||||||
#pseudo-class-panel > label {
|
#pseudo-class-panel > label {
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ruleview {
|
.ruleview {
|
||||||
|
@ -16,6 +16,7 @@ let PAGE_CONTENT = [
|
|||||||
'<div id="testid" class="testclass">Styled Node</div>',
|
'<div id="testid" class="testclass">Styled Node</div>',
|
||||||
'<span class="testclass2">This is a span</span>',
|
'<span class="testclass2">This is a span</span>',
|
||||||
'<span class="class1 class2">Multiple classes</span>',
|
'<span class="class1 class2">Multiple classes</span>',
|
||||||
|
'<span class="class3 class4">Multiple classes</span>',
|
||||||
'<p>Empty<p>',
|
'<p>Empty<p>',
|
||||||
'<h1 class="asd@@@@a!!!!:::@asd">Invalid characters in class</h1>',
|
'<h1 class="asd@@@@a!!!!:::@asd">Invalid characters in class</h1>',
|
||||||
'<h2 id="asd@@@a!!2a">Invalid characters in id</h2>'
|
'<h2 id="asd@@@a!!2a">Invalid characters in id</h2>'
|
||||||
@ -25,6 +26,7 @@ const TEST_DATA = [
|
|||||||
{ node: "#testid", expected: "#testid" },
|
{ node: "#testid", expected: "#testid" },
|
||||||
{ node: ".testclass2", expected: ".testclass2" },
|
{ node: ".testclass2", expected: ".testclass2" },
|
||||||
{ node: ".class1.class2", expected: ".class1.class2" },
|
{ node: ".class1.class2", expected: ".class1.class2" },
|
||||||
|
{ node: ".class3.class4", expected: ".class3.class4" },
|
||||||
{ node: "p", expected: "p" },
|
{ node: "p", expected: "p" },
|
||||||
{ node: "h1", expected: ".asd\\@\\@\\@\\@a\\!\\!\\!\\!\\:\\:\\:\\@asd" },
|
{ node: "h1", expected: ".asd\\@\\@\\@\\@a\\!\\!\\!\\!\\:\\:\\:\\@asd" },
|
||||||
{ node: "h2", expected: "#asd\\@\\@\\@a\\!\\!2a" }
|
{ node: "h2", expected: "#asd\\@\\@\\@a\\!\\!2a" }
|
||||||
|
@ -22,7 +22,12 @@ let PAGE_CONTENT = [
|
|||||||
'<div id="testid3">B</div>'
|
'<div id="testid3">B</div>'
|
||||||
].join("\n");
|
].join("\n");
|
||||||
|
|
||||||
|
const PSEUDO_PREF = "devtools.inspector.show_pseudo_elements";
|
||||||
|
|
||||||
add_task(function*() {
|
add_task(function*() {
|
||||||
|
// Expand the pseudo-elements section by default.
|
||||||
|
Services.prefs.setBoolPref(PSEUDO_PREF, true);
|
||||||
|
|
||||||
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(PAGE_CONTENT));
|
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(PAGE_CONTENT));
|
||||||
let {inspector, view} = yield openRuleView();
|
let {inspector, view} = yield openRuleView();
|
||||||
|
|
||||||
@ -41,6 +46,9 @@ add_task(function*() {
|
|||||||
info("Selecting the modified element");
|
info("Selecting the modified element");
|
||||||
yield selectNode(".testclass2", inspector);
|
yield selectNode(".testclass2", inspector);
|
||||||
yield checkModifiedElement(view, ".testclass2::first-letter");
|
yield checkModifiedElement(view, ".testclass2::first-letter");
|
||||||
|
|
||||||
|
// Reset the pseudo-elements section pref to its default value.
|
||||||
|
Services.prefs.clearUserPref(PSEUDO_PREF);
|
||||||
});
|
});
|
||||||
|
|
||||||
function* testEditSelector(view, name) {
|
function* testEditSelector(view, name) {
|
||||||
|
@ -7,10 +7,13 @@
|
|||||||
// Test that pseudoelements are displayed correctly in the rule view
|
// Test that pseudoelements are displayed correctly in the rule view
|
||||||
|
|
||||||
const TEST_URI = TEST_URL_ROOT + "doc_pseudoelement.html";
|
const TEST_URI = TEST_URL_ROOT + "doc_pseudoelement.html";
|
||||||
|
const PSEUDO_PREF = "devtools.inspector.show_pseudo_elements";
|
||||||
|
|
||||||
add_task(function*() {
|
add_task(function*() {
|
||||||
|
Services.prefs.setBoolPref(PSEUDO_PREF, true);
|
||||||
|
|
||||||
yield addTab(TEST_URI);
|
yield addTab(TEST_URI);
|
||||||
let {toolbox, inspector, view} = yield openRuleView();
|
let {inspector, view} = yield openRuleView();
|
||||||
|
|
||||||
yield testTopLeft(inspector, view);
|
yield testTopLeft(inspector, view);
|
||||||
yield testTopRight(inspector, view);
|
yield testTopRight(inspector, view);
|
||||||
@ -18,15 +21,13 @@ add_task(function*() {
|
|||||||
yield testBottomLeft(inspector, view);
|
yield testBottomLeft(inspector, view);
|
||||||
yield testParagraph(inspector, view);
|
yield testParagraph(inspector, view);
|
||||||
yield testBody(inspector, view);
|
yield testBody(inspector, view);
|
||||||
|
|
||||||
|
Services.prefs.clearUserPref(PSEUDO_PREF);
|
||||||
});
|
});
|
||||||
|
|
||||||
function* testTopLeft(inspector, view) {
|
function* testTopLeft(inspector, view) {
|
||||||
let selector = "#topleft";
|
let selector = "#topleft";
|
||||||
let {
|
let {rules} = yield assertPseudoElementRulesNumbers(selector, inspector, view, {
|
||||||
rules,
|
|
||||||
element,
|
|
||||||
elementStyle
|
|
||||||
} = yield assertPseudoElementRulesNumbers(selector, inspector, view, {
|
|
||||||
elementRulesNb: 4,
|
elementRulesNb: 4,
|
||||||
firstLineRulesNb: 2,
|
firstLineRulesNb: 2,
|
||||||
firstLetterRulesNb: 1,
|
firstLetterRulesNb: 1,
|
||||||
@ -35,83 +36,84 @@ function* testTopLeft(inspector, view) {
|
|||||||
|
|
||||||
let gutters = assertGutters(view);
|
let gutters = assertGutters(view);
|
||||||
|
|
||||||
// Make sure that clicking on the twisty hides pseudo elements
|
info("Make sure that clicking on the twisty hides pseudo elements");
|
||||||
let expander = gutters[0].querySelector(".ruleview-expander");
|
let expander = gutters[0].querySelector(".ruleview-expander");
|
||||||
ok (view.element.firstChild.classList.contains("show-expandable-container"), "Pseudo Elements are expanded");
|
ok(view.element.firstChild.classList.contains("show-expandable-container"),
|
||||||
expander.click();
|
"Pseudo Elements are expanded");
|
||||||
ok (!view.element.firstChild.classList.contains("show-expandable-container"), "Pseudo Elements are collapsed by twisty");
|
|
||||||
expander.click();
|
|
||||||
ok (view.element.firstChild.classList.contains("show-expandable-container"), "Pseudo Elements are expanded again");
|
|
||||||
|
|
||||||
// Make sure that dblclicking on the header container also toggles the pseudo elements
|
expander.click();
|
||||||
EventUtils.synthesizeMouseAtCenter(gutters[0], {clickCount: 2}, inspector.sidebar.getWindowForTab("ruleview"));
|
ok(!view.element.firstChild.classList.contains("show-expandable-container"),
|
||||||
ok (!view.element.firstChild.classList.contains("show-expandable-container"), "Pseudo Elements are collapsed by dblclicking");
|
"Pseudo Elements are collapsed by twisty");
|
||||||
|
|
||||||
let defaultView = element.ownerDocument.defaultView;
|
expander.click();
|
||||||
|
ok(view.element.firstChild.classList.contains("show-expandable-container"),
|
||||||
|
"Pseudo Elements are expanded again");
|
||||||
|
|
||||||
|
info("Make sure that dblclicking on the header container also toggles " +
|
||||||
|
"the pseudo elements");
|
||||||
|
EventUtils.synthesizeMouseAtCenter(gutters[0], {clickCount: 2},
|
||||||
|
view.doc.defaultView);
|
||||||
|
ok(!view.element.firstChild.classList.contains("show-expandable-container"),
|
||||||
|
"Pseudo Elements are collapsed by dblclicking");
|
||||||
|
|
||||||
let elementRule = rules.elementRules[0];
|
let elementRule = rules.elementRules[0];
|
||||||
let elementRuleView = getRuleViewRuleEditor(view, 3);
|
let elementRuleView = getRuleViewRuleEditor(view, 3);
|
||||||
|
|
||||||
let elementFirstLineRule = rules.firstLineRules[0];
|
let elementFirstLineRule = rules.firstLineRules[0];
|
||||||
let elementFirstLineRuleView = [].filter.call(view.element.children[1].children, (e) => {
|
let elementFirstLineRuleView = [...view.element.children[1].children].filter(e => {
|
||||||
return e._ruleEditor && e._ruleEditor.rule === elementFirstLineRule;
|
return e._ruleEditor && e._ruleEditor.rule === elementFirstLineRule;
|
||||||
})[0]._ruleEditor;
|
})[0]._ruleEditor;
|
||||||
|
|
||||||
is
|
is(convertTextPropsToString(elementFirstLineRule.textProps),
|
||||||
(
|
"color: orange",
|
||||||
convertTextPropsToString(elementFirstLineRule.textProps),
|
"TopLeft firstLine properties are correct");
|
||||||
"color: orange",
|
|
||||||
"TopLeft firstLine properties are correct"
|
|
||||||
);
|
|
||||||
|
|
||||||
let firstProp = elementFirstLineRuleView.addProperty("background-color", "rgb(0, 255, 0)", "");
|
let firstProp = elementFirstLineRuleView.addProperty("background-color", "rgb(0, 255, 0)", "");
|
||||||
let secondProp = elementFirstLineRuleView.addProperty("font-style", "italic", "");
|
let secondProp = elementFirstLineRuleView.addProperty("font-style", "italic", "");
|
||||||
|
|
||||||
is (firstProp, elementFirstLineRule.textProps[elementFirstLineRule.textProps.length - 2],
|
is(firstProp,
|
||||||
"First added property is on back of array");
|
elementFirstLineRule.textProps[elementFirstLineRule.textProps.length - 2],
|
||||||
is (secondProp, elementFirstLineRule.textProps[elementFirstLineRule.textProps.length - 1],
|
"First added property is on back of array");
|
||||||
"Second added property is on back of array");
|
is(secondProp,
|
||||||
|
elementFirstLineRule.textProps[elementFirstLineRule.textProps.length - 1],
|
||||||
|
"Second added property is on back of array");
|
||||||
|
|
||||||
yield elementFirstLineRule._applyingModifications;
|
yield elementFirstLineRule._applyingModifications;
|
||||||
|
|
||||||
is((yield getComputedStyleProperty(selector, ":first-line", "background-color")),
|
is((yield getComputedStyleProperty(selector, ":first-line", "background-color")),
|
||||||
"rgb(0, 255, 0)", "Added property should have been used.");
|
"rgb(0, 255, 0)", "Added property should have been used.");
|
||||||
is((yield getComputedStyleProperty(selector, ":first-line", "font-style")),
|
is((yield getComputedStyleProperty(selector, ":first-line", "font-style")),
|
||||||
"italic", "Added property should have been used.");
|
"italic", "Added property should have been used.");
|
||||||
is((yield getComputedStyleProperty(selector, null, "text-decoration")),
|
is((yield getComputedStyleProperty(selector, null, "text-decoration")),
|
||||||
"none", "Added property should not apply to element");
|
"none", "Added property should not apply to element");
|
||||||
|
|
||||||
firstProp.setEnabled(false);
|
firstProp.setEnabled(false);
|
||||||
yield elementFirstLineRule._applyingModifications;
|
yield elementFirstLineRule._applyingModifications;
|
||||||
|
|
||||||
is((yield getComputedStyleProperty(selector, ":first-line", "background-color")),
|
is((yield getComputedStyleProperty(selector, ":first-line", "background-color")),
|
||||||
"rgb(255, 0, 0)", "Disabled property should now have been used.");
|
"rgb(255, 0, 0)", "Disabled property should now have been used.");
|
||||||
is((yield getComputedStyleProperty(selector, null, "background-color")),
|
is((yield getComputedStyleProperty(selector, null, "background-color")),
|
||||||
"rgb(221, 221, 221)", "Added property should not apply to element");
|
"rgb(221, 221, 221)", "Added property should not apply to element");
|
||||||
|
|
||||||
firstProp.setEnabled(true);
|
firstProp.setEnabled(true);
|
||||||
yield elementFirstLineRule._applyingModifications;
|
yield elementFirstLineRule._applyingModifications;
|
||||||
|
|
||||||
is((yield getComputedStyleProperty(selector, ":first-line", "background-color")),
|
is((yield getComputedStyleProperty(selector, ":first-line", "background-color")),
|
||||||
"rgb(0, 255, 0)", "Added property should have been used.");
|
"rgb(0, 255, 0)", "Added property should have been used.");
|
||||||
is((yield getComputedStyleProperty(selector, null, "text-decoration")),
|
is((yield getComputedStyleProperty(selector, null, "text-decoration")),
|
||||||
"none", "Added property should not apply to element");
|
"none", "Added property should not apply to element");
|
||||||
|
|
||||||
firstProp = elementRuleView.addProperty("background-color", "rgb(0, 0, 255)", "");
|
firstProp = elementRuleView.addProperty("background-color", "rgb(0, 0, 255)", "");
|
||||||
yield elementRule._applyingModifications;
|
yield elementRule._applyingModifications;
|
||||||
|
|
||||||
is((yield getComputedStyleProperty(selector, null, "background-color")),
|
is((yield getComputedStyleProperty(selector, null, "background-color")),
|
||||||
"rgb(0, 0, 255)", "Added property should have been used.");
|
"rgb(0, 0, 255)", "Added property should have been used.");
|
||||||
is((yield getComputedStyleProperty(selector, ":first-line", "background-color")),
|
is((yield getComputedStyleProperty(selector, ":first-line", "background-color")),
|
||||||
"rgb(0, 255, 0)", "Added prop does not apply to pseudo");
|
"rgb(0, 255, 0)", "Added prop does not apply to pseudo");
|
||||||
}
|
}
|
||||||
|
|
||||||
function* testTopRight(inspector, view) {
|
function* testTopRight(inspector, view) {
|
||||||
let {
|
yield assertPseudoElementRulesNumbers("#topright", inspector, view, {
|
||||||
rules,
|
|
||||||
element,
|
|
||||||
elementStyle
|
|
||||||
} = yield assertPseudoElementRulesNumbers("#topright", inspector, view, {
|
|
||||||
elementRulesNb: 4,
|
elementRulesNb: 4,
|
||||||
firstLineRulesNb: 1,
|
firstLineRulesNb: 1,
|
||||||
firstLetterRulesNb: 1,
|
firstLetterRulesNb: 1,
|
||||||
@ -121,10 +123,13 @@ function* testTopRight(inspector, view) {
|
|||||||
let gutters = assertGutters(view);
|
let gutters = assertGutters(view);
|
||||||
|
|
||||||
let expander = gutters[0].querySelector(".ruleview-expander");
|
let expander = gutters[0].querySelector(".ruleview-expander");
|
||||||
ok (!view.element.firstChild.classList.contains("show-expandable-container"), "Pseudo Elements remain collapsed after switching element");
|
ok(!view.element.firstChild.classList.contains("show-expandable-container"),
|
||||||
|
"Pseudo Elements remain collapsed after switching element");
|
||||||
|
|
||||||
expander.scrollIntoView();
|
expander.scrollIntoView();
|
||||||
expander.click();
|
expander.click();
|
||||||
ok (view.element.firstChild.classList.contains("show-expandable-container"), "Pseudo Elements are shown again after clicking twisty");
|
ok(view.element.firstChild.classList.contains("show-expandable-container"),
|
||||||
|
"Pseudo Elements are shown again after clicking twisty");
|
||||||
}
|
}
|
||||||
|
|
||||||
function* testBottomRight(inspector, view) {
|
function* testBottomRight(inspector, view) {
|
||||||
@ -146,61 +151,36 @@ function* testBottomLeft(inspector, view) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function* testParagraph(inspector, view) {
|
function* testParagraph(inspector, view) {
|
||||||
let {
|
let {rules} = yield assertPseudoElementRulesNumbers("#bottomleft p", inspector, view, {
|
||||||
rules,
|
|
||||||
element,
|
|
||||||
elementStyle
|
|
||||||
} = yield assertPseudoElementRulesNumbers("#bottomleft p", inspector, view, {
|
|
||||||
elementRulesNb: 3,
|
elementRulesNb: 3,
|
||||||
firstLineRulesNb: 1,
|
firstLineRulesNb: 1,
|
||||||
firstLetterRulesNb: 1,
|
firstLetterRulesNb: 1,
|
||||||
selectionRulesNb: 1
|
selectionRulesNb: 1
|
||||||
});
|
});
|
||||||
|
|
||||||
let gutters = assertGutters(view);
|
assertGutters(view);
|
||||||
|
|
||||||
let elementFirstLineRule = rules.firstLineRules[0];
|
let elementFirstLineRule = rules.firstLineRules[0];
|
||||||
let elementFirstLineRuleView = [].filter.call(view.element.children[1].children, (e) => {
|
is(convertTextPropsToString(elementFirstLineRule.textProps),
|
||||||
return e._ruleEditor && e._ruleEditor.rule === elementFirstLineRule;
|
"background: blue none repeat scroll 0% 0%",
|
||||||
})[0]._ruleEditor;
|
"Paragraph first-line properties are correct");
|
||||||
|
|
||||||
is
|
|
||||||
(
|
|
||||||
convertTextPropsToString(elementFirstLineRule.textProps),
|
|
||||||
"background: blue none repeat scroll 0% 0%",
|
|
||||||
"Paragraph first-line properties are correct"
|
|
||||||
);
|
|
||||||
|
|
||||||
let elementFirstLetterRule = rules.firstLetterRules[0];
|
let elementFirstLetterRule = rules.firstLetterRules[0];
|
||||||
let elementFirstLetterRuleView = [].filter.call(view.element.children[1].children, (e) => {
|
is(convertTextPropsToString(elementFirstLetterRule.textProps),
|
||||||
return e._ruleEditor && e._ruleEditor.rule === elementFirstLetterRule;
|
"color: red; font-size: 130%",
|
||||||
})[0]._ruleEditor;
|
"Paragraph first-letter properties are correct");
|
||||||
|
|
||||||
is
|
|
||||||
(
|
|
||||||
convertTextPropsToString(elementFirstLetterRule.textProps),
|
|
||||||
"color: red; font-size: 130%",
|
|
||||||
"Paragraph first-letter properties are correct"
|
|
||||||
);
|
|
||||||
|
|
||||||
let elementSelectionRule = rules.selectionRules[0];
|
let elementSelectionRule = rules.selectionRules[0];
|
||||||
let elementSelectionRuleView = [].filter.call(view.element.children[1].children, (e) => {
|
is(convertTextPropsToString(elementSelectionRule.textProps),
|
||||||
return e._ruleEditor && e._ruleEditor.rule === elementSelectionRule;
|
"color: white; background: black none repeat scroll 0% 0%",
|
||||||
})[0]._ruleEditor;
|
"Paragraph first-letter properties are correct");
|
||||||
|
|
||||||
is
|
|
||||||
(
|
|
||||||
convertTextPropsToString(elementSelectionRule.textProps),
|
|
||||||
"color: white; background: black none repeat scroll 0% 0%",
|
|
||||||
"Paragraph first-letter properties are correct"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function* testBody(inspector, view) {
|
function* testBody(inspector, view) {
|
||||||
let {element, elementStyle} = yield testNode("body", inspector, view);
|
yield testNode("body", inspector, view);
|
||||||
|
|
||||||
let gutters = view.element.querySelectorAll(".theme-gutter");
|
let gutters = getGutters(view);
|
||||||
is (gutters.length, 0, "There are no gutter headings");
|
is(gutters.length, 0, "There are no gutter headings");
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertTextPropsToString(textProps) {
|
function convertTextPropsToString(textProps) {
|
||||||
@ -224,24 +204,33 @@ function* assertPseudoElementRulesNumbers(selector, inspector, view, ruleNbs) {
|
|||||||
selectionRules: elementStyle.rules.filter(rule => rule.pseudoElement === ":-moz-selection")
|
selectionRules: elementStyle.rules.filter(rule => rule.pseudoElement === ":-moz-selection")
|
||||||
};
|
};
|
||||||
|
|
||||||
is(rules.elementRules.length, ruleNbs.elementRulesNb, selector +
|
is(rules.elementRules.length, ruleNbs.elementRulesNb,
|
||||||
" has the correct number of non pseudo element rules");
|
selector + " has the correct number of non pseudo element rules");
|
||||||
is(rules.firstLineRules.length, ruleNbs.firstLineRulesNb, selector +
|
is(rules.firstLineRules.length, ruleNbs.firstLineRulesNb,
|
||||||
" has the correct number of :first-line rules");
|
selector + " has the correct number of :first-line rules");
|
||||||
is(rules.firstLetterRules.length, ruleNbs.firstLetterRulesNb, selector +
|
is(rules.firstLetterRules.length, ruleNbs.firstLetterRulesNb,
|
||||||
" has the correct number of :first-letter rules");
|
selector + " has the correct number of :first-letter rules");
|
||||||
is(rules.selectionRules.length, ruleNbs.selectionRulesNb, selector +
|
is(rules.selectionRules.length, ruleNbs.selectionRulesNb,
|
||||||
" has the correct number of :selection rules");
|
selector + " has the correct number of :selection rules");
|
||||||
|
|
||||||
return {rules: rules, element: element, elementStyle: elementStyle};
|
return {rules, element, elementStyle};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGutters(view) {
|
||||||
|
return view.element.querySelectorAll(".theme-gutter");
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertGutters(view) {
|
function assertGutters(view) {
|
||||||
let gutters = view.element.querySelectorAll(".theme-gutter");
|
let gutters = getGutters(view);
|
||||||
is (gutters.length, 3, "There are 3 gutter headings");
|
|
||||||
is (gutters[0].textContent, "Pseudo-elements", "Gutter heading is correct");
|
is(gutters.length, 3,
|
||||||
is (gutters[1].textContent, "This Element", "Gutter heading is correct");
|
"There are 3 gutter headings");
|
||||||
is (gutters[2].textContent, "Inherited from body", "Gutter heading is correct");
|
is(gutters[0].textContent, "Pseudo-elements",
|
||||||
|
"Gutter heading is correct");
|
||||||
|
is(gutters[1].textContent, "This Element",
|
||||||
|
"Gutter heading is correct");
|
||||||
|
is(gutters[2].textContent, "Inherited from body",
|
||||||
|
"Gutter heading is correct");
|
||||||
|
|
||||||
return gutters;
|
return gutters;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
This is the pdf.js project output, https://github.com/mozilla/pdf.js
|
This is the pdf.js project output, https://github.com/mozilla/pdf.js
|
||||||
|
|
||||||
Current extension version is: 1.1.165
|
Current extension version is: 1.1.215
|
||||||
|
@ -161,7 +161,8 @@ function createNewChannel(uri, node, principal) {
|
|||||||
uri: uri,
|
uri: uri,
|
||||||
loadingNode: node,
|
loadingNode: node,
|
||||||
loadingPrincipal: principal,
|
loadingPrincipal: principal,
|
||||||
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER});
|
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function asyncFetchChannel(channel, callback) {
|
function asyncFetchChannel(channel, callback) {
|
||||||
|
@ -183,7 +183,8 @@ let PdfjsChromeUtils = {
|
|||||||
_findbarFromMessage: function(aMsg) {
|
_findbarFromMessage: function(aMsg) {
|
||||||
let browser = aMsg.target;
|
let browser = aMsg.target;
|
||||||
let tabbrowser = browser.getTabBrowser();
|
let tabbrowser = browser.getTabBrowser();
|
||||||
let tab = tabbrowser.getTabForBrowser(browser);
|
let tab;
|
||||||
|
tab = tabbrowser.getTabForBrowser(browser);
|
||||||
return tabbrowser.getFindBar(tab);
|
return tabbrowser.getFindBar(tab);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') {
|
|||||||
(typeof window !== 'undefined' ? window : this).PDFJS = {};
|
(typeof window !== 'undefined' ? window : this).PDFJS = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
PDFJS.version = '1.1.165';
|
PDFJS.version = '1.1.215';
|
||||||
PDFJS.build = '39d2103';
|
PDFJS.build = 'c9a7498';
|
||||||
|
|
||||||
(function pdfjsWrapper() {
|
(function pdfjsWrapper() {
|
||||||
// Use strict in our context only - users might not want it
|
// Use strict in our context only - users might not want it
|
||||||
@ -4208,7 +4208,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var name = fontObj.loadedName || 'sans-serif';
|
var name = fontObj.loadedName || 'sans-serif';
|
||||||
var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') :
|
var bold = fontObj.black ? (fontObj.bold ? '900' : 'bold') :
|
||||||
(fontObj.bold ? 'bold' : 'normal');
|
(fontObj.bold ? 'bold' : 'normal');
|
||||||
|
|
||||||
var italic = fontObj.italic ? 'italic' : 'normal';
|
var italic = fontObj.italic ? 'italic' : 'normal';
|
||||||
@ -4468,6 +4468,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
if (isTextInvisible || fontSize === 0) {
|
if (isTextInvisible || fontSize === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.cachedGetSinglePixelWidth = null;
|
||||||
|
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.transform.apply(ctx, current.textMatrix);
|
ctx.transform.apply(ctx, current.textMatrix);
|
||||||
|
126
browser/extensions/pdfjs/content/build/pdf.worker.js
vendored
126
browser/extensions/pdfjs/content/build/pdf.worker.js
vendored
@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') {
|
|||||||
(typeof window !== 'undefined' ? window : this).PDFJS = {};
|
(typeof window !== 'undefined' ? window : this).PDFJS = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
PDFJS.version = '1.1.165';
|
PDFJS.version = '1.1.215';
|
||||||
PDFJS.build = '39d2103';
|
PDFJS.build = 'c9a7498';
|
||||||
|
|
||||||
(function pdfjsWrapper() {
|
(function pdfjsWrapper() {
|
||||||
// Use strict in our context only - users might not want it
|
// Use strict in our context only - users might not want it
|
||||||
@ -2062,17 +2062,33 @@ var Page = (function PageClosure() {
|
|||||||
return this.pageDict.get(key);
|
return this.pageDict.get(key);
|
||||||
},
|
},
|
||||||
|
|
||||||
getInheritedPageProp: function Page_inheritPageProp(key) {
|
getInheritedPageProp: function Page_getInheritedPageProp(key) {
|
||||||
var dict = this.pageDict;
|
var dict = this.pageDict, valueArray = null, loopCount = 0;
|
||||||
var value = dict.get(key);
|
var MAX_LOOP_COUNT = 100;
|
||||||
while (value === undefined) {
|
// Always walk up the entire parent chain, to be able to find
|
||||||
dict = dict.get('Parent');
|
// e.g. \Resources placed on multiple levels of the tree.
|
||||||
if (!dict) {
|
while (dict) {
|
||||||
|
var value = dict.get(key);
|
||||||
|
if (value) {
|
||||||
|
if (!valueArray) {
|
||||||
|
valueArray = [];
|
||||||
|
}
|
||||||
|
valueArray.push(value);
|
||||||
|
}
|
||||||
|
if (++loopCount > MAX_LOOP_COUNT) {
|
||||||
|
warn('Page_getInheritedPageProp: maximum loop count exceeded.');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
value = dict.get(key);
|
dict = dict.get('Parent');
|
||||||
}
|
}
|
||||||
return value;
|
if (!valueArray) {
|
||||||
|
return Dict.empty;
|
||||||
|
}
|
||||||
|
if (valueArray.length === 1 || !isDict(valueArray[0]) ||
|
||||||
|
loopCount > MAX_LOOP_COUNT) {
|
||||||
|
return valueArray[0];
|
||||||
|
}
|
||||||
|
return Dict.merge(this.xref, valueArray);
|
||||||
},
|
},
|
||||||
|
|
||||||
get content() {
|
get content() {
|
||||||
@ -2080,14 +2096,10 @@ var Page = (function PageClosure() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
get resources() {
|
get resources() {
|
||||||
var value = this.getInheritedPageProp('Resources');
|
|
||||||
// For robustness: The spec states that a \Resources entry has to be
|
// For robustness: The spec states that a \Resources entry has to be
|
||||||
// present, but can be empty. Some document omit it still. In this case
|
// present, but can be empty. Some document omit it still, in this case
|
||||||
// return an empty dictionary:
|
// we return an empty dictionary.
|
||||||
if (value === undefined) {
|
return shadow(this, 'resources', this.getInheritedPageProp('Resources'));
|
||||||
value = Dict.empty;
|
|
||||||
}
|
|
||||||
return shadow(this, 'resources', value);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
get mediaBox() {
|
get mediaBox() {
|
||||||
@ -2360,6 +2372,10 @@ var PDFDocument = (function PDFDocumentClosure() {
|
|||||||
PDFDocument.prototype = {
|
PDFDocument.prototype = {
|
||||||
parse: function PDFDocument_parse(recoveryMode) {
|
parse: function PDFDocument_parse(recoveryMode) {
|
||||||
this.setup(recoveryMode);
|
this.setup(recoveryMode);
|
||||||
|
var version = this.catalog.catDict.get('Version');
|
||||||
|
if (isName(version)) {
|
||||||
|
this.pdfFormatVersion = version.name;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// checking if AcroForm is present
|
// checking if AcroForm is present
|
||||||
this.acroForm = this.catalog.catDict.get('AcroForm');
|
this.acroForm = this.catalog.catDict.get('AcroForm');
|
||||||
@ -2461,8 +2477,10 @@ var PDFDocument = (function PDFDocumentClosure() {
|
|||||||
}
|
}
|
||||||
version += String.fromCharCode(ch);
|
version += String.fromCharCode(ch);
|
||||||
}
|
}
|
||||||
// removing "%PDF-"-prefix
|
if (!this.pdfFormatVersion) {
|
||||||
this.pdfFormatVersion = version.substring(5);
|
// removing "%PDF-"-prefix
|
||||||
|
this.pdfFormatVersion = version.substring(5);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// May not be a PDF file, continue anyway.
|
// May not be a PDF file, continue anyway.
|
||||||
@ -2739,6 +2757,24 @@ var Dict = (function DictClosure() {
|
|||||||
|
|
||||||
Dict.empty = new Dict(null);
|
Dict.empty = new Dict(null);
|
||||||
|
|
||||||
|
Dict.merge = function Dict_merge(xref, dictArray) {
|
||||||
|
var mergedDict = new Dict(xref);
|
||||||
|
|
||||||
|
for (var i = 0, ii = dictArray.length; i < ii; i++) {
|
||||||
|
var dict = dictArray[i];
|
||||||
|
if (!isDict(dict)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (var keyName in dict.map) {
|
||||||
|
if (mergedDict.map[keyName]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
mergedDict.map[keyName] = dict.map[keyName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mergedDict;
|
||||||
|
};
|
||||||
|
|
||||||
return Dict;
|
return Dict;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -5213,7 +5249,10 @@ var PDFFunction = (function PDFFunctionClosure() {
|
|||||||
var rmin = encode[2 * i];
|
var rmin = encode[2 * i];
|
||||||
var rmax = encode[2 * i + 1];
|
var rmax = encode[2 * i + 1];
|
||||||
|
|
||||||
tmpBuf[0] = rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin);
|
// Prevent the value from becoming NaN as a result
|
||||||
|
// of division by zero (fixes issue6113.pdf).
|
||||||
|
tmpBuf[0] = dmin === dmax ? rmin :
|
||||||
|
rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin);
|
||||||
|
|
||||||
// call the appropriate function
|
// call the appropriate function
|
||||||
fns[i](tmpBuf, 0, dest, destOffset);
|
fns[i](tmpBuf, 0, dest, destOffset);
|
||||||
@ -6222,9 +6261,9 @@ var ColorSpace = (function ColorSpaceClosure() {
|
|||||||
error('unrecognized colorspace ' + mode);
|
error('unrecognized colorspace ' + mode);
|
||||||
}
|
}
|
||||||
} else if (isArray(cs)) {
|
} else if (isArray(cs)) {
|
||||||
mode = cs[0].name;
|
mode = xref.fetchIfRef(cs[0]).name;
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
var numComps, params;
|
var numComps, params, alt;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case 'DeviceGray':
|
case 'DeviceGray':
|
||||||
@ -6246,6 +6285,17 @@ var ColorSpace = (function ColorSpaceClosure() {
|
|||||||
var stream = xref.fetchIfRef(cs[1]);
|
var stream = xref.fetchIfRef(cs[1]);
|
||||||
var dict = stream.dict;
|
var dict = stream.dict;
|
||||||
numComps = dict.get('N');
|
numComps = dict.get('N');
|
||||||
|
alt = dict.get('Alternate');
|
||||||
|
if (alt) {
|
||||||
|
var altIR = ColorSpace.parseToIR(alt, xref, res);
|
||||||
|
// Parse the /Alternate CS to ensure that the number of components
|
||||||
|
// are correct, and also (indirectly) that it is not a PatternCS.
|
||||||
|
var altCS = ColorSpace.fromIR(altIR);
|
||||||
|
if (altCS.numComps === numComps) {
|
||||||
|
return altIR;
|
||||||
|
}
|
||||||
|
warn('ICCBased color space: Ignoring incorrect /Alternate entry.');
|
||||||
|
}
|
||||||
if (numComps === 1) {
|
if (numComps === 1) {
|
||||||
return 'DeviceGrayCS';
|
return 'DeviceGrayCS';
|
||||||
} else if (numComps === 3) {
|
} else if (numComps === 3) {
|
||||||
@ -6255,7 +6305,7 @@ var ColorSpace = (function ColorSpaceClosure() {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'Pattern':
|
case 'Pattern':
|
||||||
var basePatternCS = cs[1];
|
var basePatternCS = xref.fetchIfRef(cs[1]) || null;
|
||||||
if (basePatternCS) {
|
if (basePatternCS) {
|
||||||
basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res);
|
basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res);
|
||||||
}
|
}
|
||||||
@ -6278,11 +6328,11 @@ var ColorSpace = (function ColorSpaceClosure() {
|
|||||||
} else if (isArray(name)) {
|
} else if (isArray(name)) {
|
||||||
numComps = name.length;
|
numComps = name.length;
|
||||||
}
|
}
|
||||||
var alt = ColorSpace.parseToIR(cs[2], xref, res);
|
alt = ColorSpace.parseToIR(cs[2], xref, res);
|
||||||
var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3]));
|
var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3]));
|
||||||
return ['AlternateCS', numComps, alt, tintFnIR];
|
return ['AlternateCS', numComps, alt, tintFnIR];
|
||||||
case 'Lab':
|
case 'Lab':
|
||||||
params = cs[1].getAll();
|
params = xref.fetchIfRef(cs[1]).getAll();
|
||||||
return ['LabCS', params];
|
return ['LabCS', params];
|
||||||
default:
|
default:
|
||||||
error('unimplemented color space object "' + mode + '"');
|
error('unimplemented color space object "' + mode + '"');
|
||||||
@ -16333,12 +16383,14 @@ var Font = (function FontClosure() {
|
|||||||
case 0x7F: // Control char
|
case 0x7F: // Control char
|
||||||
case 0xA0: // Non breaking space
|
case 0xA0: // Non breaking space
|
||||||
case 0xAD: // Soft hyphen
|
case 0xAD: // Soft hyphen
|
||||||
case 0x0E33: // Thai character SARA AM
|
|
||||||
case 0x2011: // Non breaking hyphen
|
case 0x2011: // Non breaking hyphen
|
||||||
case 0x205F: // Medium mathematical space
|
case 0x205F: // Medium mathematical space
|
||||||
case 0x25CC: // Dotted circle (combining mark)
|
case 0x25CC: // Dotted circle (combining mark)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if ((code & ~0xFF) === 0x0E00) { // Thai/Lao chars (with combining mark)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17762,13 +17814,18 @@ var Font = (function FontClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var charCodeToGlyphId = [], charCode, toUnicode = properties.toUnicode;
|
var charCodeToGlyphId = [], charCode;
|
||||||
|
var toUnicode = properties.toUnicode, widths = properties.widths;
|
||||||
|
var isIdentityUnicode = toUnicode instanceof IdentityToUnicodeMap;
|
||||||
|
|
||||||
function hasGlyph(glyphId, charCode) {
|
function hasGlyph(glyphId, charCode, widthCode) {
|
||||||
if (!missingGlyphs[glyphId]) {
|
if (!missingGlyphs[glyphId]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (charCode >= 0 && toUnicode.has(charCode)) {
|
if (!isIdentityUnicode && charCode >= 0 && toUnicode.has(charCode)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (widths && widthCode >= 0 && isNum(widths[widthCode])) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -17788,7 +17845,7 @@ var Font = (function FontClosure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (glyphId >= 0 && glyphId < numGlyphs &&
|
if (glyphId >= 0 && glyphId < numGlyphs &&
|
||||||
hasGlyph(glyphId, charCode)) {
|
hasGlyph(glyphId, charCode, cid)) {
|
||||||
charCodeToGlyphId[charCode] = glyphId;
|
charCodeToGlyphId[charCode] = glyphId;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -17849,18 +17906,19 @@ var Font = (function FontClosure() {
|
|||||||
var found = false;
|
var found = false;
|
||||||
for (i = 0; i < cmapMappingsLength; ++i) {
|
for (i = 0; i < cmapMappingsLength; ++i) {
|
||||||
if (cmapMappings[i].charCode === unicodeOrCharCode &&
|
if (cmapMappings[i].charCode === unicodeOrCharCode &&
|
||||||
hasGlyph(cmapMappings[i].glyphId, unicodeOrCharCode)) {
|
hasGlyph(cmapMappings[i].glyphId, unicodeOrCharCode, -1)) {
|
||||||
charCodeToGlyphId[charCode] = cmapMappings[i].glyphId;
|
charCodeToGlyphId[charCode] = cmapMappings[i].glyphId;
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found && properties.glyphNames) {
|
if (!found && properties.glyphNames) {
|
||||||
// Try to map using the post table. There are currently no known
|
// Try to map using the post table.
|
||||||
// pdfs that this fixes.
|
|
||||||
var glyphId = properties.glyphNames.indexOf(glyphName);
|
var glyphId = properties.glyphNames.indexOf(glyphName);
|
||||||
if (glyphId > 0 && hasGlyph(glyphId, -1)) {
|
if (glyphId > 0 && hasGlyph(glyphId, -1, -1)) {
|
||||||
charCodeToGlyphId[charCode] = glyphId;
|
charCodeToGlyphId[charCode] = glyphId;
|
||||||
|
} else {
|
||||||
|
charCodeToGlyphId[charCode] = 0; // notdef
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -803,7 +803,7 @@ html[dir='rtl'] .dropdownToolbarButton {
|
|||||||
.dropdownToolbarButton {
|
.dropdownToolbarButton {
|
||||||
width: 120px;
|
width: 120px;
|
||||||
max-width: 120px;
|
max-width: 120px;
|
||||||
padding: 3px 2px 2px;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: url(images/toolbarButton-menuArrows.png) no-repeat;
|
background: url(images/toolbarButton-menuArrows.png) no-repeat;
|
||||||
}
|
}
|
||||||
@ -819,7 +819,7 @@ html[dir='rtl'] .dropdownToolbarButton {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: hsl(0,0%,95%);
|
color: hsl(0,0%,95%);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 3px 2px 2px;
|
||||||
border: none;
|
border: none;
|
||||||
background: rgba(0,0,0,0); /* Opera does not support 'transparent' <select> background */
|
background: rgba(0,0,0,0); /* Opera does not support 'transparent' <select> background */
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -66,30 +66,174 @@ const PREF_NEWTAB_ENHANCED = "browser.newtabpage.enhanced";
|
|||||||
|
|
||||||
// Only allow explicitly approved frecent sites with display name
|
// Only allow explicitly approved frecent sites with display name
|
||||||
const ALLOWED_FRECENT_SITES = new Map([
|
const ALLOWED_FRECENT_SITES = new Map([
|
||||||
[ 'airdroid.com,android-developers.blogspot.com,android.com,androidandme.com,androidapplications.com,androidapps.com,androidauthority.com,androidcentral.com,androidcommunity.com,androidfilehost.com,androidforums.com,androidguys.com,androidheadlines.com,androidpit.com,androidpolice.com,androidspin.com,androidtapp.com,androinica.com,droid-life.com,droidforums.net,droidviews.com,droidxforums.com,forum.xda-developers.com,phandroid.com,play.google.com,shopandroid.com,talkandroid.com,theandroidsoul.com,thedroidguy.com,videodroid.org',
|
[ '1800petmeds.com,800petmeds.com,adopt.dogtime.com,adoptapet.com,akc.org,americanhumane.org,animal.discovery.com,animalconcerns.org,animalshelter.org,arcatapet.com,aspca.org,avma.org,bestfriends.org,blog.petmeds.com,buddydoghs.com,carealotpets.com,dailypuppy.com,dog.com,dogbar.com,dogbreedinfo.com,drsfostersmith.com,entirelypets.com,farmsanctuary.org,farmusa.org,freekibble.com,freekibblekat.com,healthypets.com,hsus.org,humanesociety.org,liveaquaria.com,marinedepot.com,medi-vet.com,nationalpetpharmacy.com,nsalamerica.org,nycacc.org,ohmydogsupplies.com,pet-dog-cat-supply-store.com,petcarerx.com,petco.com,petdiscounters.com,petedge.com,peteducation.com,petfinder.com,petfooddirect.com,petguys.com,petharbor.com,petmountain.com,petplanet.co.uk,pets911.com,petsmart.com,petsuppliesplus.com,puppyfind.com,revivalanimal.com,terrificpets.com,thatpetplace.com,theanimalrescuesite.com,theanimalrescuesite.greatergood.com,thefluffingtonpost.com,therainforestsite.com,vetdepot.com',
|
||||||
'Technology' ],
|
'pet' ],
|
||||||
[ 'assurancewireless.com,att.com,attsavings.com,boostmobile.com,budgetmobile.com,consumercellular.com,credomobile.com,gosmartmobile.com,h2owirelessnow.com,lycamobile.com,lycamobile.us,metropcs.com,myfamilymobile.com,polarmobile.com,qlinkwireless.com,republicwireless.com,sprint.com,straighttalk.com,t-mobile.com,tracfonewireless.com,verizonwireless.com,virginmobile.com,virginmobile.com.au,virginmobileusa.com,vodafone.co.uk,vodafone.com,vzwshop.com',
|
[ '1aauto.com,autoblog.com,autoguide.com,autosite.com,autoweek.com,bimmerpost.com,bmwblog.com,boldride.com,caranddriver.com,carcomplaints.com,carspoon.com,cherokeeforum.com,classiccars.com,commercialtrucktrader.com,corvetteforum.com,dealerrater.com,ebizautos.com,ford-trucks.com,hemmings.com,jalopnik.com,jeepforum.com,jeepsunlimited.com,jk-forum.com,legendaryspeed.com,motorauthority.com,motortrend.com,motorwings.com,odometer.com,pirate4x4.com,purecars.com,roadandtrack.com,teslamotorsclub.com,topgear.com,topspeed.com,totalmini.com,truckpaper.com,wranglerforum.com',
|
||||||
'Mobile Phone' ],
|
'auto' ],
|
||||||
|
[ 'autobytel.com,autocheck.com,automotive.com,autonation.com,autos.aol.com,autos.msn.com,autos.yahoo.com,autotrader.autos.msn.com,autotrader.com,autotraderclassics.com,autoweb.com,car.com,carbuyingtips.com,carfax.com,cargurus.com,carmax.com,carprices.com,cars.com,cars.oodle.com,carsdirect.com,carsforsale.com,edmunds.com,hertzcarsales.com,imotors.com,intellichoice.com,internetautoguide.com,kbb.com,lemonfree.com,nada.com,nadaguides.com,thecarconnection.com,thetruthaboutcars.com,truecar.com,usedcars.com,usnews.rankingsandreviews.com',
|
||||||
|
'auto' ],
|
||||||
|
[ 'acura.com,audi.ca,audi.com,audiusa.com,automobiles.honda.com,bentleymotors.com,bmw.com,bmwusa.com,buick.com,buyatoyota.com,cadillac.com,cars.mclaren.com,chevrolet.com,choosenissan.com,chrysler.com,daimler.com,dodge.com,ferrari.com/en_us,fiskerautomotive.com,ford.com,gm.com,gmc.com,hummer.com,hyundai.com,hyundaiusa.com,infiniti.com,infinitiusa.com,jaguarusa.com,jeep.com,kia.com,kiamotors.com,lamborghini.com/en/home,landrover.com,landroverusa.com,lexus.com,lincoln.com,maserati.us,mazda.com,mazdausa.com,mbusa.com,mbusi.com,mercedes-amg.com,mercedes-benz.com,mercuryvehicles.com,miniusa.com,nissanusa.com,pontiac.com,porsche.com/usa,ramtrucks.com,rolls-roycemotorcars.com,saturn.com,scion.com,subaru.com,teslamotors.com,toyota.com,volkswagen.co.uk,volkswagen.com,volvocars.com/us,vw.com',
|
||||||
|
'auto' ],
|
||||||
|
[ '1010tires.com,4wheelparts.com,advanceautoparts.com,andysautosport.com,autoanything.com,autogeek.net,autopartsgiant.com,autopartswarehouse.com,autotrucktoys.com,autozone.com,autozoneinc.com,bavauto.com,bigotires.com,bilsteinus.com,brembo.com,car-part.com,carid.com,carparts.com,carquest.com,dinancars.com,discounttire.com,discounttiredirect.com,firestonecompleteautocare.com,goodyear.com,hrewheels,jcwhitney.com,kw-suspensions.com,momousa.com,napaonline.com,onlinetires.com,oreillyauto.com,oriellysautoparts.com,pepboys.com,repairpal.com,rockauto.com,shop.advanceautoparts.com,slickcar.com,stoptech.com,streetbeatcustoms.com,summitracing.com,tirebuyer.com,tirerack.com,tiresplus.com,tsw.com,velocitymotoring.com,wheelmax.com',
|
||||||
|
'auto parts' ],
|
||||||
|
[ 'abebooks.co.uk,abebooks.com,addall.com,alibris.com,allaboutcircuits.com,allbookstores.com,allyoucanbooks.com,answersingenesis.org,artnet.com,audiobooks.com,barnesandnoble.com,barnesandnobleinc.com,bartleby.com,betterworldbooks.com,biblio.com,biggerbooks.com,bncollege.com,bookbyte.com,bookdepository.com,bookfinder.com,bookrenter.com,booksamillion.com,booksite.com,boundless.com,brookstone.com,btol.com,calibre-ebook.com,campusbookrentals.com,casadellibro.com,cbomc.com,cengagebrain.com,chapters.indigo.ca,christianbook.com,ciscopress.com,coursesmart.com,cqpress.com,crafterschoice.com,crossings.com,cshlp.org,deseretbook.com,directtextbook.com,discountmags.com,doubledaybookclub.com,doubledaylargeprint.com,doverpublications.com,ebooks.com,ecampus.com,fellabooks.net,fictionwise.com,flatworldknowledge.com,goodreads.com,grolier.com,harpercollins.com,hayhouse.com,historybookclub.com,hpb.com,hpbmarketplace.com,interweave.com,iseeme.com,katiekazoo.com,knetbooks.com,learnoutloud.com,librarything.com,literaryguild.com,lulu.com,lww.com,macmillan.com,magazines.com,mbsdirect.net,militarybookclub.com,mypearsonstore.com,mysteryguild.com,netplaces.com,noble.com,novelguide.com,onespirit.com,oxfordjournals.org,paperbackswap.com,papy.co.jp,peachpit.com,penguin.com,penguingroup.com,pimsleur.com,powells.com,qpb.com,quepublishing.com,reviews.com,rhapsodybookclub.com,rodalestore.com,royalsocietypublishing.org,sagepub.com,scrubsmag.com,sfbc.com,simonandschuster.com,simonandschuster.net,simpletruths.com,teach12.net,textbooks.com,textbookx.com,thegoodcook.com,thriftbooks.com,tlsbooks.com,toshibabookplace.com,tumblebooks.com,urbookdownload.com,usedbooksearch.co.uk,valorebooks.com,valuemags.com,vialibri.net,wwnorton.com,zoobooks.com',
|
||||||
|
'literature' ],
|
||||||
|
[ '53.com,ally.com,bankofamerica.com,bbt.com,bnymellon.com,capitalone.com/bank/,chase.com,citi.com,citibank.com,citizensbank.com,citizensbankonline.com,creditonebank.com,everbank.com,hsbc.com,key.com,pnc.com,pncbank.com,rbs.co.uk,regions.com,sovereignbank.com,suntrust.com,tdbank.com,usaa.com,usbank.com,wachovia.com,wamu.com,wellsfargo.com,wsecu.org',
|
||||||
|
'banking' ],
|
||||||
|
[ '247wallst.com,bizjournals.com,bloomberg.com,businessweek.com,cnbc.com,cnnmoney.com,dowjones.com,easyhomesite.com,economist.com,entrepreneur.com,fastcompany.com,finance.yahoo.com,forbes.com,fortune.com,foxbusiness.com,ft.com,hbr.org,ibtimes.com,inc.com,manta.com,marketwatch.com,newsweek.com,online.wsj.com,qz.com,reuters.com,smartmoney.com,wsj.com',
|
||||||
|
'business news' ],
|
||||||
|
[ 'achievecard.com,americanexpress.com,barclaycardus.com,card.com,citicards.com,comparecards.com,creditcards.citi.com,discover.com,discovercard.com,experian.com,skylightpaycard.com,squareup.com,visa.com,visabuxx.com,visaextras.com',
|
||||||
|
'finance' ],
|
||||||
|
[ 'alliantcreditunion.org,connexuscu.org,lmcu.org,nasafcu.com,navyfcu.org,navyfederal.org,penfed.org,sccu.com,suncoastcreditunion.com,tinkerfcu.org,veridiancu.org',
|
||||||
|
'finance' ],
|
||||||
|
[ 'allbusiness.com,bankrate.com,buyersellertips.com,cboe.com,cnbcprime.com,coindesk.com,dailyfinance.com,dailyfx.com,dealbreaker.com,easierstreetdaily.com,economywatch.com,etfdailynews.com,etfdb.com,financeformulas.net,finviz.com,fool.com,forexpros.com,forexthreads.com,ftpress.com,fx-exchange.com,insidermonkey.com,investmentu.com,investopedia.com,investorjunkie.com,investors.com,kiplinger.com,minyanville.com,moneymorning.com,moneyning.com,moneysavingexpert.com,morningstar.com,nakedcapitalism.com,ncsoft.net,oilprice.com,realclearmarkets.com,rttnews.com,seekingalpha.com,silverdoctors.com,stockcharts.com,stockpickr.com,thefinancials.com,thestreet.com,wallstreetinsanity.com,wikinvest.com,xe.com,youngmoney.com',
|
||||||
|
'investing' ],
|
||||||
|
[ 'edwardjones.com,fidelity.com,goldmansachs.com,jpmorgan.com,ml.com,morganstanley.com,mymerrill.com,personal.vanguard.com,principal.com,schwab.com,schwabplan.com,scottrade.com,tdameritrade.com,troweprice.com,vanguard.com',
|
||||||
|
'investing' ],
|
||||||
|
[ '247lendinggroup.com,americanoneunsecured.com,avant.com,bestegg.com,chasestudentloans.com,eloan.com,gofundme.com,guidetolenders.com,kiva.org,lendacademy.com,lendingclub.com,lendingtree.com,lightstream.com,loanio.com,manageyourloans.com,meetearnest.com,microplace.com,netcredit.com,peer-lend.com,personalloans.com,prosper.com,salliemae.com,sofi.com,springleaf.com,uk.zopa.com,upstart.com',
|
||||||
|
'finance' ],
|
||||||
|
[ 'betterment.com,blooom.com,futureadvisor.com,kapitall.com,motifinvesting.com,personalcapital.com,wealthfront.com,wisebanyan.com',
|
||||||
|
'investing' ],
|
||||||
|
[ 'bancdebinary.com,cherrytrade.com,empireoption.net,etrade.com,firstrade.com,forex.com,interactivebrokers.com,ishares.com,optionsxpress.com,sharebuilder.com,thinkorswim.com,tradeking.com,trademonster.com,us.etrade.com,zecco.com',
|
||||||
|
'finance' ],
|
||||||
|
[ 'annualcreditreport.com,bluebird.com,credio.com,creditkarma.com,creditreport.com,cybersource.com,equifax.com,freecreditreport.com,freecreditscore.com,freedomdebtrelief.com,freescoreonline.com,mint.com,moneymappress.com,myfico.com,nationaldebtrelief.com,onesmartpenny.com,paypal.com,transunion.com,truecredit.com,upromise.com,vuebill.com,xpressbillpay.com,youneedabudget.com',
|
||||||
|
'personal finance' ],
|
||||||
|
[ 'angieslist.com,bloomberg.com,businessinsider.com,buydomains.com,domain.com,entrepreneur.com,fastcompany.com,forbes.com,fortune.com,godaddy.com,inc.com,manta.com,nytimes.com,openforum.com,register.com,salesforce.com,sba.gov,sbomag.com,shopsmall.americanexpress.com,smallbusiness.yahoo.com,squarespace.com,startupjournal.com,startupnation.com,weebly.com,wordpress.com,youngentrepreneur.com',
|
||||||
|
'business news' ],
|
||||||
|
[ '1040now.net,24hourtax.com,acttax.com,comparetaxsoftware.org,e-file.com,etax.com,free1040taxreturn.com,hrblock.com,intuit.com,irstaxdoctors.com,libertytax.com,octaxcol.com,pay1040.com,priortax.com,quickbooks.com,quickrefunds.com,rapidtax.com,refundschedule.com,taxact.com,taxactonline.com,taxefile.com,taxhead.com,taxhelptoday.me,taxsimple.org,turbotax.com',
|
||||||
|
'tax' ],
|
||||||
|
[ 'adeccousa.com,americasjobexchange.com,aoljobs.com,applicantpro.com,applicantstack.com,apply-4-jobs.com,apply2jobs.com,att.jobs,beyond.com,careerboutique.com,careerbuilder.com,careerflash.net,careerslocal.net,climber.com,coverlettersandresume.com,dice.com,diversityonecareers.com,employmentguide.com,everyjobforme.com,experteer.com,find.ly,findtherightjob.com,freelancer.com,gigats.com,glassdoor.com,governmentjobs.com,hrapply.com,hrdepartment.com,hrsmart.com,ihire.com,indeed.com,internships.com,itsmycareer.com,job-applications.com,job-hunt.org,job-interview-site.com,job.com,jobcentral.com,jobdiagnosis.com,jobhat.com,jobing.com,jobrapido.com,jobs.aol.com,jobs.net,jobsbucket.com,jobsflag.com,jobsgalore.com,jobsonline.com,jobsradar.com,jobster.com,jobtorch.com,jobungo.com,jobvite.com,juju.com,linkedin.com,livecareer.com,localjobster.com,mindtools.com,monster.com,myjobhelper.com,myperfectresume.com,payscale.com,pryor.com,quintcareers.com,randstad.com,recruitingcenter.net,resume-library.com,resume-now.com,roberthalf.com,salary.com,salaryexpert.com,simplyhired.com,smartrecruiters.com,snagajob.com,startwire.com,theladders.com,themuse.com,theresumator.com,thingamajob.com,usajobs.gov,ziprecruiter.com',
|
||||||
|
'career services' ],
|
||||||
|
[ 'americanheart.org,americanredcross.com,americares.org,catholiccharitiesusa.org,charitybuzz.com,charitynavigator.org,charitywater.org,directrelief.org,fao.org,habitat.org,hrw.org,imf.org,mskcc.org,ohchr.org,redcross.org,reliefweb.int,salvationarmyusa.org,savethechildren.org,un.org,undp.org,unep.org,unesco.org,unfpa.org,unhcr.org,unicef.org,unicefusa.org,unops.org,volunteermatch.org,wfp.org,who.int,worldbank.org',
|
||||||
|
'philanthropic' ],
|
||||||
|
[ 'academia.edu,albany.edu,american.edu,amity.edu,annauniv.edu,apus.edu,arizona.edu,ashford.edu,asu.edu,auburn.edu,austincc.edu,baylor.edu,bc.edu,berkeley.edu,brandeis.edu,brookings.edu,brown.edu,bu.edu,buffalo.edu,byu.edu,calpoly.edu,calstate.edu,caltech.edu,cam.ac.uk,cambridge.org,capella.edu,case.edu,clemson.edu,cmu.edu,colorado.edu,colostate-pueblo.edu,colostate.edu,columbia.edu,commnet.edu,cornell.edu,cpp.edu,csulb.edu,csun.edu,csus.edu,cuny.edu,cwru.edu,dartmouth.edu,depaul.edu,devry.edu,drexel.edu,du.edu,duke.edu,emory.edu,fau.edu,fcps.edu,fiu.edu,fordham.edu,fsu.edu,fullerton.edu,fullsail.edu,gatech.edu,gcu.edu,georgetown.edu,gmu.edu,gsu.edu,gwu.edu,harvard.edu,hawaii.edu,hbs.edu,iastate.edu,iit.edu,illinois.edu,indiana.edu,iu.edu,jhu.edu,k-state.edu,kent.edu,ku.edu,lamar.edu,liberty.edu,losrios.edu,lsu.edu,luc.edu,maine.edu,maricopa.edu,mass.edu,miami.edu,miamioh.edu,missouri.edu,mit.edu,mnscu.edu,monash.edu,msu.edu,mtu.edu,nau.edu,ncsu.edu,nd.edu,neu.edu,njit.edu,northeastern.edu,northwestern.edu,nova.edu,nyu.edu,odu.edu,ohio-state.edu,ohio.edu,okstate.edu,oregonstate.edu,osu.edu,ou.edu,ox.ac.uk,pdx.edu,pearson.com,phoenix.edu,pitt.edu,princeton.edu,psu.edu,purdue.edu,regis.edu,rice.edu,rit.edu,rochester.edu,rpi.edu,rutgers.edu,sc.edu,scu.edu,sdsu.edu,seattleu.edu,sfsu.edu,si.edu,sjsu.edu,snhu.edu,stanford.edu,stonybrook.edu,suny.edu,syr.edu,tamu.edu,temple.edu,towson.edu,ttu.edu,tufts.edu,ua.edu,uark.edu,ub.edu,uc.edu,uccs.edu,ucdavis.edu,ucf.edu,uchicago.edu,uci.edu,ucla.edu,uconn.edu,ucr.edu,ucsb.edu,ucsc.edu,ucsd.edu,ucsf.edu,udel.edu,udemy.com,ufl.edu,uga.edu,uh.edu,uic.edu,uillinois.edu,uiowa.edu,uiuc.edu,uky.edu,umass.edu,umb.edu,umbc.edu,umd.edu,umich.edu,umn.edu,umuc.edu,unc.edu,uncc.edu,unf.edu,uniminuto.edu,universityofcalifornia.edu,unl.edu,unlv.edu,unm.edu,unt.edu,uoc.edu,uoregon.edu,upc.edu,upenn.edu,upi.edu,uri.edu,usc.edu,usf.edu,usg.edu,usu.edu,uta.edu,utah.edu,utdallas.edu,utexas.edu,utk.edu,uvm.edu,uw.edu,uwm.edu,vanderbilt.edu,vccs.edu,vcu.edu,virginia.edu,vt.edu,waldenu.edu,washington.edu,wayne.edu,wednet.edu,wgu.edu,wisc.edu,wisconsin.edu,wm.edu,wmich.edu,wsu.edu,wustl.edu,wvu.edu,yale.edu',
|
||||||
|
'college' ],
|
||||||
|
[ 'collegeboard.com,collegeconfidential.com,collegeview.com,ecollege.com,finaid.org,find-colleges-now.com,ratemyprofessors.com,ratemyteachers.com,studentsreview.com',
|
||||||
|
'college' ],
|
||||||
|
[ 'actstudent.org,adaptedmind.com,aesoponline.com,archives.com,bibme.org,blackboard.com,bookrags.com,cengage.com,chegg.com,classdojo.com,classzone.com,cliffsnotes.com,coursecompass.com,educationconnection.com,educationdynamics.com,ets.org,familysearch.org,fastweb.com,genealogy.com,gradesaver.com,instructure.com,khanacademy.org,learn4good.com,mathway.com,mathxl.com,mcgraw-hill.com,merriam-webster.com,mheducation.com,niche.com,openstudy.com,pearsoned.com,pearsonmylabandmastering.com,pearsonsuccessnet.com,poptropica.com,powerschool.com,proprofs.com,purplemath.com,quizlet.com,readwritethink.org,renlearn.com,rhymezone.com,schoolloop.com,schoology.com,smithsonianmag.com,sparknotes.com,study.com,studyisland.com,studymode.com,synonym.com,teacherprobs.com,teacherspayteachers.com,tutorvista.com,vocabulary.com,yourschoolmatch.com',
|
||||||
|
'education' ],
|
||||||
|
[ 'browardschools.com,k12.ca.us,k12.fl.us,k12.ga.us,k12.in.us,k12.mn.us,k12.mo.us,k12.nc.us,k12.nj.us,k12.oh.us,k12.va.us,k12.wi.us',
|
||||||
|
'education' ],
|
||||||
|
[ 'coolmath-games.com,coolmath.com,coolmath4kids.com,coolquiz.com,funbrain.com,funtrivia.com,gamesforthebrain.com,girlsgogames.com,hoodamath.com,lumosity.com,math.com,mathsisfun.com,trivia.com,wizard101.com',
|
||||||
|
'learning games' ],
|
||||||
|
[ 'askmen.com,boredomtherapy.com,buzzfeed.com,complex.com,dailymotion.com,elitedaily.com,gawker.com,howstuffworks.com,instagram.com,madamenoire.com,polygon.com,ranker.com,rollingstone.com,ted.com,theblaze.com,thechive.com,thecrux.com,thedailybeast.com,thoughtcatalog.com,uproxx.com,upworthy.com,zergnet.com',
|
||||||
|
'entertainment' ],
|
||||||
|
[ '11points.com,7gid.com,adultswim.com,break.com,cheezburger.com,collegehumor.com,cracked.com,dailydawdle.com,damnlol.com,dumb.com,dumblaws.com,ebaumsworld.com,explosm.net,failblog.org,fun-gallery.com,funnygig.com,funnyjunk.com,funnymama.com,funnyordie.com,funnytear.com,funplus.com,glassgiant.com,goingviralposts.com,gorillamask.net,i-am-bored.com,icanhascheezburger.com,ifunny.com,imjussayin.co,inherentlyfunny.com,izismile.com,jokes.com,keenspot.com,knowyourmeme.com,laughstub.com,memebase.com,mememaker.net,metacafe.com,mylol.com,picslist.com,punoftheday.com,queendom.com,rajnikantvscidjokes.in,regretfulmorning.com,shareonfb.com,somethingawful.com,stupidvideos.com,superfunnyimages.com,thedailywh.at,theonion.com,tosh.comedycentral.com,uberhumor.com,welltimedphotos.com',
|
||||||
|
'humor' ],
|
||||||
|
[ 'air.tv,amctheatres.com,boxofficemojo.com,cinapalace.com,cinaplay.com,cinemablend.com,cinemark.com,cinematical.com,collider.com,comicbookmovie.com,comingsoon.net,crackle.com,denofgeek.us,dreamworks.com,empireonline.com,enstarz.com,fandango.com,filmschoolrejects.com,flickeringmyth.com,flixster.com,fullmovie2k.com,g2g.fm,galleryhip.com,hollywood.com,hollywoodreporter.com,iglomovies.com,imdb.com,indiewire.com,instantwatcher.com,joblo.com,kickass.to,kissdrama.net,marcustheatres.com,megashare9.com,moviefone.com,movieinsider.com,moviemistakes.com,moviepilot.com,movierulz.com,movies.com,movies.yahoo.com,movieseum.com,movietickets.com,movieweb.com,mrmovietimes.com,mymovieshub.com,netflix.com,onlinemovies.pro,pelis24.com,projectfreetv.ch,redbox.com,regmovies.com,repelis.tv,rogerebert.suntimes.com,ropeofsilicon.com,rottentomatoes.com,sidereel.com,slashfilm.com,solarmovie.is,starwars.com,superherohype.com,tcm.com,twomovies.us,variety.com,vimeo.com,viooz.ac,warnerbros.com,watchfree.to,wbredirect.com,youtubeonfire.com,zmovie.tw,zumvo.com',
|
||||||
|
'movie' ],
|
||||||
|
[ '1079ishot.com,2dopeboyz.com,8tracks.com,acdc.com,allaccess.com,allhiphop.com,allmusic.com,audiofanzine.com,audiomack.com,azlyrics.com,baeblemusic.com,bandsintown.com,billboard.com,brooklynvegan.com,brunomars.com,buzznet.com,cmt.com,coachella.com,consequenceofsound.net,contactmusic.com,countryweekly.com,dangerousminds.net,datpiff.com,ddotomen.com,diffuser.fm,directlyrics.com,djbooth.net,eventful.com,fireflyfestival.com,genius.com,guitartricks.com,harmony-central.com,hiphopdx.com,hiphopearly.com,hypem.com,idolator.com,iheart.com,jambase.com,kanyetothe.com,knue.com,lamusica.com,last.fm,livemixtapes.com,loudwire.com,lyricinterpretations.com,lyrics.net,lyricsbox.com,lyricsmania.com,lyricsmode.com,metal-archives.com,metrolyrics.com,mp3.com,mtv.co.uk,myspace.com,newnownext.com,noisetrade.com,okayplayer.com,pandora.com,phish.com,pigeonsandplanes.com,pitchfork.com,popcrush.com,radio.com,rap-up.com,rdio.com,reverbnation.com,revolvermag.com,rockhall.com,saavn.com,songlyrics.com,soundcloud.com,spin.com,spinrilla.com,spotify.com,stereogum.com,stereotude.com,talkbass.com,tasteofcountry.com,thebacklot.com,theboombox.com,theboot.com,thissongissick.com,tunesbaby.com,ultimate-guitar.com,ultimateclassicrock.com,vevo.com,vibe.com,vladtv.com,whosampled.com,wikibit.me,worldstarhiphop.com,wyrk.com,xxlmag.com',
|
||||||
|
'music' ],
|
||||||
|
[ 'aceshowbiz.com,aintitcoolnews.com,allkpop.com,askkissy.com,atraf.co.il,audioboom.com,beamly.com,beyondhollywood.com,blastr.com,blippitt.com,bollywoodlife.com,bossip.com,buzzlamp.com,buzzsugar.com,cambio.com,celebdirtylaundry.com,celebrity-gossip.net,celebuzz.com,chisms.net,comicsalliance.com,concertboom.com,crushable.com,cultbox.co.uk,dailyentertainmentnews.com,dayscafe.com,deadline.com,deathandtaxesmag.com,diaryofahollywoodstreetking.com,digitalspy.com,dlisted.com,egotastic.com,empirenews.net,enelbrasero.com,eonline.com,etonline.com,ew.com,extratv.com,facade.com,famousfix.com,fanaru.com,fanpop.com,fansshare.com,fhm.com,geektyrant.com,glamourpage.com,gossipcenter.com,gossipcop.com,heatworld.com,hlntv.com,hollyscoop.com,hollywoodlife.com,hollywoodtuna.com,hypable.com,infotransfer.net,insideedition.com,interaksyon.com,jezebel.com,justjared.buzznet.com,justjared.com,justjaredjr.com,komando.com,koreaboo.com,laineygossip.com,maxgo.com,maxim.com,maxviral.com,mediatakeout.com,mosthappy.com,moviestalk.com,my.ology.com,nationalenquirer.com,necolebitchie.com,ngoisao.net,nofilmschool.com,nolocreo.com,octane.tv,okmagazine.com,ouchpress.com,people.com,peopleenespanol.com,perezhilton.com,pinkisthenewblog.com,platotv.tv,playbill.com,playbillvault.com,playgroundmag.net,popsugar.com,purepeople.com,radaronline.com,rantchic.com,realitytea.com,reshareworthy.com,rinkworks.com,ripbird.com,sara-freder.com,screencrush.com,screenjunkies.com,soapcentral.com,soapoperadigest.com,sobadsogood.com,socialitelife.com,sourcefednews.com,splitsider.com,starcasm.net,starmagazine.com,starpulse.com,straightfromthea.com,stupiddope.com,tbn.org,theawesomedaily.com,theawl.com,thefrisky.com,thefw.com,thehollywoodgossip.com,theresacaputo.com,thesuperficial.com,thezooom.com,tmz.com,tvnotas.com.mx,twanatells.com,usmagazine.com,vanityfair.com,vanswarpedtour.com,vietgiaitri.com,viral.buzz,vulture.com,wakavision.com,worthytales.net,wwtdd.com',
|
||||||
|
'entertainment news' ],
|
||||||
|
[ 'abc.go.com,abcfamily.go.com,abclocal.go.com,accesshollywood.com,aetv.com,amctv.com,animalplanet.com,bbcamerica.com,bet.com,biography.com,bravotv.com,cartoonnetwork.com,cbn.com,cbs.com,cc.com,centrictv.com,cinemax.com,comedycentral.com,ctv.ca,cwtv.com,daytondailynews.com,drphil.com,dsc.discovery.com,fox.com,fox23.com,fox4news.com,fxnetworks.com,hbo.com,history.com,hulu.com,ifc.com,iqiyi.com,jeopardy.com,kfor.com,logotv.com,mtv.com,myfoxchicago.com,myfoxdc.com,myfoxmemphis.com,myfoxphilly.com,nbc.com,nbcchicago.com,oxygen.com,pbs.org,pbskids.org,rachaelrayshow.com,rtve.es,scifi.com,sho.com,showtimeanytime.com,spike.com,sundance.tv,syfy.com,tbs.com,teamcoco.com,telemundo.com,thedoctorstv.com,titantv.com,tlc.com,tlc.discovery.com,tnt.tv,tntdrama.com,tv.com,tvguide.com,tvseriesfinale.com,usanetwork.com,uvidi.com,vh1.com,viki.com,watchcartoononline.com,watchseries-online.ch,wetv.com,wheeloffortune.com,whio.com,wnep.com,wral.com,wtvr.com,xfinitytv.com,yidio.com',
|
||||||
|
'TV show' ],
|
||||||
|
[ 'americanhiking.org,appalachiantrail.org,canadiangeographic.ca,defenders.org,discovermagazine.com,discoveroutdoors.com,dsc.discovery.com,earthtouchnews.com,edf.org,epa.gov,ewg.org,fishngame.org,foe.org,fs.fed.us,geography.about.com,landtrustalliance.org,nationalgeographic.com,nature.com,nrdc.org,nwf.org,outdoorchannel.com,outdoors.org,seedmagazine.com,trcp.org,usda.gov,worldwildlife.org',
|
||||||
|
'environment' ],
|
||||||
|
[ 'abbreviations.com,abcmouse.com,abcya.com,achieve3000.com,ancestry.com,animaljam.com,babble.com,babycenter.com,babynamespedia.com,behindthename.com,bestmomstv.com,brainyquote.com,cafemom.com,citationmachine.net,clubpenguin.com,cutemunchkins.com,discoveryeducation.com,disney.com,easybib.com,education.com,enotes.com,everydayfamily.com,familyeducation.com,gamefaqs.com,greatschools.org,hrw.com,imvu.com,infoplease.com,itsybitsysteps.com,justmommies.com,k12.com,kidsactivitiesblog.com,mathwarehouse.com,mom.me,mom365.com,mommyshorts.com,momswhothink.com,momtastic.com,monsterhigh.com,myheritage.com,nameberry.com,nickmom.com,pampers.com,parenthood.com,parenting.com,parenting.ivillage.com,parents.com,parentsociety.com,raz-kids.com,regentsprep.org,scarymommy.com,scholastic.com,shmoop.com,softschools.com,spanishdict.com,starfall.com,thebump.com,thefreedictionary.com,thenest.com,thinkbabynames.com,todaysparent.com,webkinz.com,whattoexpect.com',
|
||||||
|
'family' ],
|
||||||
|
[ 'americangirl.com,barbie.com,barbiecollectibles.com,cartoonnetworkshop.com,chuckecheese.com,coloring.ws,disney.co.uk,disney.com.au,disney.go.com,disney.in,disneychannel-asia.com,disneyinternational.com,disneyjunior.com,disneylatino.com,disneyme.com,dltk-kids.com,dressupone.com,fantage.com,funbrainjr.com,hotwheels.com,icarly.com,kiwicrate.com,marvel.com,marvelkids.com,mattelgames.com,maxsteel.com,monkeyquest.com,nick-asia.com,nick.co.uk,nick.com,nick.tv,nickelodeon.com.au,nickjr.co.uk,nickjr.com,ninjakiwi.com,notdoppler.com,powerrangers.com,sciencekids.co.nz,search.disney.com,seventeen.com,teennick.com,theslap.com,yepi.com',
|
||||||
|
'family' ],
|
||||||
|
[ 'alabama.gov,archives.gov,bls.gov,ca.gov,cancer.gov,cdc.gov,census.gov,cia.gov,cms.gov,commerce.gov,ct.gov,delaware.gov,dhs.gov,doi.gov,dol.gov,dot.gov,ed.gov,eftps.gov,epa.gov,fbi.gov,fda.gov,fema.gov,flhsmv.gov,ftc.gov,ga.gov,georgia.gov,gpo.gov,hhs.gov,house.gov,hud.gov,illinois.gov,in.gov,irs.gov,justice.gov,ky.gov,loc.gov,louisiana.gov,maryland.gov,mass.gov,michigan.gov,mo.gov,nih.gov,nj.gov,nps.gov,ny.gov,nyc.gov,ohio.gov,ok.gov,opm.gov,oregon.gov,pa.gov,recreation.gov,sba.gov,sc.gov,sec.gov,senate.gov,state.fl.us,state.gov,state.il.us,state.ma.us,state.mi.us,state.mn.us,state.nc.us,state.ny.us,state.oh.us,state.pa.us,studentloans.gov,telldc.com,texas.gov,tn.gov,travel.state.gov,tsa.gov,usa.gov,uscis.gov,uscourts.gov,usda.gov,usdoj.gov,usembassy.gov,usgs.gov,utah.gov,va.gov,virginia.gov,wa.gov,whitehouse.gov,wi.gov,wisconsin.gov',
|
||||||
|
'government' ],
|
||||||
|
[ 'beachbody.com,bodybuilding.com,caloriecount.com,extremefitness.com,fitbit.com,fitday.com,fitnessmagazine.com,fitnessonline.com,fitwatch.com,livestrong.com,maxworkouts.com,mensfitness.com,menshealth.com,muscleandfitness.com,muscleandfitnesshers.com,myfitnesspal.com,shape.com,womenshealthmag.com',
|
||||||
|
'health & fitness' ],
|
||||||
|
[ 'activebeat.com,alliancehealth.com,beyonddiet.com,caring.com,complete-health-and-happiness.com,diabeticconnect.com,doctoroz.com,everydayhealth.com,followmyhealth.com,greatist.com,health.com,healthboards.com,healthcaresource.com,healthgrades.com,healthguru.com,healthination.com,healthtap.com,helpguide.org,iherb.com,kidshealth.org,lifescript.com,lovelivehealth.com,medicaldaily.com,mercola.com,perfectorigins.com,prevention.com,qualityhealth.com,questdiagnostics.com,realself.com,sharecare.com,sparkpeople.com,spryliving.com,steadyhealth.com,symptomfind.com,ucomparehealthcare.com,vitals.com,webmd.com,weightwatchers.com,wellness.com,zocdoc.com',
|
||||||
|
'health & wellness' ],
|
||||||
|
[ 'aetna.com,anthem.com,athenahealth.com,bcbs.com,bluecrossca.com,cigna.benefitnation.net,cigna.com,cigna.healthplan.com,ehealthcare.com,ehealthinsurance.com,empireblue.com,goldenrule.com,healthcare.gov,healthnet.com,humana-medicare.com,humana.com,kaiserpermanente.org,metlife.com,my.cigna.com,mybenefits.metlife.com,myuhc.com,uhc.com,unitedhealthcareonline.com,walterrayholt.com',
|
||||||
|
'health insurance' ],
|
||||||
|
[ 'aafp.org,americanheart.org,apa.org,cancer.org,cancercenter.com,caremark.com,clevelandclinic.org,diabetesfree.org,drugs.com,emedicinehealth.com,express-scripts.com,familydoctor.org,goodrx.com,healthcaremagic.com,healthfinder.gov,healthline.com,ieee.org,intelihealth.com,labcorp.com,livecellresearch.com,mayoclinic.com,mayoclinic.org,md.com,medcohealth.com,medhelp.org,medicalnewstoday.com,medicare.gov,medicaresupplement.com,medicinenet.com,medscape.com,memorialhermann.org,merckmanuals.com,patient.co.uk,psychcentral.com,psychology.org,psychologytoday.com,rightdiagnosis.com,rxlist.com,socialpsychology.org,spine-health.com,who.int',
|
||||||
|
'health & wellness' ],
|
||||||
|
[ 'aaa.com,aig.com,allianz-assistance.com,allstate.com,allstateagencies.com,amfam.com,amica.com,autoquotesdirect.com,esurance.com,farmers.com,farmersagent.com,geico.com,general-car-insurance-quotes.net,insurance.com,libertymutual.com,libertymutualgroup.com,mercuryinsurance.com,nationwide.com,progressive.com,progressiveagent.com,progressiveinsurance.com,provide-insurance.com,safeco.com,statefarm.com,thehartford.com,travelers.com,usaa.com',
|
||||||
|
'insurance' ],
|
||||||
|
[ '101cookbooks.com,allrecipes.com,bettycrocker.com,bonappetit.com,chocolateandzucchini.com,chow.com,chowhound.chow.com,cookinglight.com,cooks.com,cooksillustrated.com,cooksrecipes.com,delish.com,eater.com,eatingwell.com,epicurious.com,food.com,foodandwine.com,foodgawker.com,foodnetwork.com,gourmet.com,grouprecipes.com,homemaderecipes.co,iheartnaptime.net,kraftfoods.com,kraftrecipes.com,myrecipes.com,opentable.com,pillsbury.com,recipe.com,recipesource.com,recipezaar.com,saveur.com,seriouseats.com,simplyrecipes.com,smittenkitchen.com,southernliving.com,supercook.com,tasteofhome.com,tastespotting.com,technicpack.net,thekitchn.com,urbanspoon.com,wonderhowto.com,yelp.com,yummly.com,zagat.com',
|
||||||
|
'food & lifestyle' ],
|
||||||
|
[ 'aarp.org,allure.com,bustle.com,cosmopolitan.com,diply.com,eharmony.com,elle.com,glamour.com,grandascent.com,harpersbazaar.com,hellogiggles.com,instructables.com,instyle.com,marieclaire.com,match.com,mindbodygreen.com,nymag.com,okcupid.com,petco.com,photobucket.com,pof.com,rantlifestyle.com,redbookmag.com,reddit.com,sheknows.com,style.com,stylebistro.com,theilovedogssite.com,theknot.com,thescene.com,thrillist.com,vogue.com,womansday.com,youngcons.com,yourdictionary.com',
|
||||||
|
'lifestyle' ],
|
||||||
|
[ 'apartmentratings.com,apartmenttherapy.com,architectmagazine.com,architecturaldigest.com,askthebuilder.com,bhg.com,bobvila.com,countryhome.com,countryliving.com,davesgarden.com,decor8blog.com,decorpad.com,diycozyhome.com,diyideas.com,diynetwork.com,doityourself.com,domainehome.com,dwell.com,elledecor.com,familyhandyman.com,frontdoor.com,gardenguides.com,gardenweb.com,getdecorating.com,goodhousekeeping.com,hgtv.com,hgtvgardens.com,hobbylobby.com,homeadvisor.com,homerepair.about.com,hometalk.com,hometime.com,hometips.com,housebeautiful.com,houzz.com,inhabitat.com,lonny.com,makingitlovely.com,marthastewart.com,michaels.com,myhomeideas.com,realsimple.com,remodelista.com,shanty-2-chic.com,styleathome.com,thehandmadehome.net,thehealthyhomeeconomist.com,thisoldhouse.com,traditionalhome.com,trulia.com,younghouselove.com',
|
||||||
|
'home & lifestyle' ],
|
||||||
|
[ '10best.com,10tv.com,11alive.com,19actionnews.com,9news.com,abcnews.com,abcnews.go.com,adweek.com,ajc.com,anchorfree.us,arcamax.com,austin360.com,azcentral.com,bbc.co.uk,boston.com,bostonglobe.com,capecodonline.com,cbsnews.com,cheatsheet.com,chicagotribune.com,chron.com,citylab.com,cnn.com,csmonitor.com,dailyitem.com,dailymail.co.uk,dallasnews.com,eleconomista.es,examiner.com,fastcolabs.com,fivethirtyeight.com,foursquare.com,foxcarolina.com,foxnews.com,globalnews.ca,greatergood.com,guardian.co.uk,historynet.com,huffingtonpost.co.uk,huffingtonpost.com,ijreview.com,independent.co.uk,journal-news.com,kare11.com,kcra.com,kctv5.com,kgw.com,khou.com,king5.com,kirotv.com,kitv.com,kmbc.com,knoxnews.com,kpho.com,kptv.com,kron4.com,ksdk.com,ksl.com,ktvb.com,kvue.com,kxan.com,latimes.com,lifehack.org,littlethings.com,mailtribune.com,mic.com,mirror.co.uk,msn.com,msnbc.com,msnbc.msn.com,myfoxboston.com,nbcnews.com,nbcnewyork.com,newburyportnews.com,news.bbc.co.uk,news.yahoo.com,news12.com,newschannel10.com,newsday.com,newser.com,newsmax.com,newyorker.com,nj.com,nj1015.com,npr.org,nydailynews.com,nypost.com,nytimes.com,palmbeachpost.com,patch.com,philly.com,phys.org,poconorecord.com,prnewswire.com,rare.us,realclearworld.com,record-eagle.com,richmond.com,rt.com,salemnews.com,salon.com,sfgate.com,slate.com,statesman.com,suntimes.com,takepart.com,telegraph.co.uk,theatlantic.com,thedailystar.com,theguardian.com,theroot.com,theverge.com,time.com,timesonline.co.uk,topix.com,usatoday.com,usatoday30.usatoday.com,usnews.com,vice.com,vox.com,wane.com,washingtonpost.com,washingtontimes.com,wave3.com,wavy.com,wbaltv.com,wbir.com,wcnc.com,wdbj7.com,westernjournalism.com,wfaa.com,wfsb.com,wftv.com,wgal.com,wishtv.com,wisn.com,wistv.com,wivb.com,wkyc.com,wlwt.com,wmur.com,woodtv.com,wpxi.com,wsbtv.com,wsfa.com,wsmv.com,wsoctv.com,wthr.com,wtnh.com,wtsp.com,wwltv.com,wyff4.com,wzzm13.com',
|
||||||
|
'news' ],
|
||||||
|
[ 'aei.org,breitbart.com,conservativetalknow.com,conservativetribune.com,dailykos.com,ddo.com,drudgereport.com,dscc.org,foreignpolicy.com,franklinprosperityreport.com,freedomworks.org,macleans.ca,mediamatters.org,militarytimes.com,nationaljournal.com,nationalreview.com,politicalwire.com,politico.com,pressrepublican.com,qpolitical.com,realclearpolitics.com,talkingpointsmemo.com,thehill.com,thenation.com,thinkprogress.org,tnr.com,worldoftanks.eu',
|
||||||
|
'news' ],
|
||||||
|
[ 'americanscientist.org,discovermagazine.com,iflscience.com,livescience.com,nasa.gov,nationalgeographic.com,nature.com,newscientist.com,popsci.com,sciencedaily.com,sciencemag.org,sciencenews.org,scientificamerican.com,space.com,zmescience.com',
|
||||||
|
'science' ],
|
||||||
|
[ 'accuweather.com,intellicast.com,noaa.gov,ssa.gov,theweathernetwork.com,weather.com,weather.gov,weather.yahoo.com,weatherbug.com,weatherunderground.com,weatherzone.com.au,wunderground.com,www.weather.com',
|
||||||
|
'weather' ],
|
||||||
|
[ 'bhphotovideo.com,bigfolio.com,bigstockphoto.com,cameralabs.com,canonrumors.com,canstockphoto.com,digitalcamerareview.com,dpreview.com,expertphotography.com,gettyimages.com,icp.org,imaging-resource.com,intothedarkroom.com,istockphoto.com,nikonusa.com,photos.com,shutterstock.com,slrgear.com,the-digital-picture.com,thephotoargus.com,usa.canon.com,whatdigitalcamera.com,zenfolio.com',
|
||||||
|
'photography' ],
|
||||||
|
[ 'abercrombie.com,ae.com,aeropostale.com,anthropologie.com,bananarepublic.com,buycostumes.com,chadwicks.com,express.com,forever21.com,freepeople.com,hm.com,hollisterco.com,jcrew.com,jessicalondon.com,kingsizemen.com,lordandtaylor.com,lulus.com,metrostyle.com,nomorerack.com,oldnavy.com,oldnavy.gap.com,polyvore.com,rackroomshoes.com,ralphlauren.com,refinery29.com,roamans.com,sammydress.com,shop.nordstrom.com,shopbop.com,topshop.com,urbanoutfitters.com,victoriassecret.com,wetseal.com,womanwithin.com',
|
||||||
|
'shopping' ],
|
||||||
|
[ 'bizrate.com,compare99.com,coupons.com,dealtime.com,epinions.com,junglee.com,kijiji.ca,pricegrabber.com,pronto.com,redplum.com,retailmenot.com,shopping.com,shopzilla.com,smarter.com,valpak.com',
|
||||||
|
'shopping' ],
|
||||||
|
[ '123greetings.com,1800baskets.com,1800flowers.com,americangreetings.com,birthdayexpress.com,bluemountain.com,e-cards.com,egreetings.com,florists.com,ftd.com,gifts.com,groupcard.com,harryanddavid.com,hipstercards.com,kabloom.com,personalcreations.com,proflowers.com,redenvelope.com,someecards.com',
|
||||||
|
'flowers & gifts' ],
|
||||||
|
[ '6pm.com,alibaba.com,aliexpress.com,amazon.co.uk,amazon.com,asos.com,bathandbodyworks.com,bloomingdales.com,bradsdeals.com,buy.com,cafepress.com,circuitcity.com,clarkhoward.com,consumeraffairs.com,costco.com,cvs.com,dhgate.com,diapers.com,dillards.com,ebates.com,ebay.com,ebaystores.com,etsy.com,fingerhut.com,groupon.com,hsn.com,jcpenney.com,kmart.com,kohls.com,kroger.com,lowes.com,macys.com,menards.com,nextag.com,nordstrom.com,orientaltrading.com,overstock.com,qvc.com,racked.com,rewardsnetwork.com,samsclub.com,sears.com,sephora.com,shopathome.com,shopify.com,shopstyle.com,slickdeals.net,soap.com,staples.com,target.com,toptenreviews.com,vistaprint.com,walgreens.com,walmart.ca,walmart.com,wayfair.com,zappos.com,zazzle.com,zulily.com',
|
||||||
|
'shopping' ],
|
||||||
|
[ 'acehardware.com,ashleyfurniture.com,bedbathandbeyond.com,brylanehome.com,casa.com,cb2.com,crateandbarrel.com,dwr.com,ethanallen.com,furniture.com,harborfreight.com,hayneedle.com,homedecorators.com,homedepot.com,ikea.com,info.ikea-usa.com,landofnod.com,pier1.com,plowhearth.com,potterybarn.com,restorationhardware.com,roomandboard.com,westelm.com,williams-sonoma.com',
|
||||||
|
'home shopping' ],
|
||||||
|
[ 'alexandermcqueen.com,bergdorfgoodman.com,bottegaveneta.com,burberry-bluelabel.com,burberry.com,chanel.com,christianlouboutin.com,coach.com,diesel.com,dior.com,dolcegabbana.com,dolcegabbana.it,fendi.com,ferragamo.com,giorgioarmani.com,givenchy.com,gucci.com,guess.com,hermes.com,jeanpaulgaultier.com,jimmychoo.com,juicycouture.com,katespade.com,louisvuitton.com,manoloblahnik.com,marcjacobs.com,neimanmarcus.com,net-a-porter.com,paulsmith.co.uk,prada.com,robertocavalli.com,saksfifthavenue.com,toryburch.com,valentino.com,versace.com,vuitton.com,ysl.com,yslbeautyus.com',
|
||||||
|
'luxury shopping' ],
|
||||||
|
[ 'bargainseatsonline.com,livenation.com,stubhub.com,ticketfly.com,ticketliquidator.com,ticketmaster.com,tickets.com,ticketsnow.com,ticketweb.com,vividseats.com',
|
||||||
|
'events & tickets' ],
|
||||||
|
[ 'babiesrus.com,brothers-brick.com,etoys.com,fao.com,fisher-price.com,hasbro.com,hasbrotoyshop.com,lego.com,legoland.com,mattel.com,toys.com,toysrus.com,toystogrowon.com,toywiz.com',
|
||||||
|
'toys & games' ],
|
||||||
|
[ 'challengegames.nfl.com,fantasy.nfl.com,fantasyfootballblog.net,fantasyfootballcafe.com,fantasyfootballnerd.com,fantasysmarts.com,fftoday.com,fftoolbox.com,football.fantasysports.yahoo.com,footballsfuture.com,mrfootball.com,officefootballpool.com,thehuddle.com',
|
||||||
|
'fantasy football' ],
|
||||||
|
[ 'dailyjoust.com,draftday.com,draftking.com,draftkings.com,draftstreet.com,fanduel.com,realmoneyfantasyleagues.com,thedailyaudible.com',
|
||||||
|
'fantasy sports' ],
|
||||||
|
[ 'cdmsports.com,fanball.com,fantasyguru.com,fantasynews.cbssports.com,fantasyquestions.com,fantasyrundown.com,fantasysharks.com,fantasysports.yahoo.com,fantazzle.com,fantrax.com,fleaflicker.com,junkyardjake.com,kffl.com,mockdraftcentral.com,myfantasyleague.com,rototimes.com,rotowire.com,rotoworld.com,rtsports.com,whatifsports.com',
|
||||||
|
'fantasy sports' ],
|
||||||
|
[ 'football.about.com,football.com,footballoutsiders.com,nationalfootballpost.com,nflalumni.org,nflpa.com,nfltraderumors.co,profootballhof.com,profootballtalk.com,profootballtalk.nbcsports.com,profootballweekly.com',
|
||||||
|
'football' ],
|
||||||
|
[ '49ers.com,atlantafalcons.com,azcardinals.com,baltimoreravens.com,bengals.com,buccaneers.com,buffalobills.com,chargers.com,chicagobears.com,clevelandbrowns.com,colts.com,dallascowboys.com,denverbroncos.com,detroitlions.com,giants.com,houstontexans.com,jaguars.com,kcchiefs.com,miamidolphins.com,neworleanssaints.com,newyorkjets.com,packers.com,panthers.com,patriots.com,philadelphiaeagles.com,raiders.com,redskins.com,seahawks.com,steelers.com,stlouisrams.com,titansonline.com,vikings.com',
|
||||||
|
'football' ],
|
||||||
|
[ 'baseball-reference.com,baseballamerica.com,europeantour.com,golf.com,golfdigest.com,lpga.com,milb.com,minorleagueball.com,mlb.com,mlb.mlb.com,nascar.com,nba.com,ncaa.com,nhl.com,pga.com,pgatour.com,prowrestling.com,surfermag.com,surfline.com,surfshot.com,thehockeynews.com,tsn.com,ufc.com,worldgolfchampionships.com,wwe.com',
|
||||||
|
'sports' ],
|
||||||
|
[ '247sports.com,active.com,armslist.com,basketball-reference.com,bigten.org,bleacherreport.com,bleedinggreennation.com,bloodyelbow.com,cagesideseats.com,cbssports.com,cinesport.com,collegespun.com,cricbuzz.com,crictime.com,csnphilly.com,csnwashington.com,cstv.com,eastbay.com,espn.com,espn.go.com,espncricinfo.com,espnfc.com,espnfc.us,espnradio.com,eteamz.com,fanatics.com,fansided.com,fbschedules.com,fieldandstream.com,flightclub.com,foxsports.com,givemesport.com,goduke.com,goheels.com,golfchannel.com,golfnow.com,grantland.com,grindtv.com,hoopshype.com,icc-cricket.com,imleagues.com,kentuckysportsradio.com,larrybrownsports.com,leaguelineup.com,maxpreps.com,mlbtraderumors.com,mmafighting.com,mmajunkie.com,mmamania.com,msn.foxsports.com,myscore.com,nbcsports.com,nbcsports.msnbc.com,nesn.com,rantsports.com,realclearsports.com,reserveamerica.com,rivals.com,runnersworld.com,sbnation.com,scout.com,sherdog.com,si.com,speedsociety.com,sportingnews.com,sports.yahoo.com,sportsillustrated.cnn.com,sportsmanias.com,sportsmonster.us,sportsonearth.com,stack.com,teamworkonline.com,thebiglead.com,thescore.com,trails.com,triblive.com,upickem.net,usatodayhss.com,watchcric.net,yardbarker.com',
|
||||||
|
'sports news' ],
|
||||||
|
[ 'adidas.com,backcountry.com,backcountrygear.com,cabelas.com,champssports.com,competitivecyclist.com,dickssportinggoods.com,finishline.com,footlocker.com,ladyfootlocker.com,modells.com,motosport.com,mountaingear.com,newbalance.com,nike.com,patagonia.com,puma.com,reebok.com,sportsmansguide.com,steepandcheap.com,tgw.com,thenorthface.com',
|
||||||
|
'sports & outdoor goods' ],
|
||||||
|
[ 'airdroid.com,android-developers.blogspot.com,android.com,androidandme.com,androidapplications.com,androidapps.com,androidauthority.com,androidcommunity.com,androidfilehost.com,androidforums.com,androidguys.com,androidheadlines.com,androidpit.com,androidspin.com,androidtapp.com,androinica.com,droid-life.com,droidforums.net,droidviews.com,droidxforums.com,forum.xda-developers.com,phandroid.com,play.google.com,shopandroid.com,talkandroid.com,theandroidsoul.com,thedroidguy.com,videodroid.org',
|
||||||
|
'technology' ],
|
||||||
|
[ '9to5mac.com,appadvice.com,apple.com,appleinsider.com,appleturns.com,appsafari.com,cultofmac.com,everymac.com,insanelymac.com,iphoneunlockspot.com,isource.com,itunes.apple.com,lowendmac.com,mac-forums.com,macdailynews.com,macenstein.com,macgasm.net,macintouch.com,maclife.com,macnews.com,macnn.com,macobserver.com,macosx.com,macpaw.com,macrumors.com,macsales.com,macstories.net,macupdate.com,macuser.co.uk,macworld.co.uk,macworld.com,maxiapple.com,spymac.com,theapplelounge.com',
|
||||||
|
'technology' ],
|
||||||
|
[ 'adobe.com,asus.com,avast.com,data.com,formstack.com,gboxapp.com,gotomeeting.com,hp.com,htc.com,ibm.com,intel.com,java.com,logme.in,mcafee.com,mcafeesecure.com,microsoftstore.com,norton.com,office.com,office365.com,opera.com,oracle.com,proboards.com,samsung.com,sourceforge.net,squarespace.com,techtarget.com,ultipro.com,uniblue.com,web.com,winzip.com',
|
||||||
|
'technology' ],
|
||||||
|
[ '3dprint.com,4sysops.com,access-programmers.co.uk,accountingweb.com,afterdawn.com,akihabaranews.com,appsrumors.com,avg.com,belkin.com,besttechinfo.com,betanews.com,botcrawl.com,breakingmuscle.com,cheap-phones.com,chip.de,chip.eu,citeworld.com,cleanpcremove.com,commentcamarche.net,computer.org,computerhope.com,computershopper.com,computerweekly.com,contextures.com,coolest-gadgets.com,csoonline.com,daniweb.com,datacenterknowledge.com,ddj.com,devicemag.com,digitaltrends.com,dottech.org,dslreports.com,edugeek.net,eetimes.com,epic.com,eurekalert.org,eweek.com,experts-exchange.com,fosshub.com,freesoftwaremagazine.com,funkyspacemonkey.com,futuremark.com,gadgetreview.com,gizmodo.co.uk,globalsecurity.org,gunup.com,guru3d.com,head-fi.org,hexus.net,hothardware.com,howtoforge.com,idg.com.au,idownloadblog.com,ihackmyi.com,ilounge.com,infomine.com,intellireview.com,intomobile.com,iphonehacks.com,ismashphone.com,it168.com,itechpost.com,itpro.co.uk,jailbreaknation.com,laptoping.com,lightreading.com,malwaretips.com,mediaroom.com,mobilemag.com,modmyi.com,modmymobile.com,mophie.com,mozillazine.org,neoseeker.com,neowin.net,newsoxy.com,nextadvisor.com,notebookcheck.com,notebookreview.com,nvidia.com,orafaq.com,osdir.com,osxdaily.com,our-hometown.com,pchome.net,pconline.com.cn,pcpop.com,pcpro.co.uk,pcreview.co.uk,pcrisk.com,pcwelt.de,phonerebel.com,phonescoop.com,physorg.com,pocket-lint.com,post-theory.com,prnewswire.co.uk,programming4.us,quickpwn.com,redmondpie.com,redorbit.com,safer-networking.org,scientificblogging.com,sciverse.com,servicerow.com,sinfuliphone.com,singularityhub.com,slashgear.com,softonic.com,softonic.com.br,softonic.fr,sophos.com,sparkfun.com,speedguide.net,stuff.tv,symantec.com,taplikahome.com,techdailynews.net,techeblog.com,techie-buzz.com,techniqueworld.com,technobuffalo.com,technologyreview.com,technologytell.com,techpowerup.com,techpp.com,techradar.com,techshout.com,techworld.com,techworld.com.au,techworldtweets.com,telecomfile.com,tgdaily.com,theinquirer.net,thenextweb.com,theregister.co.uk,thermofisher.com,thewindowsclub.com,tomsitpro.com,trustedreviews.com,tuaw.com,tweaktown.com,unwiredview.com,wccftech.com,webmonkey.com,webpronews.com,windows7codecs.com,windowscentral.com,windowsitpro.com,windowstechies.com,winsupersite.com,wired.co.uk,wp-themes.com,xml.com,zol.com.cn',
|
||||||
|
'technology' ],
|
||||||
[ 'addons.mozilla.org,air.mozilla.org,blog.mozilla.org,bugzilla.mozilla.org,developer.mozilla.org,etherpad.mozilla.org,forums.mozillazine.org,hacks.mozilla.org,hg.mozilla.org,mozilla.org,planet.mozilla.org,quality.mozilla.org,support.mozilla.org,treeherder.mozilla.org,wiki.mozilla.org',
|
[ 'addons.mozilla.org,air.mozilla.org,blog.mozilla.org,bugzilla.mozilla.org,developer.mozilla.org,etherpad.mozilla.org,forums.mozillazine.org,hacks.mozilla.org,hg.mozilla.org,mozilla.org,planet.mozilla.org,quality.mozilla.org,support.mozilla.org,treeherder.mozilla.org,wiki.mozilla.org',
|
||||||
'Mozilla' ],
|
'Mozilla' ],
|
||||||
[ '3dprint.com,4sysops.com,access-programmers.co.uk,accountingweb.com,addictivetips.com,adweek.com,afterdawn.com,akihabaranews.com,anandtech.com,appsrumors.com,arstechnica.com,belkin.com,besttechinfo.com,betanews.com,bgr.com,botcrawl.com,breakingmuscle.com,canonrumors.com,cheap-phones.com,chip.de,chip.eu,cio.com,citeworld.com,cleanpcremove.com,cnet.com,commentcamarche.net,computer.org,computerhope.com,computershopper.com,computerweekly.com,contextures.com,coolest-gadgets.com,crn.com,csoonline.com,daniweb.com,data.com,datacenterknowledge.com,ddj.com,devicemag.com,digitaltrends.com,dottech.org,dpreview.com,dslreports.com,edugeek.net,eetimes.com,engadget.com,epic.com,eurekalert.org,eweek.com,experts-exchange.com,extremetech.com,fosshub.com,freesoftwaremagazine.com,funkyspacemonkey.com,futuremark.com,gadgetreview.com,ghacks.net,gizmodo.co.uk,gizmodo.com,globalsecurity.org,greenbot.com,gunup.com,guru3d.com,head-fi.org,hexus.net,hothardware.com,howtoforge.com,idg.com.au,idigitaltimes.com,idownloadblog.com,ihackmyi.com,ilounge.com,infomine.com,informationweek.com,intellireview.com,intomobile.com,iphonehacks.com,ismashphone.com,isource.com,it168.com,itechpost.com,itpro.co.uk,itworld.com,jailbreaknation.com,kioskea.net,laptoping.com,laptopmag.com,lightreading.com,livescience.com,malwaretips.com,mediaroom.com,mobilemag.com,modmyi.com,modmymobile.com,mophie.com,mozillazine.org,neoseeker.com,neowin.net,newscientist.com,newsoxy.com,nextadvisor.com,notebookcheck.com,notebookreview.com,nvidia.com,nwc.com,orafaq.com,osdir.com,osxdaily.com,our-hometown.com,pcadvisor.co.uk,pchome.net,pcmag.com,pconline.com.cn,pcpop.com,pcpro.co.uk,pcreview.co.uk,pcrisk.com,pcwelt.de,phonerebel.com,phonescoop.com,physorg.com,pocket-lint.com,post-theory.com,prnewswire.co.uk,prnewswire.com,programming4.us,quickpwn.com,readwrite.com,redmondpie.com,redorbit.com,reviewed.com,safer-networking.org,sciencedaily.com,sciencenews.org,scientificamerican.com,scientificblogging.com,sciverse.com,servicerow.com,sinfuliphone.com,singularityhub.com,slashdot.org,slashgear.com,softonic.com,softonic.com.br,softonic.fr,sophos.com,space.com,sparkfun.com,speedguide.net,stuff.tv,techdailynews.net,techdirt.com,techeblog.com,techhive.com,techie-buzz.com,technewsworld.com,techniqueworld.com,technobuffalo.com,technologyreview.com,technologytell.com,techpowerup.com,techpp.com,techrepublic.com,techshout.com,techweb.com,techworld.com,techworld.com.au,techworldtweets.com,telecomfile.com,tgdaily.com,theinquirer.net,thenextweb.com,theregister.co.uk,thermofisher.com,theverge.com,thewindowsclub.com,tomsguide.com,tomshardware.com,tomsitpro.com,toptenreviews.com,trustedreviews.com,tuaw.com,tweaktown.com,ubergizmo.com,unwiredview.com,venturebeat.com,wccftech.com,webmonkey.com,webpronews.com,windows7codecs.com,windowscentral.com,windowsitpro.com,windowstechies.com,winsupersite.com,wired.co.uk,wired.com,wp-themes.com,xda-developers.com,xml.com,zdnet.com,zmescience.com,zol.com.cn',
|
[ 'addictivetips.com,allthingsd.com,anandtech.com,androidcentral.com,androidpolice.com,arstechnica.com,bgr.com,boygeniusreport.com,cio.com,cnet.com,computerworld.com,crn.com,electronista.com,engadget.com,extremetech.com,fastcocreate.com,fastcodesign.com,fastcoexist.com,frontlinek12.com,gigaom.com,gizmag.com,gizmodo.com,greenbot.com,howtogeek.com,idigitaltimes.com,imore.com,informationweek.com,infoworld.com,itworld.com,kioskea.net,laptopmag.com,leadpages.net,lifehacker.com,mashable.com,networkworld.com,news.cnet.com,nwc.com,pastebin.com,pcadvisor.co.uk,pcmag.com,pcworld.com,phonearena.com,reviewed.com,serverfault.com,siteadvisor.com,slashdot.org,techcrunch.com,techdirt.com,techhive.com,technewsworld.com,techrepublic.com,techweb.com,tomsguide.com,tomshardware.com,ubergizmo.com,venturebeat.com,wired.com,xda-developers.com,zdnet.com',
|
||||||
'Technology' ],
|
'technology news' ],
|
||||||
[ '9to5mac.com,appadvice.com,apple.com,appleinsider.com,appleturns.com,appsafari.com,cultofmac.com,everymac.com,insanelymac.com,iphoneunlockspot.com,isource.com,itunes.apple.com,lowendmac.com,mac-forums.com,macdailynews.com,macenstein.com,macgasm.net,macintouch.com,maclife.com,macnews.com,macnn.com,macobserver.com,macosx.com,macpaw.com,macrumors.com,macsales.com,macstories.net,macupdate.com,macuser.co.uk,macworld.co.uk,macworld.com,maxiapple.com,spymac.com,theapplelounge.com',
|
[ 'bestbuy.ca,bestbuy.com,cdw.com,compusa.com,computerlivehelp.co,cyberguys.com,dell.com,digitalinsight.com,directron.com,ebuyer.com,frontierpc.com,frys-electronics-ads.com,frys.com,geeks.com,gyazo.com,homestead.com,lenovo.com,macmall.com,microcenter.com,miniinthebox.com,mwave.com,newegg.com,officedepot.com,outletpc.com,outpost.com,radioshack.com,rakuten.com,tigerdirect.com',
|
||||||
'Technology' ],
|
'tech retail' ],
|
||||||
[ 'alistapart.com,answers.microsoft.com,backpack.openbadges.org,blog.chromium.org,caniuse.com,codefirefox.com,codepen.io,css-tricks.com,css3generator.com,cssdeck.com,csswizardry.com,devdocs.io,docs.angularjs.org,ghacks.net,github.com,html5demos.com,html5rocks.com,html5test.com,iojs.org,khanacademy.org,l10n.mozilla.org,learn.jquery.com,marketplace.firefox.com,mozilla-hispano.org,mozillians.org,news.ycombinator.com,npmjs.com,packagecontrol.io,quirksmode.org,readwrite.com,reps.mozilla.org,smashingmagazine.com,stackoverflow.com,status.modern.ie,teamtreehouse.com,tutorialspoint.com,udacity.com,validator.w3.org,w3.org,w3cschool.cc,w3schools.com,whatcanidoformozilla.org',
|
[ 'chat.com,fring.com,hello.firefox.com,oovoo.com,viber.com',
|
||||||
'Web Development' ],
|
'video chat' ],
|
||||||
[ 'classroom.google.com,codecademy.com,elearning.ut.ac.id,khanacademy.org,learn.jquery.com,teamtreehouse.com,tutorialspoint.com,udacity.com,w3cschool.cc,w3schools.com',
|
[ 'alistapart.com,answers.microsoft.com,backpack.openbadges.org,blog.chromium.org,caniuse.com,codefirefox.com,codepen.io,css-tricks.com,css3generator.com,cssdeck.com,csswizardry.com,devdocs.io,docs.angularjs.org,ghacks.net,github.com,html5demos.com,html5rocks.com,html5test.com,iojs.org,l10n.mozilla.org,marketplace.firefox.com,mozilla-hispano.org,mozillians.org,news.ycombinator.com,npmjs.com,packagecontrol.io,quirksmode.org,readwrite.com,reps.mozilla.org,smashingmagazine.com,speckyboy.com,stackoverflow.com,status.modern.ie,validator.w3.org,w3.org,webreference.com,whatcanidoformozilla.org',
|
||||||
'Web Education' ],
|
'web development' ],
|
||||||
[ 'abebooks.co.uk,abebooks.com,alibris.com,allaboutcircuits.com,allyoucanbooks.com,answersingenesis.org,artnet.com,audiobooks.com,barnesandnoble.com,barnesandnobleinc.com,bartleby.com,betterworldbooks.com,biggerbooks.com,bncollege.com,bookbyte.com,bookdepository.com,bookfinder.com,bookrenter.com,booksamillion.com,booksite.com,boundless.com,brookstone.com,btol.com,calibre-ebook.com,campusbookrentals.com,casadellibro.com,cbomc.com,cengagebrain.com,chapters.indigo.ca,christianbook.com,ciscopress.com,coursesmart.com,cqpress.com,crafterschoice.com,crossings.com,cshlp.org,deseretbook.com,directtextbook.com,discountmags.com,doubledaybookclub.com,doubledaylargeprint.com,doverpublications.com,ebooks.com,ecampus.com,fellabooks.net,fictionwise.com,flatworldknowledge.com,grolier.com,harpercollins.com,hayhouse.com,historybookclub.com,hpb.com,hpbmarketplace.com,interweave.com,iseeme.com,katiekazoo.com,knetbooks.com,learnoutloud.com,librarything.com,literaryguild.com,lulu.com,lww.com,macmillan.com,magazines.com,mbsdirect.net,militarybookclub.com,mypearsonstore.com,mysteryguild.com,netplaces.com,noble.com,novelguide.com,onespirit.com,oxfordjournals.org,paperbackswap.com,papy.co.jp,peachpit.com,penguin.com,penguingroup.com,pimsleur.com,powells.com,qpb.com,quepublishing.com,reviews.com,rhapsodybookclub.com,rodalestore.com,royalsocietypublishing.org,sagepub.com,scrubsmag.com,sfbc.com,simonandschuster.com,simonandschuster.net,simpletruths.com,teach12.net,textbooks.com,textbookx.com,thegoodcook.com,thriftbooks.com,tlsbooks.com,toshibabookplace.com,tumblebooks.com,urbookdownload.com,valorebooks.com,valuemags.com,wwnorton.com,zoobooks.com',
|
[ 'classroom.google.com,codeacademy.org,codecademy.com,codeschool.com,codeyear.com,elearning.ut.ac.id,how-to-build-websites.com,htmlcodetutorial.com,htmldog.com,htmlplayground.com,learn.jquery.com,quackit.com,roseindia.net,teamtreehouse.com,tizag.com,tutorialspoint.com,udacity.com,w3schools.com,webdevelopersnotes.com',
|
||||||
'Literature' ],
|
'webdev education' ],
|
||||||
[ 'aceshowbiz.com,aintitcoolnews.com,askkissy.com,askmen.com,atraf.co.il,audioboom.com,beamly.com,blippitt.com,bollywoodlife.com,bossip.com,buzzlamp.com,celebdirtylaundry.com,celebfocus.com,celebitchy.com,celebrity-gossip.net,celebrityabout.com,celebwild.com,chisms.net,concertboom.com,crushable.com,cultbox.co.uk,dailyentertainmentnews.com,dayscafe.com,deadline.com,deathandtaxesmag.com,diaryofahollywoodstreetking.com,digitalspy.com,egotastic.com,empirenews.net,enelbrasero.com,everydaycelebs.com,ew.com,extratv.com,facade.com,fanaru.com,fhm.com,geektyrant.com,glamourpage.com,heatworld.com,hlntv.com,hollyscoop.com,hollywoodreporter.com,hollywoodtuna.com,hypable.com,infotransfer.net,insideedition.com,interaksyon.com,jezebel.com,justjared.com,justjaredjr.com,komando.com,koreaboo.com,maxgo.com,maxim.com,maxviral.com,mediatakeout.com,mosthappy.com,moviestalk.com,my.ology.com,ngoisao.net,nofilmschool.com,nolocreo.com,octane.tv,ouchpress.com,people.com,peopleenespanol.com,perezhilton.com,pinkisthenewblog.com,platotv.tv,playbill.com,playbillvault.com,playgroundmag.net,popeater.com,popnhop.com,popsugar.co.uk,popsugar.com,purepeople.com,radaronline.com,rantchic.com,reshareworthy.com,rinkworks.com,ripbird.com,sara-freder.com,screenjunkies.com,soapcentral.com,soapoperadigest.com,sobadsogood.com,splitsider.com,starcasm.net,starpulse.com,straightfromthea.com,stupidcelebrities.net,stupiddope.com,tbn.org,theawesomedaily.com,theawl.com,thefrisky.com,thefw.com,theresacaputo.com,thezooom.com,tvnotas.com.mx,twanatells.com,vanswarpedtour.com,vietgiaitri.com,viral.buzz,vulture.com,wakavision.com,worthytales.net,wwtdd.com,younghollywood.com',
|
[ 'att.com,att.net,attonlineoffers.com,bell.ca,bellsouth.com,cableone.net,cablevision.com,centurylink.com,centurylink.net,centurylinkquote.com,charter-business.com,charter.com,charter.net,chartercabledeals.com,chartermedia.com,comcast.com,comcast.net,cox.com,cox.net,coxnewsweb.com,directv.com,dish.com,dishnetwork.com,freeconferencecall.com,frontier.com,hughesnet.com,liveitwithcharter.com,mycenturylink.com,mydish.com,net10.com,officialtvstream.com.es,optimum.com,optimum.net,paygonline.com,paytm.com,qwest.com,rcn.com,rebtel.com,ringcentral.com,straighttalkbyop.com,swappa.com,textem.net,timewarner.com,timewarnercable.com,tracfone.com,verizon.com,verizon.net,voipo.com,vonagebusiness.com,wayport.net,whistleout.com,wildblue.net,windstream.net,windstreambusiness.net,wowway.com,ww2.cox.com,xfinity.com',
|
||||||
'Entertainment News' ],
|
'telecommunication' ],
|
||||||
[ '247wallst.com,4-traders.com,advfn.com,agweb.com,allbusiness.com,barchart.com,barrons.com,beckershospitalreview.com,benzinga.com,bizjournals.com,bizsugar.com,bloomberg.com,bloomberglaw.com,business-standard.com,businessinsider.com,businessinsider.com.au,businesspundit.com,businessweek.com,businesswire.com,cboe.com,cheatsheet.com,chicagobusiness.com,cjonline.com,cnbc.com,cnnmoney.com,cqrcengage.com,dailyfinance.com,dailyfx.com,dealbreaker.com,djindexes.com,dowjones.com,easierstreetdaily.com,economist.com,economyandmarkets.com,economywatch.com,edweek.org,eleconomista.es,entrepreneur.com,etfdailynews.com,etfdb.com,ewallstreeter.com,fastcolabs.com,fastcompany.com,financeformulas.net,financialpost.com,flife.de,forbes.com,forexpros.com,fortune.com,foxbusiness.com,ft.com,ftpress.com,fx-exchange.com,hbr.org,howdofinance.com,ibtimes.com,inc.com,investopedia.com,investors.com,investorwords.com,journalofaccountancy.com,kiplinger.com,lendingandcredit.net,lfb.org,mainstreet.com,markettraders.com,marketwatch.com,maxkeiser.com,minyanville.com,ml.com,moneycontrol.com,moneymappress.com,moneynews.com,moneysavingexpert.com,morningstar.com,mortgagenewsdaily.com,motleyfool.com,mt.co.kr,nber.org,nyse.com,oilprice.com,pewsocialtrends.org,principal.com,qz.com,rantfinance.com,realclearmarkets.com,recode.net,reuters.ca,reuters.co.in,reuters.co.uk,reuters.com,rttnews.com,seekingalpha.com,smallbiztrends.com,streetinsider.com,thecheapinvestor.com,theeconomiccollapseblog.com,themoneyconverter.com,thestreet.com,tickertech.com,tradingeconomics.com,updown.com,valuewalk.com,wikinvest.com,wsj.com,zacks.com',
|
[ 'alltel.com,assurancewireless.com,attsavings.com,boostmobile.com,boostmobilestore.com,budgetmobile.com,consumercellular.com,credomobile.com,gosmartmobile.com,h2owirelessnow.com,lycamobile.com,lycamobile.us,metropcs.com,motorola.com,mycricket.com,myfamilymobile.com,nextel.com,nokia.com,nokiausa.com,polarmobile.com,qlinkwireless.com,republicwireless.com,sprint.com,sprintpcs.com,straighttalk.com,t-mobile.co.uk,t-mobile.com,tmobile.com,tracfonewireless.com,uscellular.com,verizonwireless.com,virginmobile.com,virginmobile.com.au,virginmobileusa.com,vodafone.co.uk,vodafone.com,vodaphone.co.uk,vonange.com,vzwshop.com,wireless.att.com',
|
||||||
'Financial News' ],
|
'mobile carrier' ],
|
||||||
[ '10tv.com,8newsnow.com,9news.com,abc.net.au,abc7.com,abc7chicago.com,abcnews.go.com,aclu.org,activistpost.com,ajc.com,al.com,alan.com,alarab.net,aljazeera.com,americanthinker.com,app.com,aristeguinoticias.com,azcentral.com,baltimoresun.com,becomingminimalist.com,beforeitsnews.com,bigstory.ap.org,blackamericaweb.com,bloomberg.com,bloombergview.com,boston.com,bostonherald.com,breitbart.com,buffalonews.com,c-span.org,canada.com,cbs46.com,cbsnews.com,chicagotribune.com,chron.com,citizensvoice.com,citylab.com,cleveland.com,cnn.com,coed.com,countercurrentnews.com,courant.com,ctvnews.ca,dailyherald.com,dailynews.com,dallasnews.com,delawareonline.com,democratandchronicle.com,democraticunderground.com,democrats.org,denverpost.com,desmoinesregister.com,dispatch.com,elcomercio.pe,english.aljazeera.net,examiner.com,farsnews.com,firstcoastnews.com,firstpost.com,firsttoknow.com,foreignpolicy.com,foxnews.com,freebeacon.com,freep.com,fresnobee.com,gazette.com,global.nytimes.com,heraldtribune.com,hindustantimes.com,hngn.com,humanevents.com,huzlers.com,indiatimes.com,indystar.com,irishtimes.com,jacksonville.com,jpost.com,jsonline.com,kansascity.com,kctv5.com,kentucky.com,kickerdaily.com,king5.com,kmov.com,knoxnews.com,kpho.com,kvue.com,kwqc.com,kxan.com,lainformacion.com,latimes.com,ldnews.com,lex18.com,linternaute.com,livemint.com,lostateminor.com,m24.ru,macleans.ca,manchestereveningnews.co.uk,marinecorpstimes.com,masslive.com,mavikocaeli.com.tr,mcall.com,medium.com,mentalfloss.com,mercurynews.com,metro.us,miamiherald.com,militarytimes.com,mk.ru,mlive.com,mondotimes.com,montrealgazette.com,msnbc.com,msnewsnow.com,mynews13.com,mysanantonio.com,mysuncoast.com,nbclosangeles.com,nbcnewyork.com,nbcphiladelphia.com,ndtv.com,newindianexpress.com,news.cincinnati.com,news.google.com,news.msn.com,news.yahoo.com,news10.net,news8000.com,newsday.com,newsdaymarketing.net,newsen.com,newsmax.com,newsobserver.com,newsok.com,newsru.ua,newstatesman.com,newszoom.com,nj.com,nola.com,northjersey.com,nouvelobs.com,npr.org,nwfdailynews.com,nwitimes.com,nydailynews.com,nytimes.com,observer.com,ocregister.com,okcfox.com,omaha.com,onenewspage.com,ontheissues.org,oregonlive.com,orlandosentinel.com,palmbeachpost.com,pe.com,pennlive.com,philly.com,pilotonline.com,polar.com,post-gazette.com,postandcourier.com,presstelegram.com,presstv.ir,propublica.org,providencejournal.com,realclearpolitics.com,recorderonline.com,reporterdock.com,reporterherald.com,respublica.al,reuters.com,rg.ru,roanoke.com,sacbee.com,scmp.com,scnow.com,sdpnoticias.com,seattletimes.com,semana.com,sfgate.com,sharepowered.com,sinembargo.mx,slate.com,sltrib.com,sotomayortv.com,sourcewatch.org,spectator.co.uk,squaremirror.com,star-telegram.com,staradvertiser.com,startribune.com,statesman.com,stltoday.com,streetwise.co,stuff.co.nz,success.com,suffolknewsherald.com,sun-sentinel.com,sunnewsnetwork.ca,suntimes.com,supernewschannel.com,surenews.com,svoboda.org,syracuse.com,tampabay.com,tbd.com,telegram.com,telegraph.co.uk,tennessean.com,the-open-mind.com,theadvocate.com,theage.com.au,theatlantic.com,thebarefootwriter.com,theblaze.com,thecalifornian.com,thedailysheeple.com,thefix.com,theintelligencer.net,thelocal.com,thenational.ae,thenewstribune.com,theparisreview.org,thereporter.com,therepublic.com,thestar.com,thetelegram.com,thetimes.co.uk,theuspatriot.com,time.com,timescall.com,timesdispatch.com,timesleaderonline.com,timesofisrael.com,toledoblade.com,toprightnews.com,townhall.com,tpnn.com,trendolizer.com,triblive.com,tribune.com.pk,tricities.com,troymessenger.com,trueactivist.com,truthandaction.org,tsn.ua,tulsaworld.com,twincities.com,upi.com,usatoday.com,utsandiego.com,vagazette.com,viralwomen.com,vitalworldnews.com,voasomali.com,vox.com,washingtonexaminer.com,washingtonpost.com,watchdog.org,wave3.com,wavy.com,wbay.com,wbtw.com,wcpo.com,wctrib.com,wdtn.com,weeklystandard.com,westernjournalism.com,wfsb.com,wgrz.com,whas11.com,winonadailynews.com,wishtv.com,wistv.com,wkbn.com,wkow.com,wlfi.com,wmtw.com,wmur.com,wopular.com,world-top-news.com,worldnews.com,wplol.us,wpsdlocal6.com,wptz.com,wric.com,wsmv.com,wthitv.com,wthr.com,wtnh.com,wtol.com,wtsp.com,wvec.com,wwlp.com,wwltv.com,wyff4.com,yonhapnews.co.kr,yourbreakingnews.com',
|
[ 'aa.com,aerlingus.com,airasia.com,aircanada.com,airfrance.com,airindia.com,alaskaair.com,alaskaairlines.com,allegiantair.com,britishairways.com,cathaypacific.com,china-airlines.com,continental.com,delta.com,deltavacations.com,dragonair.com,easyjet.com,elal.co.il,emirates.com,flightaware.com,flyfrontier.com,frontierairlines.com,hawaiianair.com,iberia.com,jetairways.com,jetblue.com,klm.com,koreanair.com,kuwait-airways.com,lan.com,lufthansa.com,malaysiaairlines.com,mihinlanka.com,nwa.com,qantas.com.au,qatarairways.com,ryanair.com,singaporeair.com,smartfares.com,southwest.com,southwestvacations.com,spiritair.com,spiritairlines.com,thaiair.com,united.com,usairways.com,virgin-atlantic.com,virginamerica.com,virginblue.com.au',
|
||||||
'News' ],
|
'travel & airline' ],
|
||||||
[ '2k.com,360game.vn,4399.com,a10.com,activision.com,addictinggames.com,alawar.com,alienwarearena.com,anagrammer.com,andkon.com,aq.com,arcadeprehacks.com,arcadeyum.com,arcgames.com,archeagegame.com,armorgames.com,askmrrobot.com,battle.net,battlefieldheroes.com,bigfishgames.com,bigpoint.com,bioware.com,bluesnews.com,boardgamegeek.com,bollyheaven.com,bubblebox.com,bukkit.org,bungie.net,buycraft.net,callofduty.com,candystand.com,cda.pl,challonge.com,championselect.net,cheapassgamer.com,cheatcc.com,cheatengine.org,cheathappens.com,chess.com,civfanatics.com,clashofclans-tools.com,clashofclansbuilder.com,comdotgame.com,commonsensemedia.org,coolrom.com,crazygames.com,csgolounge.com,curse.com,d20pfsrd.com,destructoid.com,diablofans.com,diablowiki.net,didigames.com,dota2.com,dota2lounge.com,dressupgames.com,dulfy.net,ebog.com,elderscrollsonline.com,elitedangerous.com,elitepvpers.com,emuparadise.me,enjoydressup.com,escapegames24.com,escapistmagazine.com,eventhubs.com,eveonline.com,farming-simulator.com,feed-the-beast.com,flashgames247.com,flightrising.com,flipline.com,flonga.com,freegames.ws,freeonlinegames.com,fresh-hotel.org,friv.com,friv.today,fullypcgames.net,funny-games.biz,funtrivia.com,futhead.com,g2a.com,gamasutra.com,game-debate.com,game-oldies.com,game321.com,gamebaby.com,gamebaby.net,gamebanana.com,gamefaqs.com,gamefly.com,gamefront.com,gamegape.com,gamehouse.com,gameinformer.com,gamejolt.com,gamemazing.com,gamemeteor.com,gamerankings.com,gamersgate.com,games-msn.com,games-workshop.com,games.com,games2girls.com,gamesbox.com,gamesfreak.net,gametop.com,gametracker.com,gametrailers.com,gamezhero.com,gbatemp.net,geforce.com,gematsu.com,giantbomb.com,girl.me,girlsgames123.com,girlsplay.com,gog.com,gogames.me,gonintendo.com,goodgamestudios.com,gosugamers.net,greenmangaming.com,gtaforums.com,gtainside.com,guildwars2.com,hackedarcadegames.com,hearthpwn.com,hirezstudios.com,hitbox.tv,hltv.org,howrse.com,icy-veins.com,indiedb.com,jayisgames.com,jigzone.com,joystiq.com,juegosdechicas.com,kabam.com,kbhgames.com,kerbalspaceprogram.com,king.com,kixeye.com,kizi.com,kogama.com,kongregate.com,kotaku.com,lolcounter.com,lolking.net,lolnexus.com,lolpro.com,lolskill.net,lootcrate.com,lumosity.com,mafa.com,mangafox.me,mangapark.com,mariowiki.com,maxgames.com,megagames.com,metacritic.com,mindjolt.com,minecraft.net,minecraftforum.net,minecraftservers.org,minecraftskins.com,mineplex.com,miniclip.com,mmo-champion.com,mmobomb.com,mmohuts.com,mmorpg.com,mmosite.com,mobafire.com,moddb.com,modxvm.com,mojang.com,moshimonsters.com,mousebreaker.com,moviestarplanet.com,mtgsalvation.com,muchgames.com,myonlinearcade.com,myplaycity.com,myrealgames.com,mythicspoiler.com,n4g.com,newgrounds.com,nexon.net,nexusmods.com,ninjakiwi.com,nintendo.com,nintendoeverything.com,nintendolife.com,nitrome.com,nosteam.ro,notdoppler.com,noxxic.com,operationsports.com,origin.com,ownedcore.com,pacogames.com,pathofexile.com,pcgamer.com,pch.com,pcsx2.net,penny-arcade.com,planetminecraft.com,plarium.com,playdota.com,playpink.com,playsides.com,playstationlifestyle.net,playstationtrophies.org,pog.com,pokemon.com,polygon.com,popcap.com,primarygames.com,probuilds.net,ps3hax.net,psnprofiles.com,psu.com,qq.com,r2games.com,resourcepack.net,retrogamer.com,rewardtv.com,riotgames.com,robertsspaceindustries.com,roblox.com,robocraftgame.com,rockpapershotgun.com,rockstargames.com,roosterteeth.com,runescape.com,schoolofdragons.com,screwattack.com,scufgaming.com,segmentnext.com,shacknews.com,shockwave.com,shoryuken.com,siliconera.com,silvergames.com,skydaz.com,smashbros.com,solomid.net,starcitygames.com,starsue.net,steamcommunity.com,steamgifts.com,strategywiki.org,supercheats.com,surrenderat20.net,swtor.com,tankionline.com,tcgplayer.com,teamfortress.com,teamliquid.net,tetrisfriends.com,thesims3.com,thesimsresource.com,thetechgame.com,topg.org,totaljerkface.com,toucharcade.com,transformice.com,trueachievements.com,twcenter.net,twitch.tv,twoplayergames.org,unity3d.com,vg247.com,vgchartz.com,videogamesblogger.com,warframe.com,warlight.net,warthunder.com,watchcartoononline.com,websudoku.com,wildstar-online.com,wildtangent.com,wineverygame.com,wizards.com,worldofsolitaire.com,worldoftanks.com,wowhead.com,wowprogress.com,wowwiki.com,xbox.com,xbox360iso.com,xboxachievements.com,xfire.com,xtremetop100.com,y8.com,yoyogames.com,zybez.net,zynga.com',
|
[ 'carnival.com,celebrity-cruises.com,celebritycruises.com,costacruise.com,cruise.com,cruiseamerica.com,cruisecritic.com,cruisedirect.com,cruisemates.com,cruises.com,cruisesonly.com,crystalcruises.com,cunard.com,disneycruise.disney.go.com,hollandamerica.com,ncl.com,pocruises.com,princess.com,royalcaribbean.com,royalcaribbean.cruiselines.com,rssc.com,seabourn.com,silversea.com,starcruises.com,vikingrivercruises.com,windstarcruises.com',
|
||||||
'Video Game' ],
|
'travel & cruise' ],
|
||||||
|
[ 'agoda.com,airbnb.com,beaches.com,bedandbreakfast.com,bestwestern.com,booking.com,caesars.com,choicehotels.com,comfortinn.com,daysinn.com,dealbase.com,doubletree3.hilton.com,embassysuites.com,fairmont.com,flipkey.com,fourseasons.com,greatwolf.com,hamptoninn.hilton.com,hamptoninn3.hilton.com,hhonors3.hilton.com,hilton.com,hiltongardeninn3.hilton.com,hiltonworldwide.com,holidayinn.com,homeaway.com,hotelclub.com,hotelopia.com,hotels.com,hotelscombined.com,hyatt.com,ihg.com,laterooms.com,lhw.com,lq.com,mandarinoriental.com,marriott.com,motel6.com,omnihotels.com,radisson.com,ramada.com,rci.com,reservationcounter.com,resortvacationstogo.com,ritzcarlton.com,roomkey.com,sheraton.com,starwoodhotels.com,starwoodhotelshawaii.com,super8.com,thetrain.com,vacationhomerentals.com,vacationrentals.com,vrbo.com,wyndhamrewards.com',
|
||||||
|
'hotel & resort' ],
|
||||||
|
[ 'airfarewatchdog.com,airliners.net,atlanta-airport.com,budgettravel.com,cntraveler.com,cntraveller.com,destination360.com,flightstats.com,flyertalk.com,fodors.com,frommers.com,letsgo.com,lonelyplanet.com,matadornetwork.com,perfectvacation.co,ricksteves.com,roughguides.com,timeout.com,travelalberta.us,travelandleisure.com,travelchannel.com,traveler.nationalgeographic.com,travelmath.com,traveltune.com,tripadvisor.com,vegas.com,viator.com,virtualtourist.com,wikitravel.org,worldtravelguide.net',
|
||||||
|
'travel' ],
|
||||||
|
[ 'aavacations.com,applevacations.com,avianca.com,bookingbuddy.com,bookit.com,cheapair.com,cheapcaribbean.com,cheapflights.com,cheapoair.com,cheaptickets.com,chinahighlights.com,costcotravel.com,ctrip.com,despegar.com,edreams.net,expedia.ca,expedia.com,fareboom.com,farebuzz.com,farecast.live.com,farecompare.com,faregeek.com,flightnetwork.com,funjet.com,golastminute.com,hipmunk.com,hotwire.com,ifly.com,justairticket.com,kayak.com,lastminute.com,lastminutetravel.com,lowestfare.com,lowfares.com,momondo.com,onetime.com,onetravel.com,orbitz.com,otel.com,priceline.com,pricelinevisa.com,sidestep.com,skyscanner.com,smartertravel.com,statravel.com,tigerair.com,travelocity.com,travelonbids.com,travelzoo.com,tripsta.com,trivago.com,universalorlando.com,universalstudioshollywood.com,vacationexpress.com,venere.com,webjet.com,yatra.com',
|
||||||
|
'travel' ],
|
||||||
|
[ 'airportrentalcars.com,alamo.com,amtrak.com,anytransitguide.com,avis.com,boltbus.com,budget.com,carrentalexpress.com,carrentals.com,coachusa.com,dollar.com,e-zrentacar.com,enterprise.com,europcar.com,foxrentacar.com,gotobus.com,greyhound.com,hertz.com,hertzondemand.com,indianrail.gov.in,irctc.co.in,megabus.com,mta.info,nationalcar.com,nationalrail.co.uk,njtransit.com,paylesscar.com,paylesscarrental.com,peterpanbus.com,raileurope.com,rentalcars.com,rideuta.com,stagecoachbus.com,thrifty.com,uber.com,wanderu.com,zipcar.com',
|
||||||
|
'travel & transit' ],
|
||||||
|
[ 'bulbagarden.net,cheatcc.com,cheatmasters.com,cheats.ign.com,comicvine.com,computerandvideogames.com,counter-strike.net,escapistmagazine.com,gamedaily.com,gamefront.com,gameinformer.com,gamerankings.com,gamespot.com,gamesradar.com,gamestop.com,gametrailers.com,gamezone.com,giantbomb.com,ign.com,kotaku.com,metacritic.com,minecraft-server-list.com,minecraftforge.net,minecraftforum.net,minecraftservers.org,minecraftskins.com,mmo-champion.com,mojang.com,pcgamer.com,planetminecraft.com,supercheats.com,thesims.com,totaljerkface.com,unity3d.com,vg247.com,wowhead.com',
|
||||||
|
'gaming' ],
|
||||||
|
[ 'a10.com,absolutist.com,addictinggames.com,aeriagames.com,agame.com,alpha-wars.com,arcadeyum.com,armorgames.com,ballerarcade.com,battle.net,battlefield.com,bigfishgames.com,bioware.com,bitrhymes.com,candystand.com,conjurorthegame.com,crazymonkeygames.com,crusharcade.com,curse.com,cuttherope.net,dreammining.com,dressupgames.com,ea.com,easports.com,fps-pb.com,freearcade.com,freeonlinegames.com,friv.com,funplusgame.com,gamefly.com,gameforge.com,gamehouse.com,gamejolt.com,gameloft.com,gameoapp.com,gamepedia.com,gamersfirst.com,games.com,games.yahoo.com,gamesgames.com,gamezhero.com,gamingwonderland.com,ganymede.eu,goodgamestudios.com,gpotato.com,gsn.com,guildwars2.com,hirezstudios.com,igg.com,iwin.com,kahoot.it,king.com,kizi.com,kongregate.com,leagueoflegends.com,lolking.net,maxgames.com,minecraft-mp.com,minecraft.net,miniclip.com,mmo-play.com,mmorpg.com,mobafire.com,moviestarplanet.com,myonlinearcade.com,needforspeed.com,newgrounds.com,nexusmods.com,nintendo.com,noxxic.com,onrpg.com,origin.com,pch.com,peakgames.net,playstation.com,pogo.com,pokemon.com,popcap.com,primarygames.com,r2games.com,railnation.us,riotgames.com,roblox.com,rockstargames.com,runescape.com,shockwave.com,silvergames.com,spore.com,steamcommunity.com,steampowered.com,stickpage.com,swtor.com,tetrisfriends.com,thegamerstop.com,thesims3.com,twitch.tv,warthunder.com,wildtangent.com,worldoftanks.com,worldofwarcraft.com,worldofwarplanes.com,worldofwarships.com,xbox.com,y8.com,zone.msn.com,zynga.com,zyngawithfriends.com',
|
||||||
|
'online gaming' ],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Only allow link urls that are http(s)
|
// Only allow link urls that are http(s)
|
||||||
@ -646,6 +790,18 @@ let DirectoryLinksProvider = {
|
|||||||
return allowed.has(scheme) && (!checkBase || ALLOWED_URL_BASE.has(base));
|
return allowed.has(scheme) && (!checkBase || ALLOWED_URL_BASE.has(base));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_escapeChars(text) {
|
||||||
|
let charMap = {
|
||||||
|
'&': '&',
|
||||||
|
'<': '<',
|
||||||
|
'>': '>',
|
||||||
|
'"': '"',
|
||||||
|
"'": '''
|
||||||
|
};
|
||||||
|
|
||||||
|
return text.replace(/[&<>"']/g, (character) => charMap[character]);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current set of directory links.
|
* Gets the current set of directory links.
|
||||||
* @param aCallback The function that the array of links is passed to.
|
* @param aCallback The function that the array of links is passed to.
|
||||||
@ -678,8 +834,8 @@ let DirectoryLinksProvider = {
|
|||||||
ParserUtils.SanitizerDropForms |
|
ParserUtils.SanitizerDropForms |
|
||||||
ParserUtils.SanitizerDropNonCSSPresentation;
|
ParserUtils.SanitizerDropNonCSSPresentation;
|
||||||
|
|
||||||
link.explanation = link.explanation ? ParserUtils.convertToPlainText(link.explanation, sanitizeFlags, 0) : "";
|
link.explanation = this._escapeChars(link.explanation ? ParserUtils.convertToPlainText(link.explanation, sanitizeFlags, 0) : "");
|
||||||
link.targetedName = ParserUtils.convertToPlainText(link.adgroup_name, sanitizeFlags, 0) || name;
|
link.targetedName = this._escapeChars(ParserUtils.convertToPlainText(link.adgroup_name, sanitizeFlags, 0) || name);
|
||||||
link.lastVisitDate = rawLinks.suggested.length - position;
|
link.lastVisitDate = rawLinks.suggested.length - position;
|
||||||
// check if link wants to avoid inadjacent sites
|
// check if link wants to avoid inadjacent sites
|
||||||
if (link.check_inadjacency) {
|
if (link.check_inadjacency) {
|
||||||
|
@ -260,6 +260,16 @@ if test -n "$MOZ_NATIVE_DEVICES" ; then
|
|||||||
AC_SUBST(ANDROID_APPCOMPAT_LIB)
|
AC_SUBST(ANDROID_APPCOMPAT_LIB)
|
||||||
AC_SUBST(ANDROID_APPCOMPAT_RES)
|
AC_SUBST(ANDROID_APPCOMPAT_RES)
|
||||||
|
|
||||||
|
ANDROID_RECYCLERVIEW_LIB="$ANDROID_COMPAT_DIR_BASE/v7/recyclerview/libs/android-support-v7-recyclerview.jar"
|
||||||
|
ANDROID_RECYCLERVIEW_RES="$ANDROID_COMPAT_DIR_BASE/v7/recyclerview/res"
|
||||||
|
AC_MSG_CHECKING([for v7 recyclerview library])
|
||||||
|
if ! test -e $ANDROID_RECYCLERVIEW_LIB ; then
|
||||||
|
AC_MSG_ERROR([You must download the v7 recyclerview Android support library. Run the Android SDK tool and install Android Support Library under Extras. See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_RECYCLERVIEW_LIB)])
|
||||||
|
fi
|
||||||
|
AC_MSG_RESULT([$ANDROID_RECYCLERVIEW_LIB])
|
||||||
|
AC_SUBST(ANDROID_RECYCLERVIEW_LIB)
|
||||||
|
AC_SUBST(ANDROID_RECYCLERVIEW_RES)
|
||||||
|
|
||||||
ANDROID_MEDIAROUTER_LIB="$ANDROID_COMPAT_DIR_BASE/v7/mediarouter/libs/android-support-v7-mediarouter.jar"
|
ANDROID_MEDIAROUTER_LIB="$ANDROID_COMPAT_DIR_BASE/v7/mediarouter/libs/android-support-v7-mediarouter.jar"
|
||||||
ANDROID_MEDIAROUTER_RES="$ANDROID_COMPAT_DIR_BASE/v7/mediarouter/res"
|
ANDROID_MEDIAROUTER_RES="$ANDROID_COMPAT_DIR_BASE/v7/mediarouter/res"
|
||||||
AC_MSG_CHECKING([for v7 mediarouter library])
|
AC_MSG_CHECKING([for v7 mediarouter library])
|
||||||
|
@ -22,28 +22,17 @@
|
|||||||
GLIBCXX_3.4.18 is from gcc 4.8.0 (190787)
|
GLIBCXX_3.4.18 is from gcc 4.8.0 (190787)
|
||||||
GLIBCXX_3.4.19 is from gcc 4.8.1 (199309)
|
GLIBCXX_3.4.19 is from gcc 4.8.1 (199309)
|
||||||
GLIBCXX_3.4.20 is from gcc 4.9.0 (199307)
|
GLIBCXX_3.4.20 is from gcc 4.9.0 (199307)
|
||||||
GLIBCXX_3.4.21 is from gcc 5.0 (210290) */
|
GLIBCXX_3.4.21 is from gcc 5.0 (210290)
|
||||||
|
|
||||||
|
This file adds the necessary compatibility tricks to avoid symbols with
|
||||||
|
version GLIBCXX_3.4.11 and bigger, keeping binary compatibility with
|
||||||
|
libstdc++ 4.3.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
#define GLIBCXX_VERSION(a, b, c) (((a) << 16) | ((b) << 8) | (c))
|
#define GLIBCXX_VERSION(a, b, c) (((a) << 16) | ((b) << 8) | (c))
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 9)
|
|
||||||
/* Instantiate these templates to avoid GLIBCXX_3.4.9 symbol versions */
|
|
||||||
template ostream& ostream::_M_insert(double);
|
|
||||||
template ostream& ostream::_M_insert(long);
|
|
||||||
template ostream& ostream::_M_insert(unsigned long);
|
|
||||||
template ostream& ostream::_M_insert(long long);
|
|
||||||
template ostream& ostream::_M_insert(unsigned long long);
|
|
||||||
template ostream& ostream::_M_insert(bool);
|
|
||||||
template ostream& ostream::_M_insert(const void*);
|
|
||||||
template ostream& __ostream_insert(ostream&, const char*, streamsize);
|
|
||||||
template istream& istream::_M_extract(double&);
|
|
||||||
template istream& istream::_M_extract(float&);
|
|
||||||
template istream& istream::_M_extract(unsigned int&);
|
|
||||||
template istream& istream::_M_extract(unsigned long&);
|
|
||||||
template istream& istream::_M_extract(unsigned short&);
|
|
||||||
template istream& istream::_M_extract(unsigned long long&);
|
|
||||||
#endif
|
|
||||||
#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 14)
|
#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 14)
|
||||||
/* Instantiate these templates to avoid GLIBCXX_3.4.14 symbol versions
|
/* Instantiate these templates to avoid GLIBCXX_3.4.14 symbol versions
|
||||||
* depending on optimization level */
|
* depending on optimization level */
|
||||||
|
@ -631,7 +631,7 @@ EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) $(EXPAND_MKSHLIB_ARGS) -- $(MKSHLIB)
|
|||||||
|
|
||||||
ifneq (,$(MOZ_LIBSTDCXX_TARGET_VERSION)$(MOZ_LIBSTDCXX_HOST_VERSION))
|
ifneq (,$(MOZ_LIBSTDCXX_TARGET_VERSION)$(MOZ_LIBSTDCXX_HOST_VERSION))
|
||||||
ifneq ($(OS_ARCH),Darwin)
|
ifneq ($(OS_ARCH),Darwin)
|
||||||
CHECK_STDCXX = @$(TOOLCHAIN_PREFIX)objdump -p $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' > /dev/null && echo 'TEST-UNEXPECTED-FAIL | check_stdcxx | We do not want these libstdc++ symbols to be used:' && $(TOOLCHAIN_PREFIX)objdump -T $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' && exit 1 || true
|
CHECK_STDCXX = @$(TOOLCHAIN_PREFIX)objdump -p $(1) | grep -e 'GLIBCXX_3\.4\.\(1[1-9]\|[2-9][0-9]\)' > /dev/null && echo 'TEST-UNEXPECTED-FAIL | check_stdcxx | We do not want these libstdc++ symbols to be used:' && $(TOOLCHAIN_PREFIX)objdump -T $(1) | grep -e 'GLIBCXX_3\.4\.\(1[1-9]\|[2-9][0-9]\)' && exit 1 || true
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -10094,6 +10094,11 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
|||||||
*/
|
*/
|
||||||
SetHistoryEntry(&mLSHE, aSHEntry);
|
SetHistoryEntry(&mLSHE, aSHEntry);
|
||||||
|
|
||||||
|
// Set the doc's URI according to the new history entry's URI.
|
||||||
|
nsCOMPtr<nsIDocument> doc = GetDocument();
|
||||||
|
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
||||||
|
doc->SetDocumentURI(aURI);
|
||||||
|
|
||||||
/* This is a anchor traversal with in the same page.
|
/* This is a anchor traversal with in the same page.
|
||||||
* call OnNewURI() so that, this traversal will be
|
* call OnNewURI() so that, this traversal will be
|
||||||
* recorded in session and global history.
|
* recorded in session and global history.
|
||||||
@ -10184,11 +10189,6 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the doc's URI according to the new history entry's URI.
|
|
||||||
nsCOMPtr<nsIDocument> doc = GetDocument();
|
|
||||||
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
|
||||||
doc->SetDocumentURI(aURI);
|
|
||||||
|
|
||||||
SetDocCurrentStateObj(mOSHE);
|
SetDocCurrentStateObj(mOSHE);
|
||||||
|
|
||||||
// Inform the favicon service that the favicon for oldURI also
|
// Inform the favicon service that the favicon for oldURI also
|
||||||
@ -11716,8 +11716,8 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
|
|||||||
// document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
|
// document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
|
||||||
// FireOnLocationChange(...) breaks security UI.
|
// FireOnLocationChange(...) breaks security UI.
|
||||||
if (!equalURIs) {
|
if (!equalURIs) {
|
||||||
SetCurrentURI(newURI, nullptr, true, LOCATION_CHANGE_SAME_DOCUMENT);
|
|
||||||
document->SetDocumentURI(newURI);
|
document->SetDocumentURI(newURI);
|
||||||
|
SetCurrentURI(newURI, nullptr, true, LOCATION_CHANGE_SAME_DOCUMENT);
|
||||||
|
|
||||||
AddURIVisit(newURI, oldURI, oldURI, 0);
|
AddURIVisit(newURI, oldURI, oldURI, 0);
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ support-files =
|
|||||||
file_bug852909.pdf
|
file_bug852909.pdf
|
||||||
file_bug852909.png
|
file_bug852909.png
|
||||||
file_bug1046022.html
|
file_bug1046022.html
|
||||||
|
file_multiple_pushState.html
|
||||||
print_postdata.sjs
|
print_postdata.sjs
|
||||||
test-form_sjis.html
|
test-form_sjis.html
|
||||||
timelineMarkers-04.html
|
timelineMarkers-04.html
|
||||||
@ -98,6 +99,7 @@ skip-if = e10s # Bug ?????? - event handler checks event.target is the content d
|
|||||||
skip-if = e10s
|
skip-if = e10s
|
||||||
[browser_loadURI.js]
|
[browser_loadURI.js]
|
||||||
skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
|
skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
|
||||||
|
[browser_multiple_pushState.js]
|
||||||
[browser_onbeforeunload_navigation.js]
|
[browser_onbeforeunload_navigation.js]
|
||||||
skip-if = e10s
|
skip-if = e10s
|
||||||
[browser_search_notification.js]
|
[browser_search_notification.js]
|
||||||
|
15
docshell/test/browser/browser_multiple_pushState.js
Normal file
15
docshell/test/browser/browser_multiple_pushState.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
add_task(function* test_multiple_pushState() {
|
||||||
|
yield BrowserTestUtils.withNewTab({
|
||||||
|
gBrowser,
|
||||||
|
url: "http://example.org/browser/docshell/test/browser/file_multiple_pushState.html",
|
||||||
|
}, function* (browser) {
|
||||||
|
const kExpected = "http://example.org/bar/ABC/DEF?key=baz";
|
||||||
|
|
||||||
|
let contentLocation = yield ContentTask.spawn(browser, null, function* () {
|
||||||
|
return content.document.location.href;
|
||||||
|
});
|
||||||
|
|
||||||
|
is(contentLocation, kExpected);
|
||||||
|
is(browser.documentURI.spec, kExpected);
|
||||||
|
});
|
||||||
|
});
|
14
docshell/test/browser/file_multiple_pushState.html
Normal file
14
docshell/test/browser/file_multiple_pushState.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test multiple calls to history.pushState</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Ohai</h1>
|
||||||
|
</body>
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.history.pushState({}, "", "/bar/ABC?key=baz");
|
||||||
|
window.history.pushState({}, "", "/bar/ABC/DEF?key=baz");
|
||||||
|
</script>
|
||||||
|
</html>
|
@ -1785,7 +1785,8 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// We're going to run these against some non-global scope.
|
// We're going to run these against some non-global scope.
|
||||||
if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) {
|
options.setHasPollutedScope(true);
|
||||||
|
if (!JS::Compile(cx, options, srcBuf, &script)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -805,6 +805,9 @@ UploadLastDir::StoreLastUsedDirectory(nsIDocument* aDoc, nsIFile* aDir)
|
|||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
prefValue->SetAsAString(unicodePath);
|
prefValue->SetAsAString(unicodePath);
|
||||||
|
|
||||||
|
// Use the document's current load context to ensure that the content pref
|
||||||
|
// service doesn't persistently store this directory for this domain if the
|
||||||
|
// user is using private browsing:
|
||||||
nsCOMPtr<nsILoadContext> loadContext = aDoc->GetLoadContext();
|
nsCOMPtr<nsILoadContext> loadContext = aDoc->GetLoadContext();
|
||||||
return contentPrefService->Set(spec, CPS_PREF_NAME, prefValue, loadContext, nullptr);
|
return contentPrefService->Set(spec, CPS_PREF_NAME, prefValue, loadContext, nullptr);
|
||||||
}
|
}
|
||||||
@ -982,7 +985,7 @@ HTMLInputElement::Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) co
|
|||||||
nsAutoString value;
|
nsAutoString value;
|
||||||
GetValueInternal(value);
|
GetValueInternal(value);
|
||||||
// SetValueInternal handles setting the VALUE_CHANGED bit for us
|
// SetValueInternal handles setting the VALUE_CHANGED bit for us
|
||||||
rv = it->SetValueInternal(value, false, true);
|
rv = it->SetValueInternal(value, nsTextEditorState::eSetValue_Notify);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1167,7 +1170,8 @@ HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
|||||||
// if @max in the example above were to change from 1 to -1.
|
// if @max in the example above were to change from 1 to -1.
|
||||||
nsAutoString value;
|
nsAutoString value;
|
||||||
GetValue(value);
|
GetValue(value);
|
||||||
nsresult rv = SetValueInternal(value, false, false);
|
nsresult rv =
|
||||||
|
SetValueInternal(value, nsTextEditorState::eSetValue_Internal);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
|
MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
|
||||||
"HTML5 spec does not allow this");
|
"HTML5 spec does not allow this");
|
||||||
@ -1180,7 +1184,8 @@ HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
|||||||
// See @max comment
|
// See @max comment
|
||||||
nsAutoString value;
|
nsAutoString value;
|
||||||
GetValue(value);
|
GetValue(value);
|
||||||
nsresult rv = SetValueInternal(value, false, false);
|
nsresult rv =
|
||||||
|
SetValueInternal(value, nsTextEditorState::eSetValue_Internal);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
|
MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
|
||||||
"HTML5 spec does not allow this");
|
"HTML5 spec does not allow this");
|
||||||
@ -1191,7 +1196,8 @@ HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
|||||||
// See @max comment
|
// See @max comment
|
||||||
nsAutoString value;
|
nsAutoString value;
|
||||||
GetValue(value);
|
GetValue(value);
|
||||||
nsresult rv = SetValueInternal(value, false, false);
|
nsresult rv =
|
||||||
|
SetValueInternal(value, nsTextEditorState::eSetValue_Internal);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
|
MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
|
||||||
"HTML5 spec does not allow this");
|
"HTML5 spec does not allow this");
|
||||||
@ -1579,7 +1585,9 @@ HTMLInputElement::SetValue(const nsAString& aValue, ErrorResult& aRv)
|
|||||||
nsAutoString currentValue;
|
nsAutoString currentValue;
|
||||||
GetValue(currentValue);
|
GetValue(currentValue);
|
||||||
|
|
||||||
nsresult rv = SetValueInternal(aValue, false, true);
|
nsresult rv =
|
||||||
|
SetValueInternal(aValue, nsTextEditorState::eSetValue_ByContent |
|
||||||
|
nsTextEditorState::eSetValue_Notify);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
aRv.Throw(rv);
|
aRv.Throw(rv);
|
||||||
return;
|
return;
|
||||||
@ -1589,7 +1597,9 @@ HTMLInputElement::SetValue(const nsAString& aValue, ErrorResult& aRv)
|
|||||||
GetValue(mFocusedValue);
|
GetValue(mFocusedValue);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nsresult rv = SetValueInternal(aValue, false, true);
|
nsresult rv =
|
||||||
|
SetValueInternal(aValue, nsTextEditorState::eSetValue_ByContent |
|
||||||
|
nsTextEditorState::eSetValue_Notify);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
aRv.Throw(rv);
|
aRv.Throw(rv);
|
||||||
return;
|
return;
|
||||||
@ -2201,7 +2211,9 @@ HTMLInputElement::SetUserInput(const nsAString& aValue)
|
|||||||
MozSetFileNameArray(list, rv);
|
MozSetFileNameArray(list, rv);
|
||||||
return rv.StealNSResult();
|
return rv.StealNSResult();
|
||||||
} else {
|
} else {
|
||||||
nsresult rv = SetValueInternal(aValue, true, true);
|
nsresult rv =
|
||||||
|
SetValueInternal(aValue, nsTextEditorState::eSetValue_BySetUserInput |
|
||||||
|
nsTextEditorState::eSetValue_Notify);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2509,9 +2521,7 @@ HTMLInputElement::UpdateFileList()
|
|||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
HTMLInputElement::SetValueInternal(const nsAString& aValue,
|
HTMLInputElement::SetValueInternal(const nsAString& aValue, uint32_t aFlags)
|
||||||
bool aUserInput,
|
|
||||||
bool aSetValueChanged)
|
|
||||||
{
|
{
|
||||||
NS_PRECONDITION(GetValueMode() != VALUE_MODE_FILENAME,
|
NS_PRECONDITION(GetValueMode() != VALUE_MODE_FILENAME,
|
||||||
"Don't call SetValueInternal for file inputs");
|
"Don't call SetValueInternal for file inputs");
|
||||||
@ -2529,12 +2539,13 @@ HTMLInputElement::SetValueInternal(const nsAString& aValue,
|
|||||||
}
|
}
|
||||||
// else DoneCreatingElement calls us again once mParserCreating is false
|
// else DoneCreatingElement calls us again once mParserCreating is false
|
||||||
|
|
||||||
if (aSetValueChanged) {
|
bool setValueChanged = !!(aFlags & nsTextEditorState::eSetValue_Notify);
|
||||||
|
if (setValueChanged) {
|
||||||
SetValueChanged(true);
|
SetValueChanged(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsSingleLineTextControl(false)) {
|
if (IsSingleLineTextControl(false)) {
|
||||||
if (!mInputData.mState->SetValue(value, aUserInput, aSetValueChanged)) {
|
if (!mInputData.mState->SetValue(value, aFlags)) {
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
if (mType == NS_FORM_INPUT_EMAIL) {
|
if (mType == NS_FORM_INPUT_EMAIL) {
|
||||||
@ -2543,7 +2554,7 @@ HTMLInputElement::SetValueInternal(const nsAString& aValue,
|
|||||||
} else {
|
} else {
|
||||||
free(mInputData.mValue);
|
free(mInputData.mValue);
|
||||||
mInputData.mValue = ToNewUnicode(value);
|
mInputData.mValue = ToNewUnicode(value);
|
||||||
if (aSetValueChanged) {
|
if (setValueChanged) {
|
||||||
SetValueChanged(true);
|
SetValueChanged(true);
|
||||||
}
|
}
|
||||||
if (mType == NS_FORM_INPUT_NUMBER) {
|
if (mType == NS_FORM_INPUT_NUMBER) {
|
||||||
@ -3151,7 +3162,8 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
|
|||||||
if (IsExperimentalMobileType(mType)) {
|
if (IsExperimentalMobileType(mType)) {
|
||||||
nsAutoString aValue;
|
nsAutoString aValue;
|
||||||
GetValueInternal(aValue);
|
GetValueInternal(aValue);
|
||||||
nsresult rv = SetValueInternal(aValue, false, false);
|
nsresult rv =
|
||||||
|
SetValueInternal(aValue, nsTextEditorState::eSetValue_Internal);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
FireChangeEventIfNeeded();
|
FireChangeEventIfNeeded();
|
||||||
@ -3272,7 +3284,9 @@ HTMLInputElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
|
|||||||
numberControlFrame->GetValueOfAnonTextControl(value);
|
numberControlFrame->GetValueOfAnonTextControl(value);
|
||||||
numberControlFrame->HandlingInputEvent(true);
|
numberControlFrame->HandlingInputEvent(true);
|
||||||
nsWeakFrame weakNumberControlFrame(numberControlFrame);
|
nsWeakFrame weakNumberControlFrame(numberControlFrame);
|
||||||
rv = SetValueInternal(value, true, true);
|
rv = SetValueInternal(value,
|
||||||
|
nsTextEditorState::eSetValue_BySetUserInput |
|
||||||
|
nsTextEditorState::eSetValue_Notify);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
if (weakNumberControlFrame.IsAlive()) {
|
if (weakNumberControlFrame.IsAlive()) {
|
||||||
numberControlFrame->HandlingInputEvent(false);
|
numberControlFrame->HandlingInputEvent(false);
|
||||||
@ -3349,7 +3363,8 @@ HTMLInputElement::CancelRangeThumbDrag(bool aIsForUserEvent)
|
|||||||
ConvertNumberToString(mRangeThumbDragStartValue, val);
|
ConvertNumberToString(mRangeThumbDragStartValue, val);
|
||||||
// TODO: What should we do if SetValueInternal fails? (The allocation
|
// TODO: What should we do if SetValueInternal fails? (The allocation
|
||||||
// is small, so we should be fine here.)
|
// is small, so we should be fine here.)
|
||||||
SetValueInternal(val, true, true);
|
SetValueInternal(val, nsTextEditorState::eSetValue_BySetUserInput |
|
||||||
|
nsTextEditorState::eSetValue_Notify);
|
||||||
nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
|
nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
|
||||||
if (frame) {
|
if (frame) {
|
||||||
frame->UpdateForValueChange();
|
frame->UpdateForValueChange();
|
||||||
@ -3369,7 +3384,8 @@ HTMLInputElement::SetValueOfRangeForUserEvent(Decimal aValue)
|
|||||||
ConvertNumberToString(aValue, val);
|
ConvertNumberToString(aValue, val);
|
||||||
// TODO: What should we do if SetValueInternal fails? (The allocation
|
// TODO: What should we do if SetValueInternal fails? (The allocation
|
||||||
// is small, so we should be fine here.)
|
// is small, so we should be fine here.)
|
||||||
SetValueInternal(val, true, true);
|
SetValueInternal(val, nsTextEditorState::eSetValue_BySetUserInput |
|
||||||
|
nsTextEditorState::eSetValue_Notify);
|
||||||
nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
|
nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
|
||||||
if (frame) {
|
if (frame) {
|
||||||
frame->UpdateForValueChange();
|
frame->UpdateForValueChange();
|
||||||
@ -3462,7 +3478,8 @@ HTMLInputElement::StepNumberControlForUserEvent(int32_t aDirection)
|
|||||||
ConvertNumberToString(newValue, newVal);
|
ConvertNumberToString(newValue, newVal);
|
||||||
// TODO: What should we do if SetValueInternal fails? (The allocation
|
// TODO: What should we do if SetValueInternal fails? (The allocation
|
||||||
// is small, so we should be fine here.)
|
// is small, so we should be fine here.)
|
||||||
SetValueInternal(newVal, true, true);
|
SetValueInternal(newVal, nsTextEditorState::eSetValue_BySetUserInput |
|
||||||
|
nsTextEditorState::eSetValue_Notify);
|
||||||
|
|
||||||
nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
|
nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
|
||||||
static_cast<nsIDOMHTMLInputElement*>(this),
|
static_cast<nsIDOMHTMLInputElement*>(this),
|
||||||
@ -4276,7 +4293,7 @@ HTMLInputElement::HandleTypeChange(uint8_t aNewType)
|
|||||||
// TODO: What should we do if SetValueInternal fails? (The allocation
|
// TODO: What should we do if SetValueInternal fails? (The allocation
|
||||||
// may potentially be big, but most likely we've failed to allocate
|
// may potentially be big, but most likely we've failed to allocate
|
||||||
// before the type change.)
|
// before the type change.)
|
||||||
SetValueInternal(value, false, false);
|
SetValueInternal(value, nsTextEditorState::eSetValue_Internal);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case VALUE_MODE_FILENAME:
|
case VALUE_MODE_FILENAME:
|
||||||
@ -4959,7 +4976,8 @@ HTMLInputElement::SetRangeText(const nsAString& aReplacement, uint32_t aStart,
|
|||||||
|
|
||||||
if (aStart <= aEnd) {
|
if (aStart <= aEnd) {
|
||||||
value.Replace(aStart, aEnd - aStart, aReplacement);
|
value.Replace(aStart, aEnd - aStart, aReplacement);
|
||||||
nsresult rv = SetValueInternal(value, false, false);
|
nsresult rv =
|
||||||
|
SetValueInternal(value, nsTextEditorState::eSetValue_ByContent);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
aRv.Throw(rv);
|
aRv.Throw(rv);
|
||||||
return;
|
return;
|
||||||
@ -5284,7 +5302,7 @@ HTMLInputElement::SetDefaultValueAsValue()
|
|||||||
GetDefaultValue(resetVal);
|
GetDefaultValue(resetVal);
|
||||||
|
|
||||||
// SetValueInternal is going to sanitize the value.
|
// SetValueInternal is going to sanitize the value.
|
||||||
return SetValueInternal(resetVal, false, false);
|
return SetValueInternal(resetVal, nsTextEditorState::eSetValue_Internal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -5525,7 +5543,7 @@ HTMLInputElement::DoneCreatingElement()
|
|||||||
// TODO: What should we do if SetValueInternal fails? (The allocation
|
// TODO: What should we do if SetValueInternal fails? (The allocation
|
||||||
// may potentially be big, but most likely we've failed to allocate
|
// may potentially be big, but most likely we've failed to allocate
|
||||||
// before the type change.)
|
// before the type change.)
|
||||||
SetValueInternal(aValue, false, false);
|
SetValueInternal(aValue, nsTextEditorState::eSetValue_Internal);
|
||||||
}
|
}
|
||||||
|
|
||||||
mShouldInitChecked = false;
|
mShouldInitChecked = false;
|
||||||
@ -5684,7 +5702,8 @@ HTMLInputElement::RestoreState(nsPresState* aState)
|
|||||||
// TODO: What should we do if SetValueInternal fails? (The allocation
|
// TODO: What should we do if SetValueInternal fails? (The allocation
|
||||||
// may potentially be big, but most likely we've failed to allocate
|
// may potentially be big, but most likely we've failed to allocate
|
||||||
// before the type change.)
|
// before the type change.)
|
||||||
SetValueInternal(inputState->GetValue(), false, true);
|
SetValueInternal(inputState->GetValue(),
|
||||||
|
nsTextEditorState::eSetValue_Notify);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,8 +39,17 @@ class Date;
|
|||||||
class File;
|
class File;
|
||||||
class FileList;
|
class FileList;
|
||||||
|
|
||||||
class UploadLastDir final : public nsIObserver, public nsSupportsWeakReference {
|
/**
|
||||||
|
* A class we use to create a singleton object that is used to keep track of
|
||||||
|
* the last directory from which the user has picked files (via
|
||||||
|
* <input type=file>) on a per-domain basis. The implementation uses
|
||||||
|
* nsIContentPrefService2/NS_CONTENT_PREF_SERVICE_CONTRACTID to store the last
|
||||||
|
* directory per-domain, and to ensure that whether the directories are
|
||||||
|
* persistently saved (saved across sessions) or not honors whether or not the
|
||||||
|
* page is being viewed in private browsing.
|
||||||
|
*/
|
||||||
|
class UploadLastDir final : public nsIObserver, public nsSupportsWeakReference
|
||||||
|
{
|
||||||
~UploadLastDir() {}
|
~UploadLastDir() {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -810,9 +819,14 @@ protected:
|
|||||||
uint32_t aLen, uint32_t* aResult);
|
uint32_t aLen, uint32_t* aResult);
|
||||||
|
|
||||||
// Helper method
|
// Helper method
|
||||||
nsresult SetValueInternal(const nsAString& aValue,
|
|
||||||
bool aUserInput,
|
/**
|
||||||
bool aSetValueChanged);
|
* Setting the value.
|
||||||
|
*
|
||||||
|
* @param aValue String to set.
|
||||||
|
* @param aFlags See nsTextEditorState::SetValueFlags.
|
||||||
|
*/
|
||||||
|
nsresult SetValueInternal(const nsAString& aValue, uint32_t aFlags);
|
||||||
|
|
||||||
nsresult GetValueInternal(nsAString& aValue) const;
|
nsresult GetValueInternal(nsAString& aValue) const;
|
||||||
|
|
||||||
|
@ -3110,7 +3110,17 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
|
|||||||
ChangeDelayLoadStatus(false);
|
ChangeDelayLoadStatus(false);
|
||||||
GetSrcMediaStream()->AddAudioOutput(this);
|
GetSrcMediaStream()->AddAudioOutput(this);
|
||||||
SetVolumeInternal();
|
SetVolumeInternal();
|
||||||
VideoFrameContainer* container = GetVideoFrameContainer();
|
|
||||||
|
bool bUseOverlayImage = mSrcStream->AsDOMHwMediaStream() != nullptr;
|
||||||
|
VideoFrameContainer* container;
|
||||||
|
|
||||||
|
if (bUseOverlayImage) {
|
||||||
|
container = GetOverlayImageVideoFrameContainer();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
container = GetVideoFrameContainer();
|
||||||
|
}
|
||||||
|
|
||||||
if (container) {
|
if (container) {
|
||||||
GetSrcMediaStream()->AddVideoOutput(container);
|
GetSrcMediaStream()->AddVideoOutput(container);
|
||||||
}
|
}
|
||||||
@ -3778,7 +3788,23 @@ VideoFrameContainer* HTMLMediaElement::GetVideoFrameContainer()
|
|||||||
}
|
}
|
||||||
|
|
||||||
mVideoFrameContainer =
|
mVideoFrameContainer =
|
||||||
new VideoFrameContainer(this, LayerManager::CreateAsynchronousImageContainer());
|
new VideoFrameContainer(this, LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS));
|
||||||
|
|
||||||
|
return mVideoFrameContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoFrameContainer* HTMLMediaElement::GetOverlayImageVideoFrameContainer()
|
||||||
|
{
|
||||||
|
if (mVideoFrameContainer)
|
||||||
|
return mVideoFrameContainer;
|
||||||
|
|
||||||
|
// Only video frames need an image container.
|
||||||
|
if (!IsVideo()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
mVideoFrameContainer =
|
||||||
|
new VideoFrameContainer(this, LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS_OVERLAY));
|
||||||
|
|
||||||
return mVideoFrameContainer;
|
return mVideoFrameContainer;
|
||||||
}
|
}
|
||||||
|
@ -212,6 +212,9 @@ public:
|
|||||||
|
|
||||||
virtual bool IsHidden() final override;
|
virtual bool IsHidden() final override;
|
||||||
|
|
||||||
|
// In order to create overlayImageContainer to support DOMHwMediaStream.
|
||||||
|
VideoFrameContainer* GetOverlayImageVideoFrameContainer();
|
||||||
|
|
||||||
// Called by the media decoder and the video frame to get the
|
// Called by the media decoder and the video frame to get the
|
||||||
// ImageContainer containing the video data.
|
// ImageContainer containing the video data.
|
||||||
virtual VideoFrameContainer* GetVideoFrameContainer() final override;
|
virtual VideoFrameContainer* GetVideoFrameContainer() final override;
|
||||||
|
@ -297,13 +297,14 @@ HTMLTextAreaElement::GetPlaceholderVisibility()
|
|||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
HTMLTextAreaElement::SetValueInternal(const nsAString& aValue,
|
HTMLTextAreaElement::SetValueInternal(const nsAString& aValue,
|
||||||
bool aUserInput)
|
uint32_t aFlags)
|
||||||
{
|
{
|
||||||
// Need to set the value changed flag here, so that
|
// Need to set the value changed flag here, so that
|
||||||
// nsTextControlFrame::UpdateValueDisplay retrieves the correct value
|
// nsTextControlFrame::UpdateValueDisplay retrieves the correct value
|
||||||
// if needed.
|
// if needed.
|
||||||
SetValueChanged(true);
|
SetValueChanged(true);
|
||||||
if (!mState.SetValue(aValue, aUserInput, true)) {
|
aFlags |= nsTextEditorState::eSetValue_Notify;
|
||||||
|
if (!mState.SetValue(aValue, aFlags)) {
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,7 +324,8 @@ HTMLTextAreaElement::SetValue(const nsAString& aValue)
|
|||||||
nsAutoString currentValue;
|
nsAutoString currentValue;
|
||||||
GetValueInternal(currentValue, true);
|
GetValueInternal(currentValue, true);
|
||||||
|
|
||||||
nsresult rv = SetValueInternal(aValue, false);
|
nsresult rv =
|
||||||
|
SetValueInternal(aValue, nsTextEditorState::eSetValue_ByContent);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
if (mFocusedValue.Equals(currentValue)) {
|
if (mFocusedValue.Equals(currentValue)) {
|
||||||
@ -339,7 +341,7 @@ HTMLTextAreaElement::SetUserInput(const nsAString& aValue)
|
|||||||
if (!nsContentUtils::IsCallerChrome()) {
|
if (!nsContentUtils::IsCallerChrome()) {
|
||||||
return NS_ERROR_DOM_SECURITY_ERR;
|
return NS_ERROR_DOM_SECURITY_ERR;
|
||||||
}
|
}
|
||||||
return SetValueInternal(aValue, true);
|
return SetValueInternal(aValue, nsTextEditorState::eSetValue_BySetUserInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
@ -968,7 +970,8 @@ HTMLTextAreaElement::SetRangeText(const nsAString& aReplacement,
|
|||||||
|
|
||||||
if (aStart <= aEnd) {
|
if (aStart <= aEnd) {
|
||||||
value.Replace(aStart, aEnd - aStart, aReplacement);
|
value.Replace(aStart, aEnd - aStart, aReplacement);
|
||||||
nsresult rv = SetValueInternal(value, false);
|
nsresult rv =
|
||||||
|
SetValueInternal(value, nsTextEditorState::eSetValue_ByContent);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
aRv.Throw(rv);
|
aRv.Throw(rv);
|
||||||
return;
|
return;
|
||||||
|
@ -322,8 +322,14 @@ protected:
|
|||||||
*/
|
*/
|
||||||
void GetValueInternal(nsAString& aValue, bool aIgnoreWrap) const;
|
void GetValueInternal(nsAString& aValue, bool aIgnoreWrap) const;
|
||||||
|
|
||||||
nsresult SetValueInternal(const nsAString& aValue,
|
/**
|
||||||
bool aUserInput);
|
* Setting the value.
|
||||||
|
*
|
||||||
|
* @param aValue String to set.
|
||||||
|
* @param aFlags See nsTextEditorState::SetValueFlags.
|
||||||
|
*/
|
||||||
|
nsresult SetValueInternal(const nsAString& aValue, uint32_t aFlags);
|
||||||
|
|
||||||
nsresult GetSelectionRange(int32_t* aSelectionStart, int32_t* aSelectionEnd);
|
nsresult GetSelectionRange(int32_t* aSelectionStart, int32_t* aSelectionEnd);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "nsAttrValueInlines.h"
|
#include "nsAttrValueInlines.h"
|
||||||
#include "nsGenericHTMLElement.h"
|
#include "nsGenericHTMLElement.h"
|
||||||
#include "nsIDOMEventListener.h"
|
#include "nsIDOMEventListener.h"
|
||||||
|
#include "nsIEditorIMESupport.h"
|
||||||
#include "nsIEditorObserver.h"
|
#include "nsIEditorObserver.h"
|
||||||
#include "nsIWidget.h"
|
#include "nsIWidget.h"
|
||||||
#include "nsIDocumentEncoder.h"
|
#include "nsIDocumentEncoder.h"
|
||||||
@ -1015,15 +1016,16 @@ nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate,
|
|||||||
// nsTextEditorState
|
// nsTextEditorState
|
||||||
|
|
||||||
nsTextEditorState::nsTextEditorState(nsITextControlElement* aOwningElement)
|
nsTextEditorState::nsTextEditorState(nsITextControlElement* aOwningElement)
|
||||||
: mTextCtrlElement(aOwningElement),
|
: mTextCtrlElement(aOwningElement)
|
||||||
mBoundFrame(nullptr),
|
, mBoundFrame(nullptr)
|
||||||
mEverInited(false),
|
, mEverInited(false)
|
||||||
mEditorInitialized(false),
|
, mEditorInitialized(false)
|
||||||
mInitializing(false),
|
, mInitializing(false)
|
||||||
mValueTransferInProgress(false),
|
, mValueTransferInProgress(false)
|
||||||
mSelectionCached(true),
|
, mSelectionCached(true)
|
||||||
mSelectionRestoreEagerInit(false),
|
, mSelectionRestoreEagerInit(false)
|
||||||
mPlaceholderVisibility(false)
|
, mPlaceholderVisibility(false)
|
||||||
|
, mIsCommittingComposition(false)
|
||||||
{
|
{
|
||||||
MOZ_COUNT_CTOR(nsTextEditorState);
|
MOZ_COUNT_CTOR(nsTextEditorState);
|
||||||
}
|
}
|
||||||
@ -1441,7 +1443,7 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
|
|||||||
rv = newEditor->EnableUndo(false);
|
rv = newEditor->EnableUndo(false);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
bool success = SetValue(defaultValue, false, false);
|
bool success = SetValue(defaultValue, eSetValue_Internal);
|
||||||
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
|
NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
rv = newEditor->EnableUndo(true);
|
rv = newEditor->EnableUndo(true);
|
||||||
@ -1705,7 +1707,7 @@ nsTextEditorState::UnbindFromFrame(nsTextControlFrame* aFrame)
|
|||||||
// Now that we don't have a frame any more, store the value in the text buffer.
|
// Now that we don't have a frame any more, store the value in the text buffer.
|
||||||
// The only case where we don't do this is if a value transfer is in progress.
|
// The only case where we don't do this is if a value transfer is in progress.
|
||||||
if (!mValueTransferInProgress) {
|
if (!mValueTransferInProgress) {
|
||||||
bool success = SetValue(value, false, false);
|
bool success = SetValue(value, eSetValue_Internal);
|
||||||
// TODO Find something better to do if this fails...
|
// TODO Find something better to do if this fails...
|
||||||
NS_ENSURE_TRUE_VOID(success);
|
NS_ENSURE_TRUE_VOID(success);
|
||||||
}
|
}
|
||||||
@ -1860,6 +1862,15 @@ nsTextEditorState::GetMaxLength(int32_t* aMaxLength)
|
|||||||
void
|
void
|
||||||
nsTextEditorState::GetValue(nsAString& aValue, bool aIgnoreWrap) const
|
nsTextEditorState::GetValue(nsAString& aValue, bool aIgnoreWrap) const
|
||||||
{
|
{
|
||||||
|
// While SetValue() is being called and requesting to commit composition to
|
||||||
|
// IME, GetValue() may be called for appending text or something. Then, we
|
||||||
|
// need to return the latest aValue of SetValue() since the value hasn't
|
||||||
|
// been set to the editor yet.
|
||||||
|
if (mIsCommittingComposition) {
|
||||||
|
aValue = mValueBeingSet;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (mEditor && mBoundFrame && (mEditorInitialized || !IsSingleLineTextControl())) {
|
if (mEditor && mBoundFrame && (mEditorInitialized || !IsSingleLineTextControl())) {
|
||||||
bool canCache = aIgnoreWrap && !IsSingleLineTextControl();
|
bool canCache = aIgnoreWrap && !IsSingleLineTextControl();
|
||||||
if (canCache && !mCachedValue.IsEmpty()) {
|
if (canCache && !mCachedValue.IsEmpty()) {
|
||||||
@ -1921,9 +1932,73 @@ nsTextEditorState::GetValue(nsAString& aValue, bool aIgnoreWrap) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
nsTextEditorState::SetValue(const nsAString& aValue, bool aUserInput,
|
nsTextEditorState::SetValue(const nsAString& aValue, uint32_t aFlags)
|
||||||
bool aSetValueChanged)
|
|
||||||
{
|
{
|
||||||
|
nsAutoString newValue(aValue);
|
||||||
|
|
||||||
|
// While mIsCommittingComposition is true (that means that some event
|
||||||
|
// handlers which are fired during committing composition are the caller of
|
||||||
|
// this method), GetValue() uses mValueBeingSet for its result because the
|
||||||
|
// first calls of this methods hasn't set the value yet. So, when it's true,
|
||||||
|
// we need to modify mValueBeingSet. In this case, we will back to the first
|
||||||
|
// call of this method, then, mValueBeingSet will be truncated when
|
||||||
|
// mIsCommittingComposition is set false. See below.
|
||||||
|
if (mIsCommittingComposition) {
|
||||||
|
mValueBeingSet = aValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that if this may be called during reframe of the editor. In such
|
||||||
|
// case, we shouldn't commit composition. Therefore, when this is called
|
||||||
|
// for internal processing, we shouldn't commit the composition.
|
||||||
|
if (aFlags & (eSetValue_BySetUserInput | eSetValue_ByContent)) {
|
||||||
|
if (EditorHasComposition()) {
|
||||||
|
// When this is called recursively, there shouldn't be composition.
|
||||||
|
if (NS_WARN_IF(mIsCommittingComposition)) {
|
||||||
|
// Don't request to commit composition again. But if it occurs,
|
||||||
|
// we should skip to set the new value to the editor here. It should
|
||||||
|
// be set later with the updated mValueBeingSet.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// If there is composition, need to commit composition first because
|
||||||
|
// other browsers do that.
|
||||||
|
// NOTE: We don't need to block nested calls of this because input nor
|
||||||
|
// other events won't be fired by setting values and script blocker
|
||||||
|
// is used during setting the value to the editor. IE also allows
|
||||||
|
// to set the editor value on the input event which is caused by
|
||||||
|
// forcibly committing composition.
|
||||||
|
if (nsContentUtils::IsSafeToRunScript()) {
|
||||||
|
WeakPtr<nsTextEditorState> self(this);
|
||||||
|
// WARNING: During this call, compositionupdate, compositionend, input
|
||||||
|
// events will be fired. Therefore, everything can occur. E.g., the
|
||||||
|
// document may be unloaded.
|
||||||
|
mValueBeingSet = aValue;
|
||||||
|
mIsCommittingComposition = true;
|
||||||
|
nsCOMPtr<nsIEditorIMESupport> editorIMESupport =
|
||||||
|
do_QueryInterface(mEditor);
|
||||||
|
MOZ_RELEASE_ASSERT(editorIMESupport);
|
||||||
|
nsresult rv = editorIMESupport->ForceCompositionEnd();
|
||||||
|
if (!self.get()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
mIsCommittingComposition = false;
|
||||||
|
// If this is called recursively during committing composition and
|
||||||
|
// some of them may be skipped above. Therefore, we need to set
|
||||||
|
// value to the editor with the aValue of the latest call.
|
||||||
|
newValue = mValueBeingSet;
|
||||||
|
// When mIsCommittingComposition is false, mValueBeingSet won't be
|
||||||
|
// used. Therefore, let's clear it.
|
||||||
|
mValueBeingSet.Truncate();
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
NS_WARNING("nsTextEditorState failed to commit composition");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NS_WARNING("SetValue() is called when there is composition but "
|
||||||
|
"it's not safe to request to commit the composition");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mEditor && mBoundFrame) {
|
if (mEditor && mBoundFrame) {
|
||||||
// The InsertText call below might flush pending notifications, which
|
// The InsertText call below might flush pending notifications, which
|
||||||
// could lead into a scheduled PrepareEditor to be called. That will
|
// could lead into a scheduled PrepareEditor to be called. That will
|
||||||
@ -1945,19 +2020,13 @@ nsTextEditorState::SetValue(const nsAString& aValue, bool aUserInput,
|
|||||||
nsWeakFrame weakFrame(mBoundFrame);
|
nsWeakFrame weakFrame(mBoundFrame);
|
||||||
|
|
||||||
// this is necessary to avoid infinite recursion
|
// this is necessary to avoid infinite recursion
|
||||||
if (!currentValue.Equals(aValue))
|
if (!currentValue.Equals(newValue))
|
||||||
{
|
{
|
||||||
ValueSetter valueSetter(mEditor);
|
ValueSetter valueSetter(mEditor);
|
||||||
|
|
||||||
// \r is an illegal character in the dom, but people use them,
|
// \r is an illegal character in the dom, but people use them,
|
||||||
// so convert windows and mac platform linebreaks to \n:
|
// so convert windows and mac platform linebreaks to \n:
|
||||||
// Unfortunately aValue is declared const, so we have to copy
|
if (newValue.FindChar(char16_t('\r')) != -1) {
|
||||||
// in order to do this substitution.
|
|
||||||
nsString newValue;
|
|
||||||
if (!newValue.Assign(aValue, fallible)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (aValue.FindChar(char16_t('\r')) != -1) {
|
|
||||||
if (!nsContentUtils::PlatformToDOMLineBreaks(newValue, fallible)) {
|
if (!nsContentUtils::PlatformToDOMLineBreaks(newValue, fallible)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2020,7 +2089,8 @@ nsTextEditorState::SetValue(const nsAString& aValue, bool aUserInput,
|
|||||||
mEditor->SetFlags(flags);
|
mEditor->SetFlags(flags);
|
||||||
|
|
||||||
mTextListener->SettingValue(true);
|
mTextListener->SettingValue(true);
|
||||||
mTextListener->SetValueChanged(aSetValueChanged);
|
bool notifyValueChanged = !!(aFlags & eSetValue_Notify);
|
||||||
|
mTextListener->SetValueChanged(notifyValueChanged);
|
||||||
|
|
||||||
// Also don't enforce max-length here
|
// Also don't enforce max-length here
|
||||||
int32_t savedMaxLength;
|
int32_t savedMaxLength;
|
||||||
@ -2043,7 +2113,7 @@ nsTextEditorState::SetValue(const nsAString& aValue, bool aUserInput,
|
|||||||
// the existing selection -- see bug 574558), in which case we don't
|
// the existing selection -- see bug 574558), in which case we don't
|
||||||
// need to reset the value here.
|
// need to reset the value here.
|
||||||
if (!mBoundFrame) {
|
if (!mBoundFrame) {
|
||||||
return SetValue(newValue, false, aSetValueChanged);
|
return SetValue(newValue, aFlags & eSetValue_Notify);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2065,7 +2135,7 @@ nsTextEditorState::SetValue(const nsAString& aValue, bool aUserInput,
|
|||||||
mValue = new nsCString;
|
mValue = new nsCString;
|
||||||
}
|
}
|
||||||
nsString value;
|
nsString value;
|
||||||
if (!value.Assign(aValue, fallible)) {
|
if (!value.Assign(newValue, fallible)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!nsContentUtils::PlatformToDOMLineBreaks(value, fallible)) {
|
if (!nsContentUtils::PlatformToDOMLineBreaks(value, fallible)) {
|
||||||
@ -2165,6 +2235,16 @@ nsTextEditorState::HideSelectionIfBlurred()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
nsTextEditorState::EditorHasComposition()
|
||||||
|
{
|
||||||
|
bool isComposing = false;
|
||||||
|
nsCOMPtr<nsIEditorIMESupport> editorIMESupport = do_QueryInterface(mEditor);
|
||||||
|
return editorIMESupport &&
|
||||||
|
NS_SUCCEEDED(editorIMESupport->GetComposing(&isComposing)) &&
|
||||||
|
isComposing;
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS(nsAnonDivObserver, nsIMutationObserver)
|
NS_IMPL_ISUPPORTS(nsAnonDivObserver, nsIMutationObserver)
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -143,9 +143,20 @@ public:
|
|||||||
nsresult PrepareEditor(const nsAString *aValue = nullptr);
|
nsresult PrepareEditor(const nsAString *aValue = nullptr);
|
||||||
void InitializeKeyboardEventListeners();
|
void InitializeKeyboardEventListeners();
|
||||||
|
|
||||||
|
enum SetValueFlags
|
||||||
|
{
|
||||||
|
// The call is for internal processing.
|
||||||
|
eSetValue_Internal = 0,
|
||||||
|
// The value is changed by a call of setUserInput() from chrome.
|
||||||
|
eSetValue_BySetUserInput = 1 << 0,
|
||||||
|
// The value is changed by changing value attribute of the element or
|
||||||
|
// something like setRangeText().
|
||||||
|
eSetValue_ByContent = 1 << 1,
|
||||||
|
// Whether the value change should be notified to the frame/contet nor not.
|
||||||
|
eSetValue_Notify = 1 << 2
|
||||||
|
};
|
||||||
MOZ_WARN_UNUSED_RESULT bool SetValue(const nsAString& aValue,
|
MOZ_WARN_UNUSED_RESULT bool SetValue(const nsAString& aValue,
|
||||||
bool aUserInput,
|
uint32_t aFlags);
|
||||||
bool aSetValueAsChanged);
|
|
||||||
void GetValue(nsAString& aValue, bool aIgnoreWrap) const;
|
void GetValue(nsAString& aValue, bool aIgnoreWrap) const;
|
||||||
void EmptyValue() { if (mValue) mValue->Truncate(); }
|
void EmptyValue() { if (mValue) mValue->Truncate(); }
|
||||||
bool IsEmpty() const { return mValue ? mValue->IsEmpty() : true; }
|
bool IsEmpty() const { return mValue ? mValue->IsEmpty() : true; }
|
||||||
@ -244,6 +255,8 @@ private:
|
|||||||
|
|
||||||
mozilla::dom::HTMLInputElement* GetParentNumberControl(nsFrame* aFrame) const;
|
mozilla::dom::HTMLInputElement* GetParentNumberControl(nsFrame* aFrame) const;
|
||||||
|
|
||||||
|
bool EditorHasComposition();
|
||||||
|
|
||||||
class InitializationGuard {
|
class InitializationGuard {
|
||||||
public:
|
public:
|
||||||
explicit InitializationGuard(nsTextEditorState& aState) :
|
explicit InitializationGuard(nsTextEditorState& aState) :
|
||||||
@ -283,14 +296,20 @@ private:
|
|||||||
nsAutoPtr<nsCString> mValue;
|
nsAutoPtr<nsCString> mValue;
|
||||||
nsRefPtr<nsAnonDivObserver> mMutationObserver;
|
nsRefPtr<nsAnonDivObserver> mMutationObserver;
|
||||||
mutable nsString mCachedValue; // Caches non-hard-wrapped value on a multiline control.
|
mutable nsString mCachedValue; // Caches non-hard-wrapped value on a multiline control.
|
||||||
|
// mValueBeingSet is available only while SetValue() is requesting to commit
|
||||||
|
// composition. I.e., this is valid only while mIsCommittingComposition is
|
||||||
|
// true. While active composition is being committed, GetValue() needs
|
||||||
|
// the latest value which is set by SetValue(). So, this is cache for that.
|
||||||
|
nsString mValueBeingSet;
|
||||||
|
SelectionProperties mSelectionProperties;
|
||||||
bool mEverInited; // Have we ever been initialized?
|
bool mEverInited; // Have we ever been initialized?
|
||||||
bool mEditorInitialized;
|
bool mEditorInitialized;
|
||||||
bool mInitializing; // Whether we're in the process of initialization
|
bool mInitializing; // Whether we're in the process of initialization
|
||||||
bool mValueTransferInProgress; // Whether a value is being transferred to the frame
|
bool mValueTransferInProgress; // Whether a value is being transferred to the frame
|
||||||
bool mSelectionCached; // Whether mSelectionProperties is valid
|
bool mSelectionCached; // Whether mSelectionProperties is valid
|
||||||
mutable bool mSelectionRestoreEagerInit; // Whether we're eager initing because of selection restore
|
mutable bool mSelectionRestoreEagerInit; // Whether we're eager initing because of selection restore
|
||||||
SelectionProperties mSelectionProperties;
|
|
||||||
bool mPlaceholderVisibility;
|
bool mPlaceholderVisibility;
|
||||||
|
bool mIsCommittingComposition;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
|
@ -41,6 +41,12 @@ parent:
|
|||||||
* native HWND of the plugin widget.
|
* native HWND of the plugin widget.
|
||||||
*/
|
*/
|
||||||
sync GetNativePluginPort() returns (uintptr_t value);
|
sync GetNativePluginPort() returns (uintptr_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends an NS_NATIVE_CHILD_WINDOW to be adopted by the widget's native window
|
||||||
|
* on the chrome side. This is only currently used on Windows.
|
||||||
|
*/
|
||||||
|
sync SetNativeChildWindow(uintptr_t childWindow);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2645,12 +2645,6 @@ TabParent::TryCacheDPIAndScale()
|
|||||||
|
|
||||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||||
|
|
||||||
if (!widget && mFrameElement) {
|
|
||||||
// Even if we don't have a widget (e.g. because we're display:none), there's
|
|
||||||
// probably a widget somewhere in the hierarchy our frame element lives in.
|
|
||||||
widget = nsContentUtils::WidgetForDocument(mFrameElement->OwnerDoc());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (widget) {
|
if (widget) {
|
||||||
mDPI = widget->GetDPI();
|
mDPI = widget->GetDPI();
|
||||||
mDefaultScale = widget->GetDefaultScale();
|
mDefaultScale = widget->GetDefaultScale();
|
||||||
@ -2660,15 +2654,10 @@ TabParent::TryCacheDPIAndScale()
|
|||||||
already_AddRefed<nsIWidget>
|
already_AddRefed<nsIWidget>
|
||||||
TabParent::GetWidget() const
|
TabParent::GetWidget() const
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIContent> content = do_QueryInterface(mFrameElement);
|
if (!mFrameElement) {
|
||||||
if (!content)
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
nsIFrame *frame = content->GetPrimaryFrame();
|
nsCOMPtr<nsIWidget> widget = nsContentUtils::WidgetForDocument(mFrameElement->OwnerDoc());
|
||||||
if (!frame)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
nsCOMPtr<nsIWidget> widget = frame->GetNearestWidget();
|
|
||||||
return widget.forget();
|
return widget.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,9 +18,13 @@
|
|||||||
#include "MediaStreamGraph.h"
|
#include "MediaStreamGraph.h"
|
||||||
#include "AudioStreamTrack.h"
|
#include "AudioStreamTrack.h"
|
||||||
#include "VideoStreamTrack.h"
|
#include "VideoStreamTrack.h"
|
||||||
|
#include "Layers.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::dom;
|
using namespace mozilla::dom;
|
||||||
|
using namespace mozilla::layers;
|
||||||
|
|
||||||
|
const TrackID TRACK_VIDEO_PRIMARY = 1;
|
||||||
|
|
||||||
class DOMMediaStream::StreamListener : public MediaStreamListener {
|
class DOMMediaStream::StreamListener : public MediaStreamListener {
|
||||||
public:
|
public:
|
||||||
@ -668,3 +672,112 @@ DOMAudioNodeMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow,
|
|||||||
return stream.forget();
|
return stream.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DOMHwMediaStream::DOMHwMediaStream()
|
||||||
|
{
|
||||||
|
#ifdef MOZ_WIDGET_GONK
|
||||||
|
mImageContainer = LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS_OVERLAY);
|
||||||
|
nsRefPtr<Image> img = mImageContainer->CreateImage(ImageFormat::OVERLAY_IMAGE);
|
||||||
|
mOverlayImage = static_cast<layers::OverlayImage*>(img.get());
|
||||||
|
mImageContainer->SetCurrentImage(mOverlayImage);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
DOMHwMediaStream::~DOMHwMediaStream()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<DOMHwMediaStream>
|
||||||
|
DOMHwMediaStream::CreateHwStream(nsIDOMWindow* aWindow)
|
||||||
|
{
|
||||||
|
nsRefPtr<DOMHwMediaStream> stream = new DOMHwMediaStream();
|
||||||
|
stream->InitSourceStream(aWindow);
|
||||||
|
stream->Init(stream->GetStream());
|
||||||
|
|
||||||
|
return stream.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DOMHwMediaStream::Init(MediaStream* stream)
|
||||||
|
{
|
||||||
|
SourceMediaStream* srcStream = stream->AsSourceStream();
|
||||||
|
|
||||||
|
if (srcStream) {
|
||||||
|
VideoSegment segment;
|
||||||
|
#ifdef MOZ_WIDGET_GONK
|
||||||
|
const StreamTime delta = STREAM_TIME_MAX; // Because MediaStreamGraph will run out frames in non-autoplay mode,
|
||||||
|
// we must give it bigger frame length to cover this situation.
|
||||||
|
mImageData.mOverlayId = DEFAULT_IMAGE_ID;
|
||||||
|
mImageData.mSize.width = DEFAULT_IMAGE_WIDTH;
|
||||||
|
mImageData.mSize.height = DEFAULT_IMAGE_HEIGHT;
|
||||||
|
mOverlayImage->SetData(mImageData);
|
||||||
|
|
||||||
|
nsRefPtr<Image> image = static_cast<Image*>(mOverlayImage.get());
|
||||||
|
mozilla::gfx::IntSize size = image->GetSize();
|
||||||
|
|
||||||
|
segment.AppendFrame(image.forget(), delta, size);
|
||||||
|
#endif
|
||||||
|
srcStream->AddTrack(TRACK_VIDEO_PRIMARY, 0, new VideoSegment());
|
||||||
|
srcStream->AppendToTrack(TRACK_VIDEO_PRIMARY, &segment);
|
||||||
|
srcStream->FinishAddTracks();
|
||||||
|
srcStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
DOMHwMediaStream::RequestOverlayId()
|
||||||
|
{
|
||||||
|
#ifdef MOZ_WIDGET_GONK
|
||||||
|
return mOverlayImage->GetOverlayId();
|
||||||
|
#else
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DOMHwMediaStream::SetImageSize(uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
#ifdef MOZ_WIDGET_GONK
|
||||||
|
OverlayImage::Data imgData;
|
||||||
|
|
||||||
|
imgData.mOverlayId = mOverlayImage->GetOverlayId();
|
||||||
|
imgData.mSize = IntSize(width, height);
|
||||||
|
mOverlayImage->SetData(imgData);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SourceMediaStream* srcStream = GetStream()->AsSourceStream();
|
||||||
|
StreamBuffer::Track* track = srcStream->FindTrack(TRACK_VIDEO_PRIMARY);
|
||||||
|
|
||||||
|
if (!track || !track->GetSegment()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MOZ_WIDGET_GONK
|
||||||
|
// Clear the old segment.
|
||||||
|
// Changing the existing content of segment is a Very BAD thing, and this way will
|
||||||
|
// confuse consumers of MediaStreams.
|
||||||
|
// It is only acceptable for DOMHwMediaStream
|
||||||
|
// because DOMHwMediaStream doesn't have consumers of TV streams currently.
|
||||||
|
track->GetSegment()->Clear();
|
||||||
|
|
||||||
|
// Change the image size.
|
||||||
|
const StreamTime delta = STREAM_TIME_MAX;
|
||||||
|
nsRefPtr<Image> image = static_cast<Image*>(mOverlayImage.get());
|
||||||
|
mozilla::gfx::IntSize size = image->GetSize();
|
||||||
|
VideoSegment segment;
|
||||||
|
|
||||||
|
segment.AppendFrame(image.forget(), delta, size);
|
||||||
|
srcStream->AppendToTrack(TRACK_VIDEO_PRIMARY, &segment);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
void
|
||||||
|
DOMHwMediaStream::SetOverlayId(int32_t aOverlayId)
|
||||||
|
{
|
||||||
|
#ifdef MOZ_WIDGET_GONK
|
||||||
|
OverlayImage::Data imgData;
|
||||||
|
|
||||||
|
imgData.mOverlayId = aOverlayId;
|
||||||
|
imgData.mSize = mOverlayImage->GetSize();
|
||||||
|
|
||||||
|
mOverlayImage->SetData(imgData);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#ifndef NSDOMMEDIASTREAM_H_
|
#ifndef NSDOMMEDIASTREAM_H_
|
||||||
#define NSDOMMEDIASTREAM_H_
|
#define NSDOMMEDIASTREAM_H_
|
||||||
|
|
||||||
|
#include "ImageContainer.h"
|
||||||
|
|
||||||
#include "nsCycleCollectionParticipant.h"
|
#include "nsCycleCollectionParticipant.h"
|
||||||
#include "nsWrapperCache.h"
|
#include "nsWrapperCache.h"
|
||||||
#include "StreamBuffer.h"
|
#include "StreamBuffer.h"
|
||||||
@ -29,6 +31,7 @@
|
|||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
class DOMHwMediaStream;
|
||||||
class DOMLocalMediaStream;
|
class DOMLocalMediaStream;
|
||||||
class MediaStream;
|
class MediaStream;
|
||||||
class MediaEngineSource;
|
class MediaEngineSource;
|
||||||
@ -47,6 +50,11 @@ class VideoTrackList;
|
|||||||
class MediaTrackListListener;
|
class MediaTrackListListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace layers {
|
||||||
|
class ImageContainer;
|
||||||
|
class OverlayImage;
|
||||||
|
}
|
||||||
|
|
||||||
class MediaStreamDirectListener;
|
class MediaStreamDirectListener;
|
||||||
|
|
||||||
#define NS_DOMMEDIASTREAM_IID \
|
#define NS_DOMMEDIASTREAM_IID \
|
||||||
@ -114,6 +122,7 @@ public:
|
|||||||
virtual void StopTrack(TrackID aTrackID);
|
virtual void StopTrack(TrackID aTrackID);
|
||||||
|
|
||||||
virtual DOMLocalMediaStream* AsDOMLocalMediaStream() { return nullptr; }
|
virtual DOMLocalMediaStream* AsDOMLocalMediaStream() { return nullptr; }
|
||||||
|
virtual DOMHwMediaStream* AsDOMHwMediaStream() { return nullptr; }
|
||||||
|
|
||||||
bool IsFinished();
|
bool IsFinished();
|
||||||
/**
|
/**
|
||||||
@ -375,6 +384,40 @@ private:
|
|||||||
nsRefPtr<AudioNode> mStreamNode;
|
nsRefPtr<AudioNode> mStreamNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DOMHwMediaStream : public DOMLocalMediaStream
|
||||||
|
{
|
||||||
|
typedef mozilla::gfx::IntSize IntSize;
|
||||||
|
typedef layers::ImageContainer ImageContainer;
|
||||||
|
#ifdef MOZ_WIDGET_GONK
|
||||||
|
typedef layers::OverlayImage OverlayImage;
|
||||||
|
typedef layers::OverlayImage::Data Data;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
DOMHwMediaStream();
|
||||||
|
|
||||||
|
static already_AddRefed<DOMHwMediaStream> CreateHwStream(nsIDOMWindow* aWindow);
|
||||||
|
virtual DOMHwMediaStream* AsDOMHwMediaStream() override { return this; }
|
||||||
|
int32_t RequestOverlayId();
|
||||||
|
void SetOverlayId(int32_t aOverlayId);
|
||||||
|
void SetImageSize(uint32_t width, uint32_t height);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
~DOMHwMediaStream();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Init(MediaStream* aStream);
|
||||||
|
|
||||||
|
#ifdef MOZ_WIDGET_GONK
|
||||||
|
nsRefPtr<ImageContainer> mImageContainer;
|
||||||
|
const int DEFAULT_IMAGE_ID = 0x01;
|
||||||
|
const int DEFAULT_IMAGE_WIDTH = 400;
|
||||||
|
const int DEFAULT_IMAGE_HEIGHT = 300;
|
||||||
|
nsRefPtr<OverlayImage> mOverlayImage;
|
||||||
|
Data mImageData;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* NSDOMMEDIASTREAM_H_ */
|
#endif /* NSDOMMEDIASTREAM_H_ */
|
||||||
|
@ -2496,6 +2496,12 @@ SourceMediaStream::FinishAddTracks()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StreamBuffer::Track*
|
||||||
|
SourceMediaStream::FindTrack(TrackID aID)
|
||||||
|
{
|
||||||
|
return mBuffer.FindTrack(aID);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SourceMediaStream::ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment)
|
SourceMediaStream::ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment)
|
||||||
{
|
{
|
||||||
|
@ -801,6 +801,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
void FinishAddTracks();
|
void FinishAddTracks();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find track by track id.
|
||||||
|
*/
|
||||||
|
StreamBuffer::Track* FindTrack(TrackID aID);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append media data to a track. Ownership of aSegment remains with the caller,
|
* Append media data to a track. Ownership of aSegment remains with the caller,
|
||||||
* but aSegment is emptied.
|
* but aSegment is emptied.
|
||||||
|
@ -13,7 +13,7 @@ UNIFIED_SOURCES += [
|
|||||||
'TestMediaPromise.cpp',
|
'TestMediaPromise.cpp',
|
||||||
'TestMP3Demuxer.cpp',
|
'TestMP3Demuxer.cpp',
|
||||||
'TestMP4Demuxer.cpp',
|
'TestMP4Demuxer.cpp',
|
||||||
'TestMP4Reader.cpp',
|
# 'TestMP4Reader.cpp', disabled so we can turn check tests back on (bug 1175752)
|
||||||
'TestTrackEncoder.cpp',
|
'TestTrackEncoder.cpp',
|
||||||
'TestVideoSegment.cpp',
|
'TestVideoSegment.cpp',
|
||||||
'TestWebMBuffered.cpp',
|
'TestWebMBuffered.cpp',
|
||||||
|
@ -55,7 +55,7 @@ already_AddRefed<SpeechSynthesisUtterance>
|
|||||||
SpeechSynthesisUtterance::Constructor(GlobalObject& aGlobal,
|
SpeechSynthesisUtterance::Constructor(GlobalObject& aGlobal,
|
||||||
ErrorResult& aRv)
|
ErrorResult& aRv)
|
||||||
{
|
{
|
||||||
return Constructor(aGlobal, NS_LITERAL_STRING(""), aRv);
|
return Constructor(aGlobal, EmptyString(), aRv);
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<SpeechSynthesisUtterance>
|
already_AddRefed<SpeechSynthesisUtterance>
|
||||||
|
@ -299,7 +299,7 @@ nsSpeechTask::DispatchStartImpl(const nsAString& aUri)
|
|||||||
mUtterance->mState = SpeechSynthesisUtterance::STATE_SPEAKING;
|
mUtterance->mState = SpeechSynthesisUtterance::STATE_SPEAKING;
|
||||||
mUtterance->mChosenVoiceURI = aUri;
|
mUtterance->mChosenVoiceURI = aUri;
|
||||||
mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("start"), 0, 0,
|
mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("start"), 0, 0,
|
||||||
NS_LITERAL_STRING(""));
|
EmptyString());
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@ -370,7 +370,7 @@ nsSpeechTask::DispatchPauseImpl(float aElapsedTime, uint32_t aCharIndex)
|
|||||||
mUtterance->mPaused = true;
|
mUtterance->mPaused = true;
|
||||||
mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("pause"),
|
mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("pause"),
|
||||||
aCharIndex, aElapsedTime,
|
aCharIndex, aElapsedTime,
|
||||||
NS_LITERAL_STRING(""));
|
EmptyString());
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,7 +397,7 @@ nsSpeechTask::DispatchResumeImpl(float aElapsedTime, uint32_t aCharIndex)
|
|||||||
mUtterance->mPaused = false;
|
mUtterance->mPaused = false;
|
||||||
mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("resume"),
|
mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("resume"),
|
||||||
aCharIndex, aElapsedTime,
|
aCharIndex, aElapsedTime,
|
||||||
NS_LITERAL_STRING(""));
|
EmptyString());
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,7 +422,7 @@ nsSpeechTask::DispatchErrorImpl(float aElapsedTime, uint32_t aCharIndex)
|
|||||||
mUtterance->mState = SpeechSynthesisUtterance::STATE_ENDED;
|
mUtterance->mState = SpeechSynthesisUtterance::STATE_ENDED;
|
||||||
mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("error"),
|
mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("error"),
|
||||||
aCharIndex, aElapsedTime,
|
aCharIndex, aElapsedTime,
|
||||||
NS_LITERAL_STRING(""));
|
EmptyString());
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ public:
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult GetPluginWidget(nsIWidget **aWidget) {
|
nsresult GetPluginWidget(nsIWidget **aWidget) const {
|
||||||
NS_IF_ADDREF(*aWidget = mWidget);
|
NS_IF_ADDREF(*aWidget = mWidget);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,10 @@ intr protocol PPluginInstance
|
|||||||
child:
|
child:
|
||||||
intr __delete__();
|
intr __delete__();
|
||||||
|
|
||||||
intr NPP_SetWindow(NPRemoteWindow window);
|
// Return value is only used on Windows and only when the window needs its
|
||||||
|
// parent set to the chrome widget native window.
|
||||||
|
intr NPP_SetWindow(NPRemoteWindow window)
|
||||||
|
returns (NPRemoteWindow childWindowToBeAdopted);
|
||||||
|
|
||||||
intr NPP_GetValue_NPPVpluginWantsAllNetworkStreams()
|
intr NPP_GetValue_NPPVpluginWantsAllNetworkStreams()
|
||||||
returns (bool value, NPError result);
|
returns (bool value, NPError result);
|
||||||
|
@ -1124,7 +1124,8 @@ void PluginInstanceChild::DeleteWindow()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool
|
bool
|
||||||
PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
|
PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow,
|
||||||
|
NPRemoteWindow* aChildWindowToBeAdopted)
|
||||||
{
|
{
|
||||||
PLUGIN_LOG_DEBUG(("%s (aWindow=<window: 0x%lx, x: %d, y: %d, width: %d, height: %d>)",
|
PLUGIN_LOG_DEBUG(("%s (aWindow=<window: 0x%lx, x: %d, y: %d, width: %d, height: %d>)",
|
||||||
FULLFUNCTION,
|
FULLFUNCTION,
|
||||||
@ -1215,9 +1216,32 @@ PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
|
|||||||
if (!CreatePluginWindow())
|
if (!CreatePluginWindow())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ReparentPluginWindow(reinterpret_cast<HWND>(aWindow.window));
|
|
||||||
SizePluginWindow(aWindow.width, aWindow.height);
|
SizePluginWindow(aWindow.width, aWindow.height);
|
||||||
|
|
||||||
|
// If the window is not our parent set the return child window so that
|
||||||
|
// it can be re-parented in the chrome process. Re-parenting now
|
||||||
|
// happens there as we might not have sufficient permission.
|
||||||
|
// Also, this needs to be after SizePluginWindow because SetWindowPos
|
||||||
|
// relies on things that it sets.
|
||||||
|
HWND parentWindow = reinterpret_cast<HWND>(aWindow.window);
|
||||||
|
if (mPluginParentHWND != parentWindow && IsWindow(parentWindow)) {
|
||||||
|
mPluginParentHWND = parentWindow;
|
||||||
|
aChildWindowToBeAdopted->window =
|
||||||
|
reinterpret_cast<uint64_t>(mPluginWindowHWND);
|
||||||
|
} else {
|
||||||
|
// Now we know that the window has the correct parent we can show
|
||||||
|
// it. The actual visibility is controlled by its parent.
|
||||||
|
// First time round, these calls are made by our caller after the
|
||||||
|
// parent is set.
|
||||||
|
ShowWindow(mPluginWindowHWND, SW_SHOWNA);
|
||||||
|
|
||||||
|
// This used to be called in SizePluginWindow, but we need to make
|
||||||
|
// sure that mPluginWindowHWND has had it's parent set correctly,
|
||||||
|
// otherwise it can cause a focus issue.
|
||||||
|
SetWindowPos(mPluginWindowHWND, nullptr, 0, 0, aWindow.width,
|
||||||
|
aWindow.height, SWP_NOZORDER | SWP_NOREPOSITION);
|
||||||
|
}
|
||||||
|
|
||||||
mWindow.window = (void*)mPluginWindowHWND;
|
mWindow.window = (void*)mPluginWindowHWND;
|
||||||
mWindow.x = aWindow.x;
|
mWindow.x = aWindow.x;
|
||||||
mWindow.y = aWindow.y;
|
mWindow.y = aWindow.y;
|
||||||
@ -1405,25 +1429,6 @@ PluginInstanceChild::DestroyPluginWindow()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
PluginInstanceChild::ReparentPluginWindow(HWND hWndParent)
|
|
||||||
{
|
|
||||||
if (hWndParent != mPluginParentHWND && IsWindow(hWndParent)) {
|
|
||||||
// Fix the child window's style to be a child window.
|
|
||||||
LONG_PTR style = GetWindowLongPtr(mPluginWindowHWND, GWL_STYLE);
|
|
||||||
style |= WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
|
|
||||||
style &= ~WS_POPUP;
|
|
||||||
SetWindowLongPtr(mPluginWindowHWND, GWL_STYLE, style);
|
|
||||||
|
|
||||||
// Do the reparenting.
|
|
||||||
SetParent(mPluginWindowHWND, hWndParent);
|
|
||||||
|
|
||||||
// Make sure we're visible.
|
|
||||||
ShowWindow(mPluginWindowHWND, SW_SHOWNA);
|
|
||||||
}
|
|
||||||
mPluginParentHWND = hWndParent;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
PluginInstanceChild::SizePluginWindow(int width,
|
PluginInstanceChild::SizePluginWindow(int width,
|
||||||
int height)
|
int height)
|
||||||
@ -1431,8 +1436,6 @@ PluginInstanceChild::SizePluginWindow(int width,
|
|||||||
if (mPluginWindowHWND) {
|
if (mPluginWindowHWND) {
|
||||||
mPluginSize.x = width;
|
mPluginSize.x = width;
|
||||||
mPluginSize.y = height;
|
mPluginSize.y = height;
|
||||||
SetWindowPos(mPluginWindowHWND, nullptr, 0, 0, width, height,
|
|
||||||
SWP_NOZORDER | SWP_NOREPOSITION);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,8 @@ class PluginInstanceChild : public PPluginInstanceChild
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool AnswerNPP_SetWindow(const NPRemoteWindow& window) override;
|
bool AnswerNPP_SetWindow(const NPRemoteWindow& window,
|
||||||
|
NPRemoteWindow* aChildWindowToBeAdopted) override;
|
||||||
|
|
||||||
virtual bool
|
virtual bool
|
||||||
AnswerNPP_GetValue_NPPVpluginWantsAllNetworkStreams(bool* wantsAllStreams, NPError* rv) override;
|
AnswerNPP_GetValue_NPPVpluginWantsAllNetworkStreams(bool* wantsAllStreams, NPError* rv) override;
|
||||||
@ -278,7 +279,6 @@ private:
|
|||||||
static bool RegisterWindowClass();
|
static bool RegisterWindowClass();
|
||||||
bool CreatePluginWindow();
|
bool CreatePluginWindow();
|
||||||
void DestroyPluginWindow();
|
void DestroyPluginWindow();
|
||||||
void ReparentPluginWindow(HWND hWndParent);
|
|
||||||
void SizePluginWindow(int width, int height);
|
void SizePluginWindow(int width, int height);
|
||||||
int16_t WinlessHandleEvent(NPEvent& event);
|
int16_t WinlessHandleEvent(NPEvent& event);
|
||||||
void CreateWinlessPopupSurrogate();
|
void CreateWinlessPopupSurrogate();
|
||||||
|
@ -49,6 +49,8 @@
|
|||||||
#include "mozilla/plugins/PluginSurfaceParent.h"
|
#include "mozilla/plugins/PluginSurfaceParent.h"
|
||||||
#include "nsClassHashtable.h"
|
#include "nsClassHashtable.h"
|
||||||
#include "nsHashKeys.h"
|
#include "nsHashKeys.h"
|
||||||
|
#include "nsIWidget.h"
|
||||||
|
#include "nsPluginNativeWindow.h"
|
||||||
extern const wchar_t* kFlashFullscreenClass;
|
extern const wchar_t* kFlashFullscreenClass;
|
||||||
#elif defined(MOZ_WIDGET_GTK)
|
#elif defined(MOZ_WIDGET_GTK)
|
||||||
#include <gdk/gdk.h>
|
#include <gdk/gdk.h>
|
||||||
@ -1025,8 +1027,30 @@ PluginInstanceParent::NPP_SetWindow(const NPWindow* aWindow)
|
|||||||
window.colormap = ws_info->colormap;
|
window.colormap = ws_info->colormap;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!CallNPP_SetWindow(window))
|
NPRemoteWindow childWindow;
|
||||||
|
if (!CallNPP_SetWindow(window, &childWindow)) {
|
||||||
return NPERR_GENERIC_ERROR;
|
return NPERR_GENERIC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(XP_WIN)
|
||||||
|
// If a child window is returned it means that we need to re-parent it.
|
||||||
|
if (childWindow.window) {
|
||||||
|
nsCOMPtr<nsIWidget> widget;
|
||||||
|
static_cast<const nsPluginNativeWindow*>(aWindow)->
|
||||||
|
GetPluginWidget(getter_AddRefs(widget));
|
||||||
|
if (widget) {
|
||||||
|
widget->SetNativeData(NS_NATIVE_CHILD_WINDOW,
|
||||||
|
static_cast<uintptr_t>(childWindow.window));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now it has got the correct parent, make sure it is visible.
|
||||||
|
// In subsequent calls to SetWindow these calls happen in the Child.
|
||||||
|
HWND childHWND = reinterpret_cast<HWND>(childWindow.window);
|
||||||
|
ShowWindow(childHWND, SW_SHOWNA);
|
||||||
|
SetWindowPos(childHWND, nullptr, 0, 0, window.width, window.height,
|
||||||
|
SWP_NOZORDER | SWP_NOREPOSITION);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return NPERR_NO_ERROR;
|
return NPERR_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -96,17 +96,17 @@ AddSandboxAllowedFiles(int32_t aSandboxLevel,
|
|||||||
|
|
||||||
// Level 2 and above is now using low integrity, so we need to give write
|
// Level 2 and above is now using low integrity, so we need to give write
|
||||||
// access to the Flash directories.
|
// access to the Flash directories.
|
||||||
|
// This should be made Flash specific (Bug 1171396).
|
||||||
AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR,
|
AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR,
|
||||||
NS_LITERAL_STRING("\\Macromedia\\Flash Player\\*"));
|
NS_LITERAL_STRING("\\Macromedia\\Flash Player\\*"));
|
||||||
AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR,
|
AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR,
|
||||||
NS_LITERAL_STRING("\\Adobe\\Flash Player\\*"));
|
NS_LITERAL_STRING("\\Adobe\\Flash Player\\*"));
|
||||||
|
|
||||||
#if defined(_X86_)
|
// Write access to the Temp directory is used to turn off protected mode
|
||||||
// Write access to the Temp directory should only be needed for 32-bit as
|
// and is needed in some mochitest crash tests.
|
||||||
// it is used to turn off protected mode, which only applies to x86.
|
// Bug 1171393 tracks removing this requirement.
|
||||||
AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_OS_TEMP_DIR,
|
AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_OS_TEMP_DIR,
|
||||||
NS_LITERAL_STRING("\\*"));
|
NS_LITERAL_STRING("\\*"));
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -210,5 +210,20 @@ PluginWidgetParent::RecvGetNativePluginPort(uintptr_t* value)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PluginWidgetParent::RecvSetNativeChildWindow(const uintptr_t& aChildWindow)
|
||||||
|
{
|
||||||
|
#if defined(XP_WIN)
|
||||||
|
ENSURE_CHANNEL;
|
||||||
|
PWLOG("PluginWidgetParent::RecvSetNativeChildWindow(%p)\n",
|
||||||
|
static_cast<void*>(aChildWindow));
|
||||||
|
mWidget->SetNativeData(NS_NATIVE_CHILD_WINDOW, aChildWindow);
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
NS_NOTREACHED("PluginWidgetParent::RecvSetNativeChildWindow not implemented!");
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace plugins
|
} // namespace plugins
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
@ -31,6 +31,7 @@ public:
|
|||||||
virtual bool RecvCreate(nsresult* aResult) override;
|
virtual bool RecvCreate(nsresult* aResult) override;
|
||||||
virtual bool RecvSetFocus(const bool& aRaise) override;
|
virtual bool RecvSetFocus(const bool& aRaise) override;
|
||||||
virtual bool RecvGetNativePluginPort(uintptr_t* value) override;
|
virtual bool RecvGetNativePluginPort(uintptr_t* value) override;
|
||||||
|
bool RecvSetNativeChildWindow(const uintptr_t& aChildWindow) override;
|
||||||
|
|
||||||
// Helper for compositor checks on the channel
|
// Helper for compositor checks on the channel
|
||||||
bool ActorDestroyed() { return !mWidget; }
|
bool ActorDestroyed() { return !mWidget; }
|
||||||
|
@ -197,8 +197,10 @@ TVTuner::GetStream() const
|
|||||||
nsresult
|
nsresult
|
||||||
TVTuner::InitMediaStream()
|
TVTuner::InitMediaStream()
|
||||||
{
|
{
|
||||||
// TODO Instantiate |mStream| when bug 987498 is done.
|
nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(GetOwner());
|
||||||
|
nsRefPtr<DOMHwMediaStream> stream = DOMHwMediaStream::CreateHwStream(window);
|
||||||
|
|
||||||
|
mStream = stream.forget();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,5 +220,17 @@ TVTuner::DispatchCurrentSourceChangedEvent(TVSource* aSource)
|
|||||||
return NS_DispatchToCurrentThread(runnable);
|
return NS_DispatchToCurrentThread(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
TVTuner::NotifyImageSizeChanged(uint32_t aWidth, uint32_t aHeight)
|
||||||
|
{
|
||||||
|
DOMHwMediaStream* hwMediaStream = mStream->AsDOMHwMediaStream();
|
||||||
|
|
||||||
|
if (hwMediaStream) {
|
||||||
|
hwMediaStream->SetImageSize(aWidth, aHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
@ -31,6 +31,7 @@ public:
|
|||||||
|
|
||||||
static already_AddRefed<TVTuner> Create(nsPIDOMWindow* aWindow,
|
static already_AddRefed<TVTuner> Create(nsPIDOMWindow* aWindow,
|
||||||
nsITVTunerData* aData);
|
nsITVTunerData* aData);
|
||||||
|
nsresult NotifyImageSizeChanged(uint32_t aWidth, uint32_t aHeight);
|
||||||
|
|
||||||
// WebIDL (internal functions)
|
// WebIDL (internal functions)
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ BufferRecycleBin::GetBuffer(uint32_t aSize)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageContainer::ImageContainer(int flag)
|
ImageContainer::ImageContainer(ImageContainer::Mode flag)
|
||||||
: mReentrantMonitor("ImageContainer.mReentrantMonitor"),
|
: mReentrantMonitor("ImageContainer.mReentrantMonitor"),
|
||||||
mPaintCount(0),
|
mPaintCount(0),
|
||||||
mPreviousImagePainted(false),
|
mPreviousImagePainted(false),
|
||||||
@ -147,11 +147,24 @@ ImageContainer::ImageContainer(int flag)
|
|||||||
mCompositionNotifySink(nullptr),
|
mCompositionNotifySink(nullptr),
|
||||||
mImageClient(nullptr)
|
mImageClient(nullptr)
|
||||||
{
|
{
|
||||||
if (flag == ENABLE_ASYNC && ImageBridgeChild::IsCreated()) {
|
if (ImageBridgeChild::IsCreated()) {
|
||||||
// the refcount of this ImageClient is 1. we don't use a RefPtr here because the refcount
|
// the refcount of this ImageClient is 1. we don't use a RefPtr here because the refcount
|
||||||
// of this class must be done on the ImageBridge thread.
|
// of this class must be done on the ImageBridge thread.
|
||||||
mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE).take();
|
switch(flag) {
|
||||||
MOZ_ASSERT(mImageClient);
|
case SYNCHRONOUS:
|
||||||
|
break;
|
||||||
|
case ASYNCHRONOUS:
|
||||||
|
mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE).take();
|
||||||
|
MOZ_ASSERT(mImageClient);
|
||||||
|
break;
|
||||||
|
case ASYNCHRONOUS_OVERLAY:
|
||||||
|
mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE_OVERLAY).take();
|
||||||
|
MOZ_ASSERT(mImageClient);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_ASSERT(false, "This flag is invalid.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,9 +291,9 @@ class ImageContainer final : public SupportsWeakPtr<ImageContainer> {
|
|||||||
public:
|
public:
|
||||||
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ImageContainer)
|
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ImageContainer)
|
||||||
|
|
||||||
enum { DISABLE_ASYNC = 0x0, ENABLE_ASYNC = 0x01 };
|
enum Mode { SYNCHRONOUS = 0x0, ASYNCHRONOUS = 0x01, ASYNCHRONOUS_OVERLAY = 0x02 };
|
||||||
|
|
||||||
explicit ImageContainer(int flag = 0);
|
explicit ImageContainer(ImageContainer::Mode flag = SYNCHRONOUS);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an Image in one of the given formats.
|
* Create an Image in one of the given formats.
|
||||||
|
@ -167,16 +167,9 @@ LayerManager::Mutated(Layer* aLayer)
|
|||||||
#endif // DEBUG
|
#endif // DEBUG
|
||||||
|
|
||||||
already_AddRefed<ImageContainer>
|
already_AddRefed<ImageContainer>
|
||||||
LayerManager::CreateImageContainer()
|
LayerManager::CreateImageContainer(ImageContainer::Mode flag)
|
||||||
{
|
{
|
||||||
nsRefPtr<ImageContainer> container = new ImageContainer(ImageContainer::DISABLE_ASYNC);
|
nsRefPtr<ImageContainer> container = new ImageContainer(flag);
|
||||||
return container.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
already_AddRefed<ImageContainer>
|
|
||||||
LayerManager::CreateAsynchronousImageContainer()
|
|
||||||
{
|
|
||||||
nsRefPtr<ImageContainer> container = new ImageContainer(ImageContainer::ENABLE_ASYNC);
|
|
||||||
return container.forget();
|
return container.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include "mozilla/Logging.h" // for PRLogModuleInfo
|
#include "mozilla/Logging.h" // for PRLogModuleInfo
|
||||||
#include "nsIWidget.h" // For plugin window configuration information structs
|
#include "nsIWidget.h" // For plugin window configuration information structs
|
||||||
#include "gfxVR.h"
|
#include "gfxVR.h"
|
||||||
|
#include "ImageContainer.h"
|
||||||
|
|
||||||
class gfxContext;
|
class gfxContext;
|
||||||
|
|
||||||
@ -81,7 +82,6 @@ class PaintedLayer;
|
|||||||
class ContainerLayer;
|
class ContainerLayer;
|
||||||
class ImageLayer;
|
class ImageLayer;
|
||||||
class ColorLayer;
|
class ColorLayer;
|
||||||
class ImageContainer;
|
|
||||||
class CanvasLayer;
|
class CanvasLayer;
|
||||||
class ReadbackLayer;
|
class ReadbackLayer;
|
||||||
class ReadbackProcessor;
|
class ReadbackProcessor;
|
||||||
@ -435,19 +435,12 @@ public:
|
|||||||
* Can be called anytime, from any thread.
|
* Can be called anytime, from any thread.
|
||||||
*
|
*
|
||||||
* Creates an Image container which forwards its images to the compositor within
|
* Creates an Image container which forwards its images to the compositor within
|
||||||
* layer transactions on the main thread.
|
* layer transactions on the main thread or asynchronously using the ImageBridge IPDL protocol.
|
||||||
|
* In the case of asynchronous, If the protocol is not available, the returned ImageContainer
|
||||||
|
* will forward images within layer transactions.
|
||||||
*/
|
*/
|
||||||
static already_AddRefed<ImageContainer> CreateImageContainer();
|
static already_AddRefed<ImageContainer> CreateImageContainer(ImageContainer::Mode flag
|
||||||
|
= ImageContainer::SYNCHRONOUS);
|
||||||
/**
|
|
||||||
* Can be called anytime, from any thread.
|
|
||||||
*
|
|
||||||
* Tries to create an Image container which forwards its images to the compositor
|
|
||||||
* asynchronously using the ImageBridge IPDL protocol. If the protocol is not
|
|
||||||
* available, the returned ImageContainer will forward images within layer
|
|
||||||
* transactions, just like if it was created with CreateImageContainer().
|
|
||||||
*/
|
|
||||||
static already_AddRefed<ImageContainer> CreateAsynchronousImageContainer();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of layer manager his is. This is to be used sparsely in order to
|
* Type of layer manager his is. This is to be used sparsely in order to
|
||||||
|
@ -363,6 +363,16 @@ ImageHostOverlay::UseOverlaySource(OverlaySource aOverlay)
|
|||||||
mOverlay = aOverlay;
|
mOverlay = aOverlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IntSize
|
||||||
|
ImageHostOverlay::GetImageSize() const
|
||||||
|
{
|
||||||
|
if (mHasPictureRect) {
|
||||||
|
return IntSize(mPictureRect.width, mPictureRect.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IntSize();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ImageHostOverlay::PrintInfo(std::stringstream& aStream, const char* aPrefix)
|
ImageHostOverlay::PrintInfo(std::stringstream& aStream, const char* aPrefix)
|
||||||
{
|
{
|
||||||
|
@ -112,7 +112,8 @@ public:
|
|||||||
const nsIntRegion* aVisibleRegion = nullptr) override;
|
const nsIntRegion* aVisibleRegion = nullptr) override;
|
||||||
virtual LayerRenderState GetRenderState() override;
|
virtual LayerRenderState GetRenderState() override;
|
||||||
virtual void UseOverlaySource(OverlaySource aOverlay) override;
|
virtual void UseOverlaySource(OverlaySource aOverlay) override;
|
||||||
virtual void SetPictureRect(const gfx::IntRect& aPictureRect) override
|
virtual gfx::IntSize GetImageSize() const override;
|
||||||
|
virtual void SetPictureRect(const nsIntRect& aPictureRect) override
|
||||||
{
|
{
|
||||||
mPictureRect = aPictureRect;
|
mPictureRect = aPictureRect;
|
||||||
mHasPictureRect = true;
|
mHasPictureRect = true;
|
||||||
|
@ -262,7 +262,7 @@ CompositorChild::RecvUpdatePluginConfigurations(const nsIntPoint& aContentOffset
|
|||||||
|
|
||||||
// Tracks visible plugins we update, so we can hide any plugins we don't.
|
// Tracks visible plugins we update, so we can hide any plugins we don't.
|
||||||
nsTArray<uintptr_t> visiblePluginIds;
|
nsTArray<uintptr_t> visiblePluginIds;
|
||||||
|
nsIWidget* parent = nullptr;
|
||||||
for (uint32_t pluginsIdx = 0; pluginsIdx < aPlugins.Length(); pluginsIdx++) {
|
for (uint32_t pluginsIdx = 0; pluginsIdx < aPlugins.Length(); pluginsIdx++) {
|
||||||
nsIWidget* widget =
|
nsIWidget* widget =
|
||||||
nsIWidget::LookupRegisteredPluginWindow(aPlugins[pluginsIdx].windowId());
|
nsIWidget::LookupRegisteredPluginWindow(aPlugins[pluginsIdx].windowId());
|
||||||
@ -270,6 +270,9 @@ CompositorChild::RecvUpdatePluginConfigurations(const nsIntPoint& aContentOffset
|
|||||||
NS_WARNING("Unexpected, plugin id not found!");
|
NS_WARNING("Unexpected, plugin id not found!");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!parent) {
|
||||||
|
parent = widget->GetParent();
|
||||||
|
}
|
||||||
bool isVisible = aPlugins[pluginsIdx].visible();
|
bool isVisible = aPlugins[pluginsIdx].visible();
|
||||||
if (widget && !widget->Destroyed()) {
|
if (widget && !widget->Destroyed()) {
|
||||||
gfx::IntRect bounds;
|
gfx::IntRect bounds;
|
||||||
@ -321,13 +324,14 @@ CompositorChild::RecvUpdatePluginConfigurations(const nsIntPoint& aContentOffset
|
|||||||
}
|
}
|
||||||
// Any plugins we didn't update need to be hidden, as they are
|
// Any plugins we didn't update need to be hidden, as they are
|
||||||
// not associated with visible content.
|
// not associated with visible content.
|
||||||
nsIWidget::UpdateRegisteredPluginWindowVisibility(visiblePluginIds);
|
nsIWidget::UpdateRegisteredPluginWindowVisibility((uintptr_t)parent, visiblePluginIds);
|
||||||
return true;
|
return true;
|
||||||
#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
|
#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CompositorChild::RecvUpdatePluginVisibility(nsTArray<uintptr_t>&& aVisibleIdList)
|
CompositorChild::RecvUpdatePluginVisibility(const uintptr_t& aOwnerWidget,
|
||||||
|
nsTArray<uintptr_t>&& aVisibleIdList)
|
||||||
{
|
{
|
||||||
#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
|
#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
|
||||||
NS_NOTREACHED("CompositorChild::RecvUpdatePluginVisibility calls "
|
NS_NOTREACHED("CompositorChild::RecvUpdatePluginVisibility calls "
|
||||||
@ -335,7 +339,7 @@ CompositorChild::RecvUpdatePluginVisibility(nsTArray<uintptr_t>&& aVisibleIdList
|
|||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
nsIWidget::UpdateRegisteredPluginWindowVisibility(aVisibleIdList);
|
nsIWidget::UpdateRegisteredPluginWindowVisibility(aOwnerWidget, aVisibleIdList);
|
||||||
return true;
|
return true;
|
||||||
#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
|
#endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,8 @@ public:
|
|||||||
nsTArray<PluginWindowData>&& aPlugins) override;
|
nsTArray<PluginWindowData>&& aPlugins) override;
|
||||||
|
|
||||||
virtual bool
|
virtual bool
|
||||||
RecvUpdatePluginVisibility(nsTArray<uintptr_t>&& aWindowList) override;
|
RecvUpdatePluginVisibility(const uintptr_t& aOwnerWidget,
|
||||||
|
nsTArray<uintptr_t>&& aWindowList) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request that the parent tell us when graphics are ready on GPU.
|
* Request that the parent tell us when graphics are ready on GPU.
|
||||||
|
@ -1440,12 +1440,9 @@ CompositorParent::AllocPLayerTransactionParent(const nsTArray<LayersBackend>& aB
|
|||||||
{
|
{
|
||||||
MOZ_ASSERT(aId == 0);
|
MOZ_ASSERT(aId == 0);
|
||||||
|
|
||||||
// mWidget doesn't belong to the compositor thread, so it should be set to
|
|
||||||
// nullptr before returning from this method, to avoid accessing it elsewhere.
|
|
||||||
gfx::IntRect rect;
|
gfx::IntRect rect;
|
||||||
mWidget->GetClientBounds(rect);
|
mWidget->GetClientBounds(rect);
|
||||||
InitializeLayerManager(aBackendHints);
|
InitializeLayerManager(aBackendHints);
|
||||||
mWidget = nullptr;
|
|
||||||
|
|
||||||
if (!mLayerManager) {
|
if (!mLayerManager) {
|
||||||
NS_WARNING("Failed to initialise Compositor");
|
NS_WARNING("Failed to initialise Compositor");
|
||||||
@ -2037,7 +2034,9 @@ UpdatePluginWindowState(uint64_t aId)
|
|||||||
// to do here is hide the plugins for the old tree, so don't waste time
|
// to do here is hide the plugins for the old tree, so don't waste time
|
||||||
// calculating clipping.
|
// calculating clipping.
|
||||||
nsTArray<uintptr_t> aVisibleIdList;
|
nsTArray<uintptr_t> aVisibleIdList;
|
||||||
unused << lts.mParent->SendUpdatePluginVisibility(aVisibleIdList);
|
uintptr_t parentWidget = (uintptr_t)lts.mParent->GetWidget();
|
||||||
|
unused << lts.mParent->SendUpdatePluginVisibility(parentWidget,
|
||||||
|
aVisibleIdList);
|
||||||
lts.mUpdatedPluginDataAvailable = false;
|
lts.mUpdatedPluginDataAvailable = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -424,6 +424,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
static bool IsInCompositorThread();
|
static bool IsInCompositorThread();
|
||||||
|
|
||||||
|
nsIWidget* GetWidget() { return mWidget; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Protected destructor, to discourage deletion outside of Release():
|
// Protected destructor, to discourage deletion outside of Release():
|
||||||
virtual ~CompositorParent();
|
virtual ~CompositorParent();
|
||||||
|
@ -69,7 +69,8 @@ child:
|
|||||||
* Sets the list of currently visible plugin windows based on a
|
* Sets the list of currently visible plugin windows based on a
|
||||||
* list of plugin window ids.
|
* list of plugin window ids.
|
||||||
*/
|
*/
|
||||||
async UpdatePluginVisibility(uintptr_t[] aVisibleIdList);
|
async UpdatePluginVisibility(uintptr_t aOwnerWidget,
|
||||||
|
uintptr_t[] aVisibleIdList);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Drop any buffers that might be retained on the child compositor
|
* Drop any buffers that might be retained on the child compositor
|
||||||
|
@ -670,10 +670,6 @@ ShadowLayerForwarder::EndTransaction(InfallibleTArray<EditReply>* aReplies,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear any cached plugin data we might have, now that the
|
|
||||||
// transaction is complete.
|
|
||||||
mPluginWindowData.Clear();
|
|
||||||
|
|
||||||
*aSent = true;
|
*aSent = true;
|
||||||
mIsFirstPaint = false;
|
mIsFirstPaint = false;
|
||||||
MOZ_LAYERS_LOG(("[LayersForwarder] ... done"));
|
MOZ_LAYERS_LOG(("[LayersForwarder] ... done"));
|
||||||
|
@ -514,7 +514,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
mPreviousTimestamp = TimeStamp::Now();
|
mPreviousTimestamp = TimeStamp::Now();
|
||||||
mStartingVsync = true;
|
|
||||||
if (CVDisplayLinkStart(mDisplayLink) != kCVReturnSuccess) {
|
if (CVDisplayLinkStart(mDisplayLink) != kCVReturnSuccess) {
|
||||||
NS_WARNING("Could not activate the display link");
|
NS_WARNING("Could not activate the display link");
|
||||||
CVDisplayLinkRelease(mDisplayLink);
|
CVDisplayLinkRelease(mDisplayLink);
|
||||||
@ -548,7 +547,6 @@ public:
|
|||||||
// Normalize the timestamps given to the VsyncDispatchers to the vsync
|
// Normalize the timestamps given to the VsyncDispatchers to the vsync
|
||||||
// that just occured, not the vsync that is upcoming.
|
// that just occured, not the vsync that is upcoming.
|
||||||
TimeStamp mPreviousTimestamp;
|
TimeStamp mPreviousTimestamp;
|
||||||
bool mStartingVsync;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Manages the display link render thread
|
// Manages the display link render thread
|
||||||
@ -574,30 +572,26 @@ static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink,
|
|||||||
// Executed on OS X hardware vsync thread
|
// Executed on OS X hardware vsync thread
|
||||||
OSXVsyncSource::OSXDisplay* display = (OSXVsyncSource::OSXDisplay*) aDisplayLinkContext;
|
OSXVsyncSource::OSXDisplay* display = (OSXVsyncSource::OSXDisplay*) aDisplayLinkContext;
|
||||||
int64_t nextVsyncTimestamp = aOutputTime->hostTime;
|
int64_t nextVsyncTimestamp = aOutputTime->hostTime;
|
||||||
|
|
||||||
mozilla::TimeStamp nextVsync = mozilla::TimeStamp::FromSystemTime(nextVsyncTimestamp);
|
mozilla::TimeStamp nextVsync = mozilla::TimeStamp::FromSystemTime(nextVsyncTimestamp);
|
||||||
|
|
||||||
mozilla::TimeStamp previousVsync = display->mPreviousTimestamp;
|
mozilla::TimeStamp previousVsync = display->mPreviousTimestamp;
|
||||||
bool firstVsync = display->mStartingVsync;
|
|
||||||
|
|
||||||
display->mStartingVsync = false;
|
|
||||||
display->mPreviousTimestamp = nextVsync;
|
|
||||||
mozilla::TimeStamp now = TimeStamp::Now();
|
mozilla::TimeStamp now = TimeStamp::Now();
|
||||||
if (nextVsync <= previousVsync) {
|
|
||||||
TimeDuration next = nextVsync - now;
|
|
||||||
TimeDuration prev = now - previousVsync;
|
|
||||||
printf_stderr("Next from now: %f, prev from now: %f, first vsync %d\n",
|
|
||||||
next.ToMilliseconds(), prev.ToMilliseconds(), firstVsync);
|
|
||||||
MOZ_ASSERT(false, "Next vsync less than previous vsync\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bug 1158321 - The VsyncCallback can sometimes execute before the reported
|
// Snow leopard sometimes sends vsync timestamps very far in the past.
|
||||||
// vsync time. In those cases, normalize the timestamp to Now() as sending
|
// Normalize the vsync timestamps to now.
|
||||||
// timestamps in the future has undefined behavior. See the comment above
|
if (nextVsync <= previousVsync) {
|
||||||
// OSXDisplay::mPreviousTimestamp
|
nextVsync = now;
|
||||||
if (now < previousVsync) {
|
previousVsync = now;
|
||||||
|
} else if (now < previousVsync) {
|
||||||
|
// Bug 1158321 - The VsyncCallback can sometimes execute before the reported
|
||||||
|
// vsync time. In those cases, normalize the timestamp to Now() as sending
|
||||||
|
// timestamps in the future has undefined behavior. See the comment above
|
||||||
|
// OSXDisplay::mPreviousTimestamp
|
||||||
previousVsync = now;
|
previousVsync = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
display->mPreviousTimestamp = nextVsync;
|
||||||
|
|
||||||
display->NotifyVsync(previousVsync);
|
display->NotifyVsync(previousVsync);
|
||||||
return kCVReturnSuccess;
|
return kCVReturnSuccess;
|
||||||
}
|
}
|
||||||
|
@ -22,13 +22,13 @@ public:
|
|||||||
NS_IMETHOD Convert(const char* aSrc,
|
NS_IMETHOD Convert(const char* aSrc,
|
||||||
int32_t* aSrcLength,
|
int32_t* aSrcLength,
|
||||||
char16_t* aDest,
|
char16_t* aDest,
|
||||||
int32_t* aDestLength);
|
int32_t* aDestLength) override;
|
||||||
|
|
||||||
MOZ_WARN_UNUSED_RESULT NS_IMETHOD GetMaxLength(const char* aSrc,
|
MOZ_WARN_UNUSED_RESULT NS_IMETHOD GetMaxLength(const char* aSrc,
|
||||||
int32_t aSrcLength,
|
int32_t aSrcLength,
|
||||||
int32_t* aDestLength) override;
|
int32_t* aDestLength) override;
|
||||||
|
|
||||||
NS_IMETHOD Reset();
|
NS_IMETHOD Reset() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool mSeenByte;
|
bool mSeenByte;
|
||||||
|
@ -57,12 +57,12 @@ protected:
|
|||||||
// Subclassing of nsBasicDecoderSupport class [declaration]
|
// Subclassing of nsBasicDecoderSupport class [declaration]
|
||||||
|
|
||||||
NS_IMETHOD Convert(const char * aSrc, int32_t * aSrcLength,
|
NS_IMETHOD Convert(const char * aSrc, int32_t * aSrcLength,
|
||||||
char16_t * aDest, int32_t * aDestLength);
|
char16_t * aDest, int32_t * aDestLength) override;
|
||||||
|
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
// Subclassing of nsBasicDecoderSupport class [declaration]
|
// Subclassing of nsBasicDecoderSupport class [declaration]
|
||||||
|
|
||||||
NS_IMETHOD Reset();
|
NS_IMETHOD Reset() override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ public:
|
|||||||
MOZ_WARN_UNUSED_RESULT NS_IMETHOD GetMaxLength(const char* aSrc,
|
MOZ_WARN_UNUSED_RESULT NS_IMETHOD GetMaxLength(const char* aSrc,
|
||||||
int32_t aSrcLength,
|
int32_t aSrcLength,
|
||||||
int32_t* aDestLength) override;
|
int32_t* aDestLength) override;
|
||||||
NS_IMETHOD Reset();
|
NS_IMETHOD Reset() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint8_t mState;
|
uint8_t mState;
|
||||||
|
@ -285,8 +285,8 @@ class GarbageCollectionEvent
|
|||||||
// Represents a single slice of a possibly multi-slice incremental garbage
|
// Represents a single slice of a possibly multi-slice incremental garbage
|
||||||
// collection.
|
// collection.
|
||||||
struct Collection {
|
struct Collection {
|
||||||
int64_t startTimestamp;
|
double startTimestamp;
|
||||||
int64_t endTimestamp;
|
double endTimestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The set of garbage collection slices that made up this GC cycle.
|
// The set of garbage collection slices that made up this GC cycle.
|
||||||
|
@ -212,6 +212,18 @@ TryEvalJSON(JSContext* cx, JSLinearString* str, MutableHandleValue rval)
|
|||||||
: ParseEvalStringAsJSON(cx, linearChars.twoByteRange(), rval);
|
: ParseEvalStringAsJSON(cx, linearChars.twoByteRange(), rval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
HasPollutedScopeChain(JSObject* scopeChain)
|
||||||
|
{
|
||||||
|
while (scopeChain) {
|
||||||
|
if (scopeChain->is<DynamicWithObject>())
|
||||||
|
return true;
|
||||||
|
scopeChain = scopeChain->enclosingScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Define subset of ExecuteType so that casting performs the injection.
|
// Define subset of ExecuteType so that casting performs the injection.
|
||||||
enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL };
|
enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL };
|
||||||
|
|
||||||
@ -238,6 +250,12 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (evalType == DIRECT_EVAL && caller.script()->isDerivedClassConstructor()) {
|
||||||
|
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DISABLED_DERIVED_CLASS,
|
||||||
|
"direct eval");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// ES5 15.1.2.1 step 1.
|
// ES5 15.1.2.1 step 1.
|
||||||
if (args.length() < 1) {
|
if (args.length() < 1) {
|
||||||
args.rval().setUndefined();
|
args.rval().setUndefined();
|
||||||
@ -314,8 +332,13 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame
|
|||||||
if (!staticScope)
|
if (!staticScope)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
bool hasPollutedGlobalScope =
|
||||||
|
HasPollutedScopeChain(scopeobj) ||
|
||||||
|
(evalType == DIRECT_EVAL && callerScript->hasPollutedGlobalScope());
|
||||||
|
|
||||||
CompileOptions options(cx);
|
CompileOptions options(cx);
|
||||||
options.setFileAndLine(filename, 1)
|
options.setFileAndLine(filename, 1)
|
||||||
|
.setHasPollutedScope(hasPollutedGlobalScope)
|
||||||
.setIsRunOnce(true)
|
.setIsRunOnce(true)
|
||||||
.setForEval(true)
|
.setForEval(true)
|
||||||
.setNoScriptRval(false)
|
.setNoScriptRval(false)
|
||||||
@ -333,7 +356,7 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame
|
|||||||
: SourceBufferHolder::NoOwnership;
|
: SourceBufferHolder::NoOwnership;
|
||||||
SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
|
SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
|
||||||
JSScript* compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
|
JSScript* compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
|
||||||
scopeobj, staticScope, callerScript,
|
scopeobj, callerScript, staticScope,
|
||||||
options, srcBuf, linearStr, staticLevel);
|
options, srcBuf, linearStr, staticLevel);
|
||||||
if (!compiled)
|
if (!compiled)
|
||||||
return false;
|
return false;
|
||||||
@ -387,7 +410,7 @@ js::DirectEvalStringFromIon(JSContext* cx,
|
|||||||
bool mutedErrors;
|
bool mutedErrors;
|
||||||
uint32_t pcOffset;
|
uint32_t pcOffset;
|
||||||
DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
|
DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
|
||||||
&mutedErrors, CALLED_FROM_JSOP_EVAL);
|
&mutedErrors, CALLED_FROM_JSOP_EVAL);
|
||||||
|
|
||||||
const char* introducerFilename = filename;
|
const char* introducerFilename = filename;
|
||||||
if (maybeScript && maybeScript->scriptSource()->introducerFilename())
|
if (maybeScript && maybeScript->scriptSource()->introducerFilename())
|
||||||
@ -400,6 +423,8 @@ js::DirectEvalStringFromIon(JSContext* cx,
|
|||||||
|
|
||||||
CompileOptions options(cx);
|
CompileOptions options(cx);
|
||||||
options.setFileAndLine(filename, 1)
|
options.setFileAndLine(filename, 1)
|
||||||
|
.setHasPollutedScope(HasPollutedScopeChain(scopeobj) ||
|
||||||
|
callerScript->hasPollutedGlobalScope())
|
||||||
.setIsRunOnce(true)
|
.setIsRunOnce(true)
|
||||||
.setForEval(true)
|
.setForEval(true)
|
||||||
.setNoScriptRval(false)
|
.setNoScriptRval(false)
|
||||||
@ -417,7 +442,7 @@ js::DirectEvalStringFromIon(JSContext* cx,
|
|||||||
: SourceBufferHolder::NoOwnership;
|
: SourceBufferHolder::NoOwnership;
|
||||||
SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
|
SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
|
||||||
JSScript* compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
|
JSScript* compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
|
||||||
scopeobj, staticScope, callerScript,
|
scopeobj, callerScript, staticScope,
|
||||||
options, srcBuf, linearStr, staticLevel);
|
options, srcBuf, linearStr, staticLevel);
|
||||||
if (!compiled)
|
if (!compiled)
|
||||||
return false;
|
return false;
|
||||||
@ -488,31 +513,35 @@ js::ExecuteInGlobalAndReturnScope(JSContext* cx, HandleObject global, HandleScri
|
|||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
assertSameCompartment(cx, global);
|
assertSameCompartment(cx, global);
|
||||||
MOZ_ASSERT(global->is<GlobalObject>());
|
MOZ_ASSERT(global->is<GlobalObject>());
|
||||||
MOZ_RELEASE_ASSERT(scriptArg->hasNonSyntacticScope());
|
MOZ_RELEASE_ASSERT(scriptArg->hasPollutedGlobalScope());
|
||||||
|
|
||||||
RootedScript script(cx, scriptArg);
|
RootedScript script(cx, scriptArg);
|
||||||
if (script->compartment() != cx->compartment()) {
|
if (script->compartment() != cx->compartment()) {
|
||||||
Rooted<ScopeObject*> staticScope(cx, StaticNonSyntacticScopeObjects::create(cx, nullptr));
|
script = CloneScript(cx, nullptr, nullptr, script);
|
||||||
if (!staticScope)
|
|
||||||
return false;
|
|
||||||
script = CloneGlobalScript(cx, staticScope, script);
|
|
||||||
if (!script)
|
if (!script)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Debugger::onNewScript(cx, script);
|
Debugger::onNewScript(cx, script);
|
||||||
}
|
}
|
||||||
|
|
||||||
Rooted<GlobalObject*> globalRoot(cx, &global->as<GlobalObject>());
|
RootedObject scope(cx, JS_NewPlainObject(cx));
|
||||||
Rooted<ScopeObject*> scope(cx, NonSyntacticVariablesObject::create(cx, globalRoot));
|
|
||||||
if (!scope)
|
if (!scope)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (!scope->setQualifiedVarObj(cx))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!scope->setUnqualifiedVarObj(cx))
|
||||||
|
return false;
|
||||||
|
|
||||||
JSObject* thisobj = GetThisObject(cx, global);
|
JSObject* thisobj = GetThisObject(cx, global);
|
||||||
if (!thisobj)
|
if (!thisobj)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
RootedValue thisv(cx, ObjectValue(*thisobj));
|
RootedValue thisv(cx, ObjectValue(*thisobj));
|
||||||
RootedValue rval(cx);
|
RootedValue rval(cx);
|
||||||
|
// XXXbz when this is fixed to pass in an actual ScopeObject, fix
|
||||||
|
// up the assert in js::CloneFunctionObject accordingly.
|
||||||
if (!ExecuteKernel(cx, script, *scope, thisv, UndefinedValue(), EXECUTE_GLOBAL,
|
if (!ExecuteKernel(cx, script, *scope, thisv, UndefinedValue(), EXECUTE_GLOBAL,
|
||||||
NullFramePtr() /* evalInFrame */, rval.address()))
|
NullFramePtr() /* evalInFrame */, rval.address()))
|
||||||
{
|
{
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user