Merge m-c to b-i

This commit is contained in:
Phil Ringnalda 2015-01-31 09:16:37 -08:00
commit bee9cd608f
337 changed files with 28491 additions and 23239 deletions

View File

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 1109248 - This needed a CLOBBER on Windows and OSX.
Bug 870366 - Blacklisting PREF_JS_EXPORTS in Makefile.ins (because of 852814)

View File

@ -15,6 +15,9 @@ namespace a11y {
bool
DocAccessibleParent::RecvShowEvent(const ShowEventData& aData)
{
if (mShutdown)
return true;
if (aData.NewTree().IsEmpty()) {
NS_ERROR("no children being added");
return false;
@ -95,6 +98,9 @@ DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
bool
DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID)
{
if (mShutdown)
return true;
ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
if (!rootEntry) {
NS_ERROR("invalid root being removed!");
@ -151,17 +157,21 @@ PLDHashOperator
DocAccessibleParent::ShutdownAccessibles(ProxyEntry* entry, void*)
{
ProxyDestroyed(entry->mProxy);
return PL_DHASH_NEXT;
return PL_DHASH_REMOVE;
}
void
DocAccessibleParent::Destroy()
{
MOZ_ASSERT(mChildDocs.IsEmpty(),
"why weren't the child docs destroyed already?");
NS_ASSERTION(mChildDocs.IsEmpty(),
"why weren't the child docs destroyed already?");
MOZ_ASSERT(!mShutdown);
mShutdown = true;
uint32_t childDocCount = mChildDocs.Length();
for (uint32_t i = childDocCount - 1; i < childDocCount; i--)
mChildDocs[i]->Destroy();
mAccessibles.EnumerateEntries(ShutdownAccessibles, nullptr);
ProxyDestroyed(this);
mParentDoc ? mParentDoc->RemoveChildDoc(this)

View File

@ -15,7 +15,7 @@ namespace a11y {
void
ProxyAccessible::Shutdown()
{
MOZ_ASSERT(!mOuterDoc);
NS_ASSERTION(!mOuterDoc, "Why do we still have a child doc?");
// XXX Ideally this wouldn't be necessary, but it seems OuterDoc accessibles
// can be destroyed before the doc they own.

View File

@ -5,8 +5,6 @@
USE_RCS_MK := 1
include $(topsrcdir)/config/makefiles/rcs.mk
PREF_JS_EXPORTS = $(srcdir)/b2g.js
UA_UPDATE_FILE = ua-update.json
$(UA_UPDATE_FILE): % : %.in

View File

@ -76,3 +76,8 @@ if CONFIG['OS_ARCH'] == 'WINNT':
]
FAIL_ON_WARNINGS = True
JS_PREFERENCE_FILES += [
'b2g.js',
]

View File

@ -1,5 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
PREF_JS_EXPORTS += $(topsrcdir)/b2g/app/b2g.js

View File

@ -4,3 +4,8 @@
DIST_SUBDIR = 'browser'
export('DIST_SUBDIR')
JS_PREFERENCE_FILES += [
'/b2g/app/b2g.js',
]

View File

@ -25,7 +25,10 @@ RETRIEVE_WINDOWS_INSTALLER = 1
MOZ_LANGPACK_EID=langpack-$(AB_CD)@b2g.mozilla.org
PREF_JS_EXPORTS = $(call MERGE_FILE,b2g-l10n.js)
L10N_PREF_JS_EXPORTS = $(call MERGE_FILE,b2g-l10n.js)
L10N_PREF_JS_EXPORTS_PATH = $(FINAL_TARGET)/$(PREF_DIR)
L10N_PREF_JS_EXPORTS_FLAGS = $(PREF_PPFLAGS) --silence-missing-directive-warnings
PP_TARGETS += L10N_PREF_JS_EXPORTS
ifneq (,$(filter cocoa,$(MOZ_WIDGET_TOOLKIT)))
MOZ_PKG_MAC_DSSTORE=$(_ABS_DIST)/branding/dsstore

View File

@ -4,9 +4,6 @@
dist_dest = $(DIST)/$(MOZ_MACBUNDLE_NAME)
PREF_JS_EXPORTS = $(srcdir)/profile/firefox.js \
$(NULL)
# hardcode en-US for the moment
AB_CD = en-US
@ -18,10 +15,6 @@ DEFINES += \
-DPBMODE_ICO='"$(DIST)/branding/pbmode.ico"' \
$(NULL)
ifdef LIBXUL_SDK #{
PREF_JS_EXPORTS += $(srcdir)/profile/channel-prefs.js
endif #} LIBXUL_SDK
# Build a binary bootstrapping with XRE_main
ifndef MOZ_WINCONSOLE

View File

@ -1,5 +1,5 @@
<?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1421170069000">
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1422484744000">
<emItems>
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
<versionRange minVersion="0" maxVersion="*">
@ -953,6 +953,12 @@
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i822" id="{6af08a71-380e-42dd-9312-0111d2bc0630}">
<versionRange minVersion="0" maxVersion="*" severity="3">
</versionRange>
<prefs>
</prefs>
</emItem>
<emItem blockID="i490" id="now.msn.com@services.mozilla.org">
<versionRange minVersion="0" maxVersion="*" severity="3">
@ -2792,6 +2798,18 @@
<match name="filename" exp="(NPSWF32.*\.dll)|(Flash\ Player\.plugin)" /> <versionRange minVersion="14.0" maxVersion="15.0.0.242" severity="0" vulnerabilitystatus="1"></versionRange>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
</pluginItem>
<pluginItem blockID="p824">
<match name="filename" exp="(NPSWF32.*\.dll)|(Flash\ Player\.plugin)" /> <versionRange minVersion="13.0.0.259" maxVersion="13.0.0.263" severity="0" vulnerabilitystatus="1"></versionRange>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
</pluginItem>
<pluginItem os="Linux" blockID="p826">
<match name="filename" exp="libflashplayer\.so" /> <versionRange minVersion="11.2.202.425" maxVersion="11.2.202.439" severity="0" vulnerabilitystatus="1"></versionRange>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
</pluginItem>
<pluginItem blockID="p828">
<match name="filename" exp="(NPSWF32.*\.dll)|(Flash\ Player\.plugin)" /> <versionRange minVersion="15.0.0.243" maxVersion="16.0.0.287" severity="0" vulnerabilitystatus="1"></versionRange>
<infoURL>https://get.adobe.com/flashplayer/</infoURL>
</pluginItem>
</pluginItems>
<gfxItems>

View File

@ -11,6 +11,15 @@ if CONFIG['OS_ARCH'] == 'WINNT' and (CONFIG['MOZ_METRO'] or CONFIG['MOZ_ASAN']):
else:
GeckoProgram(CONFIG['MOZ_APP_NAME'], msvcrt='static')
JS_PREFERENCE_FILES += [
'profile/firefox.js',
]
if CONFIG['LIBXUL_SDK']:
PREF_JS_EXPORTS += [
'profile/channel-prefs.js',
]
SOURCES += [
'nsBrowserApp.cpp',
]

View File

@ -0,0 +1,8 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
html:not(.crashDumpSubmitted) #reportSent,
html:not(.crashDumpAvailable) #report-box {
display: none;
}

View File

@ -12,21 +12,35 @@ function parseQueryString() {
document.title = parseQueryString();
addEventListener("DOMContentLoaded", () => {
let tryAgain = document.getElementById("tryAgain");
let sendCrashReport = document.getElementById("checkSendReport");
function shouldSendReport() {
if (!document.documentElement.classList.contains("crashDumpAvailable"))
return false;
return document.getElementById("sendReport").checked;
}
tryAgain.addEventListener("click", () => {
let event = new CustomEvent("AboutTabCrashedTryAgain", {
bubbles: true,
detail: {
sendCrashReport: sendCrashReport.checked,
},
});
document.dispatchEvent(event);
function sendEvent(message) {
let event = new CustomEvent("AboutTabCrashedMessage", {
bubbles: true,
detail: {
message,
sendCrashReport: shouldSendReport(),
},
});
});
document.dispatchEvent(event);
}
function closeTab() {
sendEvent("closeTab");
}
function restoreTab() {
sendEvent("restoreTab");
}
function restoreAll() {
sendEvent("restoreAll");
}
// Error pages are loaded as LOAD_BACKGROUND, so they don't get load events.
var event = new CustomEvent("AboutTabCrashedLoad", {bubbles:true});

View File

@ -12,18 +12,19 @@
<!ENTITY % globalDTD
SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % browserDTD
SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % tabCrashedDTD
SYSTEM "chrome://browser/locale/aboutTabCrashed.dtd">
%tabCrashedDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" type="text/css" media="all"
href="chrome://global/skin/in-content/info-pages.css"/>
<link rel="stylesheet" type="text/css" media="all"
href="chrome://browser/content/aboutTabCrashed.css"/>
<link rel="stylesheet" type="text/css" media="all"
href="chrome://browser/skin/aboutTabCrashed.css"/>
</head>
@ -36,12 +37,19 @@
<p>&tabCrashed.message;</p>
<div id="report-box">
<input type="checkbox" id="checkSendReport" checked="checked"/>
<label for="checkSendReport">&tabCrashed.checkSendReport;</label>
<input type="checkbox" id="sendReport" checked="checked"/>
<label for="sendReport">&tabCrashed.sendReport;</label>
</div>
<p id="reportSent">&tabCrashed.reportSent;</p>
<div class="button-container">
<button id="tryAgain">&tabCrashed.tryAgain;</button>
<button id="closeTab" onclick="closeTab()">
&tabCrashed.closeTab;</button>
<button id="restoreTab" onclick="restoreTab()">
&tabCrashed.restoreTab;</button>
<button id="restoreAll" onclick="restoreAll()" autofocus="true" class="primary">
&tabCrashed.restoreAll;</button>
</div>
</div>
</body>

View File

@ -271,6 +271,12 @@ XPCOMUtils.defineLazyGetter(this, "PageMenuParent", function() {
return new tmp.PageMenuParent();
});
function* browserWindows() {
let windows = Services.wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements())
yield windows.getNext();
}
/**
* We can avoid adding multiple load event listeners and save some time by adding
* one listener that calls all real handlers.
@ -1116,7 +1122,7 @@ var gBrowserInit = {
#endif
}, false, true);
gBrowser.addEventListener("AboutTabCrashedTryAgain", function(event) {
gBrowser.addEventListener("AboutTabCrashedMessage", function(event) {
let ownerDoc = event.originalTarget;
if (!ownerDoc.documentURI.startsWith("about:tabcrashed")) {
@ -1134,8 +1140,23 @@ var gBrowserInit = {
TabCrashReporter.submitCrashReport(browser);
}
#endif
let tab = gBrowser.getTabForBrowser(browser);
SessionStore.reviveCrashedTab(tab);
switch (event.detail.message) {
case "closeTab":
gBrowser.removeTab(tab, { animate: true });
break;
case "restoreTab":
SessionStore.reviveCrashedTab(tab);
break;
case "restoreAll":
for (let browserWin of browserWindows()) {
for (let tab of window.gBrowser.tabs) {
SessionStore.reviveCrashedTab(tab);
}
}
break;
}
}, false, true);
let uriToLoad = this._getUriToLoad();
@ -6471,11 +6492,9 @@ function warnAboutClosingWindow() {
return gBrowser.warnAboutClosingTabs(gBrowser.closingTabsEnum.ALL);
// Figure out if there's at least one other browser window around.
let e = Services.wm.getEnumerator("navigator:browser");
let otherPBWindowExists = false;
let nonPopupPresent = false;
while (e.hasMoreElements()) {
let win = e.getNext();
for (let win of browserWindows()) {
if (!win.closed && win != window) {
if (isPBWindow && PrivateBrowsingUtils.isWindowPrivate(win))
otherPBWindowExists = true;
@ -7574,9 +7593,7 @@ function switchToTabHavingURI(aURI, aOpenNew, aOpenParams={}) {
if (isBrowserWindow && switchIfURIInWindow(window))
return true;
let winEnum = Services.wm.getEnumerator("navigator:browser");
while (winEnum.hasMoreElements()) {
let browserWin = winEnum.getNext();
for (let browserWin of browserWindows()) {
// Skip closed (but not yet destroyed) windows,
// and the current window (which was checked earlier).
if (browserWin.closed || browserWin == window)

View File

@ -51,9 +51,10 @@ tabpanels {
}
}
.tab-icon-image:not([src]):not([pinned]),
.tab-icon-image:not([src]):not([pinned]):not([crashed]),
.tab-throbber:not([busy]),
.tab-throbber[busy] + .tab-icon-image {
.tab-icon-image[busy],
.tab-icon-overlay[busy] {
display: none;
}

View File

@ -664,9 +664,13 @@
// We need to add 2 because loadURIWithFlags may have
// cancelled a pending load which would have cleared
// its anchor scroll detection temporary increment.
if (aWebProgress.isTopLevel)
if (aWebProgress.isTopLevel) {
this.mBrowser.userTypedClear += 2;
// If the browser is loading it must not be crashed anymore
this.mTab.removeAttribute("crashed");
}
if (this._shouldShowProgress(aRequest)) {
if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
this.mTab.setAttribute("busy", "true");
@ -1484,6 +1488,10 @@
if (aShouldBeRemote) {
tab.setAttribute("remote", "true");
// Switching the browser to be remote will connect to a new child
// process so the browser can no longer be considered to be
// crashed.
tab.removeAttribute("crashed");
} else {
tab.removeAttribute("remote");
aBrowser.messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: tab.pinned })
@ -3579,6 +3587,7 @@
browser.docShell.displayLoadError(Cr.NS_ERROR_CONTENT_CRASHED, uri, null);
browser.removeAttribute("crashedPageTitle");
let tab = this.getTabForBrowser(browser);
tab.setAttribute("crashed", true);
this.setIcon(tab, icon);
]]>
</handler>
@ -4980,11 +4989,14 @@
class="tab-throbber"
role="presentation"
layer="true" />
<xul:image xbl:inherits="src=image,fadein,pinned,selected"
<xul:image xbl:inherits="src=image,fadein,pinned,selected,busy,crashed"
anonid="tab-icon-image"
class="tab-icon-image"
validate="never"
role="presentation"/>
<xul:image xbl:inherits="crashed,busy"
class="tab-icon-overlay"
role="presentation"/>
<xul:label flex="1"
anonid="tab-label"
xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected"

View File

@ -69,6 +69,7 @@ browser.jar:
content/browser/aboutRobots-widget-left.png (content/aboutRobots-widget-left.png)
content/browser/aboutSocialError.xhtml (content/aboutSocialError.xhtml)
content/browser/aboutProviderDirectory.xhtml (content/aboutProviderDirectory.xhtml)
content/browser/aboutTabCrashed.css (content/aboutTabCrashed.css)
content/browser/aboutTabCrashed.js (content/aboutTabCrashed.js)
content/browser/aboutTabCrashed.xhtml (content/aboutTabCrashed.xhtml)
* content/browser/browser.css (content/browser.css)

View File

@ -4,8 +4,6 @@
include $(topsrcdir)/config/config.mk
PREF_JS_EXPORTS = $(srcdir)/pref/firefox-branding.js
# On Windows only do this step for browser, skip for metro.
ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows browser)
BRANDING_FILES := \

View File

@ -8,3 +8,8 @@ DIRS += ['content', 'locales']
DIST_SUBDIR = 'browser'
export('DIST_SUBDIR')
JS_PREFERENCE_FILES += [
'pref/firefox-branding.js',
]

View File

@ -4,8 +4,6 @@
include $(topsrcdir)/config/config.mk
PREF_JS_EXPORTS = $(srcdir)/pref/firefox-branding.js
# On Windows only do this step for browser, skip for metro.
ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows browser)
BRANDING_FILES := \

View File

@ -8,3 +8,8 @@ DIRS += ['content', 'locales']
DIST_SUBDIR = 'browser'
export('DIST_SUBDIR')
JS_PREFERENCE_FILES += [
'pref/firefox-branding.js',
]

View File

@ -4,8 +4,6 @@
include $(topsrcdir)/config/config.mk
PREF_JS_EXPORTS = $(srcdir)/pref/firefox-branding.js
# On Windows only do this step for browser, skip for metro.
ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows browser)
BRANDING_FILES := \

View File

@ -8,3 +8,8 @@ DIRS += ['content', 'locales']
DIST_SUBDIR = 'browser'
export('DIST_SUBDIR')
JS_PREFERENCE_FILES += [
'pref/firefox-branding.js',
]

View File

@ -4,8 +4,6 @@
include $(topsrcdir)/config/config.mk
PREF_JS_EXPORTS = $(srcdir)/pref/firefox-branding.js
# On Windows only do this step for browser, skip for metro.
ifeq ($(MOZ_WIDGET_TOOLKIT) $(DIST_SUBDIR),windows browser)
BRANDING_FILES := \

View File

@ -8,3 +8,8 @@ DIRS += ['content', 'locales']
DIST_SUBDIR = 'browser'
export('DIST_SUBDIR')
JS_PREFERENCE_FILES += [
'pref/firefox-branding.js',
]

View File

@ -3,11 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Shared conversation window styles */
.standalone .video-layout-wrapper,
.conversation .media video {
background-color: #444;
}
.conversation {
position: relative;
}
@ -508,10 +503,11 @@
* XXX this approach is fragile because it makes assumptions
* about the generated OT markup, any change will break it
*/
.local-stream.local-stream-audio,
.standalone .OT_subscriber .OT_video-poster,
.fx-embedded .OT_video-container .OT_video-poster,
.local-stream-audio .OT_publisher .OT_video-poster {
/*
* For any audio-only streams, we want to display our own background
*/
.OT_audio-only .OT_widget-container .OT_video-poster {
background-image: url("../img/audio-call-avatar.svg");
background-repeat: no-repeat;
background-color: #4BA6E7;
@ -519,6 +515,22 @@
background-position: center;
}
/*
* Audio-only. For local streams, cancel out the SDK's opacity of 0.25.
* For remote streams we leave them shaded, as otherwise its too bright.
*/
.local-stream-audio .OT_publisher .OT_video-poster {
opacity: 1
}
/*
* In audio-only mode, don't display the video element, doing so interferes
* with the background opacity of the video-poster element.
*/
.OT_audio-only .OT_widget-container .OT_video-element {
display: none;
}
/*
* Ensure that the publisher (i.e. local) video is never cropped, so that it's
* not possible for someone to be presented with a picture that displays
@ -537,7 +549,7 @@
* Another less ugly possibility would be to work with Ted Mielczarek to use
* the fake camera drivers he has for Linux.
*/
.room-conversation .OT_publisher .OT_video-container {
.room-conversation .OT_publisher .OT_widget-container {
height: 100% !important;
width: 100% !important;
top: 0 !important;
@ -545,12 +557,12 @@
background-color: transparent; /* avoid visually obvious letterboxing */
}
.room-conversation .OT_publisher .OT_video-container video {
.room-conversation .OT_publisher .OT_widget-container video {
background-color: transparent; /* avoid visually obvious letterboxing */
}
.fx-embedded .room-conversation .room-preview .OT_publisher .OT_video-container,
.fx-embedded .room-conversation .room-preview .OT_publisher .OT_video-container video {
.fx-embedded .room-conversation .room-preview .OT_publisher .OT_widget-container,
.fx-embedded .room-conversation .room-preview .OT_publisher .OT_widget-container video {
/* Desktop conversation window room preview local stream actually wants
a black background */
background-color: #000;
@ -673,7 +685,6 @@ html, .fx-embedded, #main,
}
.standalone {
max-width: 1000px;
margin: 0 auto;
}
}
@ -905,11 +916,6 @@ html, .fx-embedded, #main,
width: 75%;
}
.standalone .room-conversation .local-stream {
width: 33%;
height: 26.5%;
}
.standalone .room-conversation .conversation-toolbar {
background: #000;
border: none;
@ -945,11 +951,6 @@ html, .fx-embedded, #main,
.standalone .room-conversation .video_wrapper.remote_wrapper {
width: 100%;
}
.standalone .room-conversation .local-stream {
/* Assumes 4:3 aspect ratio */
width: 180px;
height: 135px;
}
.standalone .conversation-toolbar {
height: 38px;
padding: 8px;

View File

@ -176,6 +176,15 @@ loop.shared.actions = (function() {
MediaConnected: Action.define("mediaConnected", {
}),
/**
* Used for notifying that the dimensions of a stream just changed. Also
* dispatched when a stream connects for the first time.
*/
VideoDimensionsChanged: Action.define("videoDimensionsChanged", {
videoType: String,
dimensions: Object
}),
/**
* Used to mute or unmute a stream
*/

View File

@ -68,7 +68,9 @@ loop.store.ActiveRoomStore = (function() {
// session. 'Used' means at least one call has been placed
// with it. Entering and leaving the room without seeing
// anyone is not considered as 'used'
used: false
used: false,
localVideoDimensions: {},
remoteVideoDimensions: {}
};
},
@ -119,7 +121,8 @@ loop.store.ActiveRoomStore = (function() {
"remotePeerConnected",
"windowUnload",
"leaveRoom",
"feedbackComplete"
"feedbackComplete",
"videoDimensionsChanged"
]);
},
@ -477,6 +480,23 @@ loop.store.ActiveRoomStore = (function() {
// Note, that we want some values, such as the windowId, so we don't
// do a full reset here.
this.setStoreState(this.getInitialStoreState());
},
/**
* Handles a change in dimensions of a video stream and updates the store data
* with the new dimensions of a local or remote stream.
*
* @param {sharedActions.VideoDimensionsChanged} actionData
*/
videoDimensionsChanged: function(actionData) {
// NOTE: in the future, when multiple remote video streams are supported,
// we'll need to make this support multiple remotes as well. Good
// starting point for video tiling.
var storeProp = (actionData.isLocal ? "local" : "remote") + "VideoDimensions";
var nextState = {};
nextState[storeProp] = this.getStoreState()[storeProp];
nextState[storeProp][actionData.videoType] = actionData.dimensions;
this.setStoreState(nextState);
}
});

View File

@ -157,29 +157,217 @@ loop.shared.mixins = (function() {
* elements and handling updates of the media containers.
*/
var MediaSetupMixin = {
_videoDimensionsCache: {
local: {},
remote: {}
},
componentDidMount: function() {
rootObject.addEventListener('orientationchange', this.updateVideoContainer);
rootObject.addEventListener('resize', this.updateVideoContainer);
rootObject.addEventListener("orientationchange", this.updateVideoContainer);
rootObject.addEventListener("resize", this.updateVideoContainer);
},
componentWillUnmount: function() {
rootObject.removeEventListener('orientationchange', this.updateVideoContainer);
rootObject.removeEventListener('resize', this.updateVideoContainer);
rootObject.removeEventListener("orientationchange", this.updateVideoContainer);
rootObject.removeEventListener("resize", this.updateVideoContainer);
},
/**
* Whenever the dimensions change of a video stream, this function is called
* by `updateVideoDimensions` to store the new values and notifies the callee
* if the dimensions changed compared to the currently stored values.
*
* @param {String} which Type of video stream. May be 'local' or 'remote'
* @param {Object} newDimensions Object containing 'width' and 'height' properties
* @return {Boolean} `true` when the dimensions have changed,
* `false` if not
*/
_updateDimensionsCache: function(which, newDimensions) {
var cache = this._videoDimensionsCache[which];
var cacheKeys = Object.keys(cache);
var changed = false;
Object.keys(newDimensions).forEach(function(videoType) {
if (cacheKeys.indexOf(videoType) === -1) {
cache[videoType] = newDimensions[videoType];
cache[videoType].aspectRatio = this.getAspectRatio(cache[videoType]);
changed = true;
return;
}
if (cache[videoType].width !== newDimensions[videoType].width) {
cache[videoType].width = newDimensions[videoType].width;
changed = true;
}
if (cache[videoType].height !== newDimensions[videoType].height) {
cache[videoType].height = newDimensions[videoType].height;
changed = true;
}
if (changed) {
cache[videoType].aspectRatio = this.getAspectRatio(cache[videoType]);
}
}, this);
return changed;
},
/**
* Whenever the dimensions change of a video stream, this function is called
* to process these changes and possibly trigger an update to the video
* container elements.
*
* @param {Object} localVideoDimensions Object containing 'width' and 'height'
* properties grouped by stream name
* @param {Object} remoteVideoDimensions Object containing 'width' and 'height'
* properties grouped by stream name
*/
updateVideoDimensions: function(localVideoDimensions, remoteVideoDimensions) {
var localChanged = this._updateDimensionsCache("local", localVideoDimensions);
var remoteChanged = this._updateDimensionsCache("remote", remoteVideoDimensions);
if (localChanged || remoteChanged) {
this.updateVideoContainer();
}
},
/**
* Get the aspect ratio of a width/ height pair, which should be the dimensions
* of a stream. The returned object is an aspect ratio indexed by 1; the leading
* size has a value smaller than 1 and the slave size has a value of 1.
* this is exactly the same as notations like 4:3 and 16:9, which are merely
* human-readable forms of their fractional counterparts. 4:3 === 1:0.75 and
* 16:9 === 1:0.5625.
* So we're using the aspect ratios in their original form, because that's
* easier to do calculus with.
*
* Example:
* A stream with dimensions `{ width: 640, height: 480 }` yields an indexed
* aspect ratio of `{ width: 1, height: 0.75 }`. This means that the 'height'
* will determine the value of 'width' when the stream is stretched or shrunk
* to fit inside its container element at the maximum size.
*
* @param {Object} dimensions Object containing 'width' and 'height' properties
* @return {Object} Contains the indexed aspect ratio for 'width'
* and 'height' assigned to the corresponding
* properties.
*/
getAspectRatio: function(dimensions) {
if (dimensions.width === dimensions.height) {
return {width: 1, height: 1};
}
var denominator = Math.max(dimensions.width, dimensions.height);
return {
width: dimensions.width / denominator,
height: dimensions.height / denominator
};
},
/**
* Retrieve the dimensions of the remote video stream.
* Example output:
* {
* width: 680,
* height: 480,
* streamWidth: 640,
* streamHeight: 480,
* offsetX: 20,
* offsetY: 0
* }
*
* Note: Once we support multiple remote video streams, this function will
* need to be updated.
* @return {Object} contains the remote stream dimension properties of its
* container node, the stream itself and offset of the stream
* relative to its container node in pixels.
*/
getRemoteVideoDimensions: function() {
var remoteVideoDimensions;
Object.keys(this._videoDimensionsCache.remote).forEach(function(videoType) {
var node = this._getElement("." + (videoType === "camera" ? "remote" : videoType));
var width = node.offsetWidth;
// If the width > 0 then we record its real size by taking its aspect
// ratio in account. Due to the 'contain' fit-mode, the stream will be
// centered inside the video element.
// We'll need to deal with more than one remote video stream whenever
// that becomes something we need to support.
if (width) {
remoteVideoDimensions = {
width: width,
height: node.offsetHeight
};
var ratio = this._videoDimensionsCache.remote[videoType].aspectRatio;
var leadingAxis = Math.min(ratio.width, ratio.height) === ratio.width ?
"width" : "height";
var slaveSize = remoteVideoDimensions[leadingAxis] +
(remoteVideoDimensions[leadingAxis] * (1 - ratio[leadingAxis]));
remoteVideoDimensions.streamWidth = leadingAxis === "width" ?
remoteVideoDimensions.width : slaveSize;
remoteVideoDimensions.streamHeight = leadingAxis === "height" ?
remoteVideoDimensions.height: slaveSize;
}
}, this);
// Supply some sensible defaults for the remoteVideoDimensions if no remote
// stream is connected (yet).
if (!remoteVideoDimensions) {
var node = this._getElement(".remote");
var width = node.offsetWidth;
var height = node.offsetHeight;
remoteVideoDimensions = {
width: width,
height: height,
streamWidth: width,
streamHeight: height
};
}
// Calculate the size of each individual letter- or pillarbox for convenience.
remoteVideoDimensions.offsetX = remoteVideoDimensions.width -
remoteVideoDimensions.streamWidth
if (remoteVideoDimensions.offsetX > 0) {
remoteVideoDimensions.offsetX /= 2;
}
remoteVideoDimensions.offsetY = remoteVideoDimensions.height -
remoteVideoDimensions.streamHeight;
if (remoteVideoDimensions.offsetY > 0) {
remoteVideoDimensions.offsetY /= 2;
}
return remoteVideoDimensions;
},
/**
* Used to update the video container whenever the orientation or size of the
* display area changes.
*
* Buffer the calls to this function to make sure we don't overflow the stack
* with update calls when many 'resize' event are fired, to prevent blocking
* the event loop.
*/
updateVideoContainer: function() {
var localStreamParent = this._getElement('.local .OT_publisher');
var remoteStreamParent = this._getElement('.remote .OT_subscriber');
if (localStreamParent) {
localStreamParent.style.width = "100%";
}
if (remoteStreamParent) {
remoteStreamParent.style.height = "100%";
if (this._bufferedUpdateVideo) {
rootObject.clearTimeout(this._bufferedUpdateVideo);
this._bufferedUpdateVideo = null;
}
this._bufferedUpdateVideo = rootObject.setTimeout(function() {
this._bufferedUpdateVideo = null;
var localStreamParent = this._getElement(".local .OT_publisher");
var remoteStreamParent = this._getElement(".remote .OT_subscriber");
if (localStreamParent) {
localStreamParent.style.width = "100%";
}
if (remoteStreamParent) {
remoteStreamParent.style.height = "100%";
}
// Update the position and dimensions of the containers of local video
// streams, if necessary. The consumer of this mixin should implement the
// actual updating mechanism.
Object.keys(this._videoDimensionsCache.local).forEach(function(videoType) {
var ratio = this._videoDimensionsCache.local[videoType].aspectRatio
if (videoType == "camera" && this.updateLocalCameraPosition) {
this.updateLocalCameraPosition(ratio);
}
}, this);
}.bind(this), 0);
},
/**
@ -198,15 +386,11 @@ loop.shared.mixins = (function() {
// @see https://bugzilla.mozilla.org/show_bug.cgi?id=1020445
return {
insertMode: "append",
fitMode: "contain",
width: "100%",
height: "100%",
publishVideo: options.publishVideo,
style: {
audioLevelDisplayMode: "off",
buttonDisplayMode: "off",
nameDisplayMode: "off",
videoDisabledDisplayMode: "off"
}
showControls: false,
};
},

View File

@ -9,6 +9,7 @@ loop.OTSdkDriver = (function() {
var sharedActions = loop.shared.actions;
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
var STREAM_PROPERTIES = loop.shared.utils.STREAM_PROPERTIES;
/**
* This is a wrapper for the OT sdk. It is used to translate the SDK events into
@ -51,6 +52,7 @@ loop.OTSdkDriver = (function() {
// the media.
this.publisher = this.sdk.initPublisher(this.getLocalElement(),
this.publisherConfig);
this.publisher.on("streamCreated", this._onLocalStreamCreated.bind(this));
this.publisher.on("accessAllowed", this._onPublishComplete.bind(this));
this.publisher.on("accessDenied", this._onPublishDenied.bind(this));
this.publisher.on("accessDialogOpened",
@ -91,6 +93,7 @@ loop.OTSdkDriver = (function() {
this._onConnectionDestroyed.bind(this));
this.session.on("sessionDisconnected",
this._onSessionDisconnected.bind(this));
this.session.on("streamPropertyChanged", this._onStreamPropertyChanged.bind(this));
// This starts the actual session connection.
this.session.connect(sessionData.apiKey, sessionData.sessionToken,
@ -102,12 +105,13 @@ loop.OTSdkDriver = (function() {
*/
disconnectSession: function() {
if (this.session) {
this.session.off("streamCreated connectionDestroyed sessionDisconnected");
this.session.off("streamCreated streamDestroyed connectionDestroyed " +
"sessionDisconnected streamPropertyChanged");
this.session.disconnect();
delete this.session;
}
if (this.publisher) {
this.publisher.off("accessAllowed accessDenied accessDialogOpened");
this.publisher.off("accessAllowed accessDenied accessDialogOpened streamCreated");
this.publisher.destroy();
delete this.publisher;
}
@ -234,6 +238,14 @@ loop.OTSdkDriver = (function() {
* https://tokbox.com/opentok/libraries/client/js/reference/StreamEvent.html
*/
_onRemoteStreamCreated: function(event) {
if (event.stream[STREAM_PROPERTIES.HAS_VIDEO]) {
this.dispatcher.dispatch(new sharedActions.VideoDimensionsChanged({
isLocal: false,
videoType: event.stream.videoType,
dimensions: event.stream[STREAM_PROPERTIES.VIDEO_DIMENSIONS]
}));
}
this.session.subscribe(event.stream,
this.getRemoteElement(), this.publisherConfig);
@ -243,6 +255,22 @@ loop.OTSdkDriver = (function() {
}
},
/**
* Handles the event when the local stream is created.
*
* @param {StreamEvent} event The event details:
* https://tokbox.com/opentok/libraries/client/js/reference/StreamEvent.html
*/
_onLocalStreamCreated: function(event) {
if (event.stream[STREAM_PROPERTIES.HAS_VIDEO]) {
this.dispatcher.dispatch(new sharedActions.VideoDimensionsChanged({
isLocal: true,
videoType: event.stream.videoType,
dimensions: event.stream[STREAM_PROPERTIES.VIDEO_DIMENSIONS]
}));
}
},
/**
* Called from the sdk when the media access dialog is opened.
* Prevents the default action, to prevent the SDK's "allow access"
@ -282,6 +310,19 @@ loop.OTSdkDriver = (function() {
}));
},
/**
* Handles publishing of property changes to a stream.
*/
_onStreamPropertyChanged: function(event) {
if (event.changedProperty == STREAM_PROPERTIES.VIDEO_DIMENSIONS) {
this.dispatcher.dispatch(new sharedActions.VideoDimensionsChanged({
isLocal: event.stream.connection.id == this.session.connection.id,
videoType: event.stream.videoType,
dimensions: event.stream[STREAM_PROPERTIES.VIDEO_DIMENSIONS]
}));
}
},
/**
* Publishes the local stream if the session is connected
* and the publisher is ready.

View File

@ -42,6 +42,12 @@ loop.shared.utils = (function(mozL10n) {
UNKNOWN: "reason-unknown"
};
var STREAM_PROPERTIES = {
VIDEO_DIMENSIONS: "videoDimensions",
HAS_AUDIO: "hasAudio",
HAS_VIDEO: "hasVideo"
};
/**
* Format a given date into an l10n-friendly string.
*
@ -138,6 +144,7 @@ loop.shared.utils = (function(mozL10n) {
FAILURE_DETAILS: FAILURE_DETAILS,
REST_ERRNOS: REST_ERRNOS,
WEBSOCKET_REASONS: WEBSOCKET_REASONS,
STREAM_PROPERTIES: STREAM_PROPERTIES,
Helper: Helper,
composeCallUrlEmail: composeCallUrlEmail,
formatDate: formatDate,

View File

@ -11,16 +11,15 @@
/* Root OT object, this is where our CSS reset happens */
.OT_root,
.OT_root * {
color: #ffffff;
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font-family: Arial, Helvetica, sans-serif;
vertical-align: baseline;
color: #ffffff;
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font-family: Arial, Helvetica, sans-serif;
vertical-align: baseline;
}
/**
* Specific Element Reset
*/
@ -31,10 +30,10 @@
.OT_root h4,
.OT_root h5,
.OT_root h6 {
color: #ffffff;
font-family: Arial, Helvetica, sans-serif;
font-size: 100%;
font-weight: bold;
color: #ffffff;
font-family: Arial, Helvetica, sans-serif;
font-size: 100%;
font-weight: bold;
}
.OT_root header {
@ -62,11 +61,11 @@
}
.OT_root strong {
font-weight: bold;
font-weight: bold;
}
.OT_root em {
font-style: italic;
font-style: italic;
}
.OT_root a,
@ -74,46 +73,46 @@
.OT_root a:visited,
.OT_root a:hover,
.OT_root a:active {
font-family: Arial, Helvetica, sans-serif;
font-family: Arial, Helvetica, sans-serif;
}
.OT_root ul, .OT_root ol {
margin: 1em 1em 1em 2em;
margin: 1em 1em 1em 2em;
}
.OT_root ol {
list-style: decimal outside;
list-style: decimal outside;
}
.OT_root ul {
list-style: disc outside;
list-style: disc outside;
}
.OT_root dl {
margin: 4px;
margin: 4px;
}
.OT_root dl dt,
.OT_root dl dd {
float: left;
margin: 0;
padding: 0;
}
.OT_root dl dt {
clear: left;
text-align: right;
width: 50px;
}
.OT_root dl dd {
margin-left: 10px;
}
.OT_root dl dt,
.OT_root dl dd {
float: left;
margin: 0;
padding: 0;
}
.OT_root dl dt {
clear: left;
text-align: right;
width: 50px;
}
.OT_root dl dd {
margin-left: 10px;
}
.OT_root img {
border: 0 none;
border: 0 none;
}
/* Modal dialog styles */
/* Modal dialog styles */
@ -166,7 +165,6 @@
text-align: center;
}
.OT_dialog-messages-main {
margin-bottom: 36px;
line-height: 36px;
@ -302,37 +300,34 @@
/* Publisher and Subscriber styles */
.OT_publisher, .OT_subscriber {
position: relative;
min-width: 48px;
min-height: 48px;
position: relative;
min-width: 48px;
min-height: 48px;
}
.OT_publisher video,
.OT_subscriber video,
.OT_publisher object,
.OT_subscriber object {
width: 100%;
height: 100%;
}
.OT_publisher .OT_video-element,
.OT_subscriber .OT_video-element {
display: block;
position: absolute;
width: 100%;
.OT_publisher object,
.OT_subscriber object {
transform-origin: 0 0;
}
/* Styles that are applied when the video element should be mirrored */
.OT_publisher.OT_mirrored video{
-webkit-transform: scale(-1, 1);
-moz-transform:scale(-1,1);
.OT_publisher.OT_mirrored .OT_video-element {
transform: scale(-1, 1);
transform-origin: 50% 50%;
}
.OT_subscriber_error {
background-color: #000;
color: #fff;
text-align: center;
background-color: #000;
color: #fff;
text-align: center;
}
.OT_subscriber_error > p {
padding: 20px;
padding: 20px;
}
/* The publisher/subscriber name/mute background */
@ -346,67 +341,67 @@
.OT_subscriber .OT_archiving-status,
.OT_publihser .OT_archiving-light-box,
.OT_subscriber .OT_archiving-light-box {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
top: 0;
left: 0;
right: 0;
display: block;
height: 34px;
position: absolute;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
top: 0;
left: 0;
right: 0;
display: block;
height: 34px;
position: absolute;
}
.OT_publisher .OT_bar,
.OT_subscriber .OT_bar {
background: rgba(0, 0, 0, 0.4);
background: rgba(0, 0, 0, 0.4);
}
.OT_publisher .OT_edge-bar-item,
.OT_subscriber .OT_edge-bar-item {
z-index: 1; /* required to get audio level meter underneath */
z-index: 1; /* required to get audio level meter underneath */
}
/* The publisher/subscriber name panel/archiving status bar */
.OT_publisher .OT_name,
.OT_subscriber .OT_name {
background-color: transparent;
color: #ffffff;
font-size: 15px;
line-height: 34px;
font-weight: normal;
padding: 0 4px 0 36px;
background-color: transparent;
color: #ffffff;
font-size: 15px;
line-height: 34px;
font-weight: normal;
padding: 0 4px 0 36px;
}
.OT_publisher .OT_archiving-status,
.OT_subscriber .OT_archiving-status {
background: rgba(0, 0, 0, 0.4);
top: auto;
bottom: 0;
left: 34px;
padding: 0 4px;
color: rgba(255,255,255,0.8);
font-size: 15px;
line-height: 34px;
font-weight: normal;
background: rgba(0, 0, 0, 0.4);
top: auto;
bottom: 0;
left: 34px;
padding: 0 4px;
color: rgba(255, 255, 255, 0.8);
font-size: 15px;
line-height: 34px;
font-weight: normal;
}
.OT_micro .OT_archiving-status,
.OT_micro:hover .OT_archiving-status,
.OT_mini .OT_archiving-status,
.OT_mini:hover .OT_archiving-status {
display: none;
display: none;
}
.OT_publisher .OT_archiving-light-box,
.OT_subscriber .OT_archiving-light-box {
background: rgba(0, 0, 0, 0.4);
top: auto;
bottom: 0;
right: auto;
width: 34px;
height: 34px;
background: rgba(0, 0, 0, 0.4);
top: auto;
bottom: 0;
right: auto;
width: 34px;
height: 34px;
}
.OT_archiving-light {
@ -423,6 +418,7 @@
-moz-box-shadow: 0 0 5px 1px #575757;
box-shadow: 0 0 5px 1px #575757;
}
.OT_archiving-light.OT_active {
background-color: #970d13;
-webkit-animation: OT_pulse 1.3s ease-in;
@ -432,6 +428,7 @@
-moz-animation-iteration-count: infinite;
-webkit-animation-iteration-count: infinite;
}
@-moz-keyframes OT_pulse {
0% {
-webkit-box-shadow: 0 0 0px 0px #c70019;
@ -463,6 +460,7 @@
box-shadow: 0 0 0px 0px #c70019;
}
}
@-webkit-keyframes OT_pulse {
0% {
-webkit-box-shadow: 0 0 0px 0px #c70019;
@ -494,6 +492,7 @@
box-shadow: 0 0 0px 0px #c70019;
}
}
@-o-keyframes OT_pulse {
0% {
-webkit-box-shadow: 0 0 0px 0px #c70019;
@ -525,6 +524,7 @@
box-shadow: 0 0 0px 0px #c70019;
}
}
@-ms-keyframes OT_pulse {
0% {
-webkit-box-shadow: 0 0 0px 0px #c70019;
@ -556,6 +556,7 @@
box-shadow: 0 0 0px 0px #c70019;
}
}
@-webkit-keyframes OT_pulse {
0% {
-webkit-box-shadow: 0 0 0px 0px #c70019;
@ -591,15 +592,15 @@
.OT_mini .OT_bar,
.OT_bar.OT_mode-mini,
.OT_bar.OT_mode-mini-auto {
bottom: 0;
height: auto;
bottom: 0;
height: auto;
}
.OT_mini .OT_name.OT_mode-off,
.OT_mini .OT_name.OT_mode-on,
.OT_mini .OT_name.OT_mode-auto,
.OT_mini:hover .OT_name.OT_mode-auto {
display: none;
display: none;
}
.OT_publisher .OT_name,
@ -624,42 +625,42 @@
.OT_publisher .OT_mute,
.OT_subscriber .OT_mute {
right: 0;
top: 0;
border-left: 1px solid rgba(255, 255, 255, 0.2);
height: 36px;
width: 37px;
right: 0;
top: 0;
border-left: 1px solid rgba(255, 255, 255, 0.2);
height: 36px;
width: 37px;
}
.OT_mini .OT_mute,
.OT_mute.OT_mode-mini,
.OT_mute.OT_mode-mini-auto {
top: 50%;
left: 50%;
right: auto;
margin-top: -18px;
margin-left: -18.5px;
border-left: none;
.OT_publisher.OT_mini .OT_mute.OT_mode-auto.OT_mode-on-hold,
.OT_subscriber.OT_mini .OT_mute.OT_mode-auto.OT_mode-on-hold {
top: 50%;
left: 50%;
right: auto;
margin-top: -18px;
margin-left: -18.5px;
border-left: none;
}
.OT_publisher .OT_mute {
background-image: url(../images/rtc/mic-on.png);
background-position: 9px 5px;
background-image: url(../images/rtc/mic-on.png);
background-position: 9px 5px;
}
.OT_publisher .OT_mute.OT_active {
background-image: url(../images/rtc/mic-off.png);
background-position: 9px 4px;
background-image: url(../images/rtc/mic-off.png);
background-position: 9px 4px;
}
.OT_subscriber .OT_mute {
background-image: url(../images/rtc/speaker-on.png);
background-position: 8px 7px;
background-image: url(../images/rtc/speaker-on.png);
background-position: 8px 7px;
}
.OT_subscriber .OT_mute.OT_active {
background-image: url(../images/rtc/speaker-off.png);
background-position: 7px 7px;
background-image: url(../images/rtc/speaker-off.png);
background-position: 7px 7px;
}
/**
@ -672,17 +673,9 @@
/* Default display mode transitions for various chrome elements */
.OT_publisher .OT_edge-bar-item,
.OT_subscriber .OT_edge-bar-item {
-ms-transition-property: top, bottom, opacity;
-ms-transition-duration: 0.5s;
-moz-transition-property: top, bottom, opacity;
-moz-transition-duration: 0.5s;
-webkit-transition-property: top, bottom, opacity;
-webkit-transition-duration: 0.5s;
-o-transition-property: top, bottom, opacity;
-o-transition-duration: 0.5s;
transition-property: top, bottom, opacity;
transition-duration: 0.5s;
transition-timing-function: ease-in;
transition-property: top, bottom, opacity;
transition-duration: 0.5s;
transition-timing-function: ease-in;
}
.OT_publisher .OT_edge-bar-item.OT_mode-off,
@ -691,14 +684,14 @@
.OT_subscriber .OT_edge-bar-item.OT_mode-auto,
.OT_publisher .OT_edge-bar-item.OT_mode-mini-auto,
.OT_subscriber .OT_edge-bar-item.OT_mode-mini-auto {
top: -25px;
opacity: 0;
top: -25px;
opacity: 0;
}
.OT_mini .OT_mute.OT_mode-auto,
.OT_publisher .OT_mute.OT_mode-mini-auto,
.OT_subscriber .OT_mute.OT_mode-mini-auto {
top: 50%;
top: 50%;
}
.OT_publisher .OT_edge-bar-item.OT_edge-bottom.OT_mode-off,
@ -707,8 +700,8 @@
.OT_subscriber .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto,
.OT_publisher .OT_edge-bar-item.OT_edge-bottom.OT_mode-mini-auto,
.OT_subscriber .OT_edge-bar-item.OT_edge-bottom.OT_mode-mini-auto {
top: auto;
bottom: -25px;
top: auto;
bottom: -25px;
}
.OT_publisher .OT_edge-bar-item.OT_mode-on,
@ -719,127 +712,148 @@
.OT_subscriber:hover .OT_edge-bar-item.OT_mode-auto,
.OT_publisher:hover .OT_edge-bar-item.OT_mode-mini-auto,
.OT_subscriber:hover .OT_edge-bar-item.OT_mode-mini-auto {
top: 0;
opacity: 1;
top: 0;
opacity: 1;
}
.OT_mini .OT_mute.OT_mode-on,
.OT_mini:hover .OT_mute.OT_mode-auto,
.OT_mute.OT_mode-mini,
.OT_root:hover .OT_mute.OT_mode-mini-auto {
top: 50%;
.OT_root:hover .OT_mute.OT_mode-mini-auto {
top: 50%;
}
.OT_publisher .OT_edge-bar-item.OT_edge-bottom.OT_mode-on,
.OT_subscriber .OT_edge-bar-item.OT_edge-bottom.OT_mode-on,
.OT_publisher:hover .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto,
.OT_subscriber:hover .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto {
top: auto;
bottom: 0;
opacity: 1;
top: auto;
bottom: 0;
opacity: 1;
}
/* Contains the video element, used to fix video letter-boxing */
.OT_video-container {
position: absolute;
background-color: #000000;
overflow: hidden;
.OT_widget-container {
position: absolute;
background-color: #000000;
overflow: hidden;
}
.OT_hidden-audio {
position: absolute !important;
height: 1px !important;
width: 1px !important;
position: absolute !important;
height: 1px !important;
width: 1px !important;
}
/* Load animation */
.OT_root .OT_video-loading {
background: url('../images/rtc/loader.gif') no-repeat;
display:none;
position: absolute;
width: 32px;
height: 32px;
left: 50%;
top: 50%;
margin-left: -16px;
margin-top: -16px;
background: url('../images/rtc/loader.gif') no-repeat;
display: none;
position: absolute;
width: 32px;
height: 32px;
left: 50%;
top: 50%;
margin-left: -16px;
margin-top: -16px;
}
.OT_publisher.OT_loading .OT_video-loading,
.OT_subscriber.OT_loading .OT_video-loading {
display: block;
display: block;
}
.OT_publisher.OT_loading video,
.OT_subscriber.OT_loading video,
.OT_publisher.OT_loading object,
.OT_subscriber.OT_loading object {
display: none;
.OT_publisher.OT_loading .OT_video-element,
.OT_subscriber.OT_loading .OT_video-element {
/*display: none;*/
}
.OT_video-centering {
display: table;
width: 100%;
height: 100%;
}
.OT_video-container {
display: table-cell;
vertical-align: middle;
}
.OT_video-poster {
width: 100%;
height: 100%;
display: none;
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
display: none;
opacity: .25;
background-size: auto 76%;
background-repeat: no-repeat;
background-position: center bottom;
background-image: url(../images/rtc/audioonly-silhouette.svg);
opacity: .25;
background-repeat: no-repeat;
background-image: url(../images/rtc/audioonly-silhouette.svg);
}
.OT_fit-mode-cover .OT_video-poster {
background-size: auto 76%;
background-position: center bottom;
}
.OT_fit-mode-contain .OT_video-poster {
background-size: contain;
background-position: center;
}
.OT_audio-level-meter {
position: absolute;
width: 25%;
max-width: 224px;
min-width: 21px;
top: 0;
right: 0;
overflow: hidden;
position: absolute;
width: 25%;
max-width: 224px;
min-width: 21px;
top: 0;
right: 0;
overflow: hidden;
}
.OT_audio-level-meter:before {
/* makes the height of the container equals its width */
content: '';
display: block;
padding-top: 100%;
/* makes the height of the container equals its width */
content: '';
display: block;
padding-top: 100%;
}
.OT_audio-level-meter__bar {
position: absolute;
width: 192%; /* meter value can overflow of 8% */
height: 192%;
top: -96% /* half of the size */;
right: -96%;
border-radius: 50%;
position: absolute;
width: 192%; /* meter value can overflow of 8% */
height: 192%;
top: -96% /* half of the size */;
right: -96%;
border-radius: 50%;
background-color: rgba(0, 0, 0, .8);
background-color: rgba(0, 0, 0, .8);
}
.OT_audio-level-meter__audio-only-img {
position: absolute;
top: 22%;
right: 15%;
width: 40%;
position: absolute;
top: 22%;
right: 15%;
width: 40%;
opacity: .7;
opacity: .7;
background: url(../images/rtc/audioonly-headset.svg) no-repeat center;
background: url(../images/rtc/audioonly-headset.svg) no-repeat center;
}
.OT_audio-level-meter__audio-only-img:before {
/* makes the height of the container equals its width */
content: '';
display: block;
padding-top: 100%;
/* makes the height of the container equals its width */
content: '';
display: block;
padding-top: 100%;
}
.OT_audio-level-meter__value {
position: absolute;
border-radius: 50%;
background-image: radial-gradient(circle, rgba(151,206,0,1) 0%, rgba(151,206,0,0) 100%);
position: absolute;
border-radius: 50%;
background-image: radial-gradient(circle, rgba(151, 206, 0, 1) 0%, rgba(151, 206, 0, 0) 100%);
}
.OT_audio-level-meter.OT_mode-off {
@ -848,31 +862,31 @@
.OT_audio-level-meter.OT_mode-on,
.OT_audio-only .OT_audio-level-meter.OT_mode-auto {
display: block;
display: block;
}
.OT_video-disabled-indicator {
opacity: 1;
border: none;
display: none;
position: absolute;
background-color: transparent;
background-repeat: no-repeat;
background-position:bottom right;
top: 0;
left: 0;
bottom: 3px;
right: 3px;
opacity: 1;
border: none;
display: none;
position: absolute;
background-color: transparent;
background-repeat: no-repeat;
background-position: bottom right;
top: 0;
left: 0;
bottom: 3px;
right: 3px;
}
.OT_video-disabled {
background-image: url(../images/rtc/video-disabled.png);
background-image: url(../images/rtc/video-disabled.png);
}
.OT_video-disabled-warning {
background-image: url(../images/rtc/video-disabled-warning.png);
background-image: url(../images/rtc/video-disabled-warning.png);
}
.OT_video-disabled-indicator.OT_active {
display: block;
display: block;
}

View File

@ -1,7 +1,7 @@
/*
Copyright (c) 2014 TokBox, Inc.
Released under the MIT license
http://opensource.org/licenses/MIT
*/
/**
* @license
* Copyright (c) 2014 TokBox, Inc.
* Released under the MIT license
* http://opensource.org/licenses/MIT
*/
!function(){TB.Config.replaceWith({global:{exceptionLogging:{enabled:!0,messageLimitPerPartner:100},iceServers:{enabled:!1},instrumentation:{enabled:!1,debugging:!1},tokshow:{textchat:!0}},partners:{change878:{instrumentation:{enabled:!0,debugging:!0}}}})}(TB);

File diff suppressed because it is too large Load Diff

View File

@ -224,7 +224,9 @@ loop.standaloneRoomViews = (function(mozL10n) {
* @private
*/
_onActiveRoomStateChanged: function() {
this.setState(this.props.activeRoomStore.getStoreState());
var state = this.props.activeRoomStore.getStoreState();
this.updateVideoDimensions(state.localVideoDimensions, state.remoteVideoDimensions);
this.setState(state);
},
componentDidMount: function() {
@ -283,6 +285,41 @@ 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) {
var node = this._getElement(".local");
var parent = node.offsetParent || this._getElement(".media");
// The local camera view should be a sixth of the size of its offset parent
// and positioned to overlap with the remote stream at a quarter of its width.
var parentWidth = parent.offsetWidth;
var targetWidth = parentWidth / 6;
node.style.right = "auto";
if (window.matchMedia && window.matchMedia("screen and (max-width:640px)").matches) {
targetWidth = 180;
node.style.left = "auto";
} else {
// Now position the local camera view correctly with respect to the remote
// video stream.
var remoteVideoDimensions = this.getRemoteVideoDimensions();
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 - ((targetWidth * ratio.height) / 4)) + "px";
}
node.style.width = (targetWidth * ratio.width) + "px";
node.style.height = (targetWidth * ratio.height) + "px";
},
/**
* Checks if current room is active.
*

View File

@ -224,7 +224,9 @@ loop.standaloneRoomViews = (function(mozL10n) {
* @private
*/
_onActiveRoomStateChanged: function() {
this.setState(this.props.activeRoomStore.getStoreState());
var state = this.props.activeRoomStore.getStoreState();
this.updateVideoDimensions(state.localVideoDimensions, state.remoteVideoDimensions);
this.setState(state);
},
componentDidMount: function() {
@ -283,6 +285,41 @@ 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) {
var node = this._getElement(".local");
var parent = node.offsetParent || this._getElement(".media");
// The local camera view should be a sixth of the size of its offset parent
// and positioned to overlap with the remote stream at a quarter of its width.
var parentWidth = parent.offsetWidth;
var targetWidth = parentWidth / 6;
node.style.right = "auto";
if (window.matchMedia && window.matchMedia("screen and (max-width:640px)").matches) {
targetWidth = 180;
node.style.left = "auto";
} else {
// Now position the local camera view correctly with respect to the remote
// video stream.
var remoteVideoDimensions = this.getRemoteVideoDimensions();
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 - ((targetWidth * ratio.height) / 4)) + "px";
}
node.style.width = (targetWidth * ratio.width) + "px";
node.style.height = (targetWidth * ratio.height) + "px";
},
/**
* Checks if current room is active.
*

View File

@ -68,7 +68,9 @@ describe("loop.roomViews", function () {
videoMuted: false,
failureReason: undefined,
used: false,
foo: "bar"
foo: "bar",
localVideoDimensions: {},
remoteVideoDimensions: {}
});
});

View File

@ -129,7 +129,7 @@ class Test1BrowserCall(MarionetteTestCase):
def check_remote_video(self):
video_wrapper = self.wait_for_element_displayed(
By.CSS_SELECTOR,
".media .OT_subscriber .OT_video-container", 20)
".media .OT_subscriber .OT_widget-container", 20)
video = self.wait_for_subelement_displayed(
video_wrapper, By.TAG_NAME, "video")

View File

@ -282,6 +282,31 @@ describe("loop.store.ActiveRoomStore", function () {
});
});
describe("#videoDimensionsChanged", function() {
it("should not contain any video dimensions at the very start", function() {
expect(store.getStoreState()).eql(store.getInitialStoreState());
});
it("should update the store with new video dimensions", function() {
var actionData = {
isLocal: true,
videoType: "camera",
dimensions: { width: 640, height: 480 }
};
store.videoDimensionsChanged(new sharedActions.VideoDimensionsChanged(actionData));
expect(store.getStoreState().localVideoDimensions)
.to.have.property(actionData.videoType, actionData.dimensions);
actionData.isLocal = false;
store.videoDimensionsChanged(new sharedActions.VideoDimensionsChanged(actionData));
expect(store.getStoreState().remoteVideoDimensions)
.to.have.property(actionData.videoType, actionData.dimensions);
});
});
describe("#setupRoomInfo", function() {
var fakeRoomInfo;

View File

@ -204,8 +204,16 @@ describe("loop.shared.mixins", function() {
}
});
sandbox.useFakeTimers();
rootObject = {
events: {},
setTimeout: function(func, timeout) {
return setTimeout(func, timeout);
},
clearTimeout: function(timer) {
return clearTimeout(timer);
},
addEventListener: function(eventName, listener) {
this.events[eventName] = listener;
},
@ -244,20 +252,26 @@ describe("loop.shared.mixins", function() {
describe("resize", function() {
it("should update the width on the local stream element", function() {
localElement = {
offsetWidth: 100,
offsetHeight: 100,
style: { width: "0%" }
};
rootObject.events.resize();
sandbox.clock.tick(10);
expect(localElement.style.width).eql("100%");
});
it("should update the height on the remote stream element", function() {
remoteElement = {
offsetWidth: 100,
offsetHeight: 100,
style: { height: "0%" }
};
rootObject.events.resize();
sandbox.clock.tick(10);
expect(remoteElement.style.height).eql("100%");
});
@ -266,24 +280,81 @@ describe("loop.shared.mixins", function() {
describe("orientationchange", function() {
it("should update the width on the local stream element", function() {
localElement = {
offsetWidth: 100,
offsetHeight: 100,
style: { width: "0%" }
};
rootObject.events.orientationchange();
sandbox.clock.tick(10);
expect(localElement.style.width).eql("100%");
});
it("should update the height on the remote stream element", function() {
remoteElement = {
offsetWidth: 100,
offsetHeight: 100,
style: { height: "0%" }
};
rootObject.events.orientationchange();
sandbox.clock.tick(10);
expect(remoteElement.style.height).eql("100%");
});
});
describe("Video stream dimensions", function() {
var localVideoDimensions = {
camera: {
width: 640,
height: 480
}
};
var remoteVideoDimensions = {
camera: {
width: 420,
height: 138
}
};
beforeEach(function() {
view.updateVideoDimensions(localVideoDimensions, remoteVideoDimensions);
});
it("should register video dimension updates correctly", function() {
expect(view._videoDimensionsCache.local.camera.width)
.eql(localVideoDimensions.camera.width);
expect(view._videoDimensionsCache.local.camera.height)
.eql(localVideoDimensions.camera.height);
expect(view._videoDimensionsCache.local.camera.aspectRatio.width).eql(1);
expect(view._videoDimensionsCache.local.camera.aspectRatio.height).eql(0.75);
expect(view._videoDimensionsCache.remote.camera.width)
.eql(remoteVideoDimensions.camera.width);
expect(view._videoDimensionsCache.remote.camera.height)
.eql(remoteVideoDimensions.camera.height);
expect(view._videoDimensionsCache.remote.camera.aspectRatio.width).eql(1);
expect(view._videoDimensionsCache.remote.camera.aspectRatio.height)
.eql(0.32857142857142857);
});
it("should fetch remote video stream dimensions correctly", function() {
remoteElement = {
offsetWidth: 600,
offsetHeight: 320
};
var remoteVideoDimensions = view.getRemoteVideoDimensions();
expect(remoteVideoDimensions.width).eql(remoteElement.offsetWidth);
expect(remoteVideoDimensions.height).eql(remoteElement.offsetHeight);
expect(remoteVideoDimensions.streamWidth).eql(534.8571428571429);
expect(remoteVideoDimensions.streamHeight).eql(remoteElement.offsetHeight);
expect(remoteVideoDimensions.offsetX).eql(32.571428571428555);
expect(remoteVideoDimensions.offsetY).eql(0);
});
});
});
});

View File

@ -8,6 +8,7 @@ describe("loop.OTSdkDriver", function () {
var sharedActions = loop.shared.actions;
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
var STREAM_PROPERTIES = loop.shared.utils.STREAM_PROPERTIES;
var sandbox;
var dispatcher, driver, publisher, sdk, session, sessionData;
var fakeLocalElement, fakeRemoteElement, publisherConfig, fakeEvent;
@ -310,6 +311,44 @@ describe("loop.OTSdkDriver", function () {
});
});
describe("streamPropertyChanged", function() {
var fakeStream = {
connection: { id: "fake" },
videoType: "screen",
videoDimensions: {
width: 320,
height: 160
}
};
it("should not dispatch a VideoDimensionsChanged action for other properties", function() {
session.trigger("streamPropertyChanged", {
stream: fakeStream,
changedProperty: STREAM_PROPERTIES.HAS_AUDIO
});
session.trigger("streamPropertyChanged", {
stream: fakeStream,
changedProperty: STREAM_PROPERTIES.HAS_VIDEO
});
sinon.assert.notCalled(dispatcher.dispatch);
});
it("should dispatch a VideoDimensionsChanged action", function() {
session.connection = {
id: "localUser"
};
session.trigger("streamPropertyChanged", {
stream: fakeStream,
changedProperty: STREAM_PROPERTIES.VIDEO_DIMENSIONS
});
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithMatch(dispatcher.dispatch,
sinon.match.hasOwn("name", "videoDimensionsChanged"))
})
});
describe("connectionCreated", function() {
beforeEach(function() {
session.connection = {

View File

@ -24,7 +24,7 @@ add_task(function smart_bookmarks_disabled() {
let smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
do_check_eq(smartBookmarkItemIds.length, 0);
do_log_info("check that pref has not been bumped up");
do_print("check that pref has not been bumped up");
do_check_eq(Services.prefs.getIntPref("browser.places.smartBookmarksVersion"), -1);
});
@ -34,7 +34,7 @@ add_task(function create_smart_bookmarks() {
let smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
do_check_neq(smartBookmarkItemIds.length, 0);
do_log_info("check that pref has been bumped up");
do_print("check that pref has been bumped up");
do_check_true(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
});
@ -42,14 +42,14 @@ add_task(function remove_smart_bookmark_and_restore() {
let smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
let smartBookmarksCount = smartBookmarkItemIds.length;
do_log_info("remove one smart bookmark and restore");
do_print("remove one smart bookmark and restore");
PlacesUtils.bookmarks.removeItem(smartBookmarkItemIds[0]);
Services.prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
gluesvc.ensurePlacesDefaultQueriesInitialized();
smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
do_check_eq(smartBookmarkItemIds.length, smartBookmarksCount);
do_log_info("check that pref has been bumped up");
do_print("check that pref has been bumped up");
do_check_true(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
});
@ -57,7 +57,7 @@ add_task(function move_smart_bookmark_rename_and_restore() {
let smartBookmarkItemIds =
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO);
let smartBookmarksCount = smartBookmarkItemIds.length;
do_log_info("smart bookmark should be restored in place");
do_print("smart bookmark should be restored in place");
let parent = PlacesUtils.bookmarks.getFolderIdForItem(smartBookmarkItemIds[0]);
let oldTitle = PlacesUtils.bookmarks.getItemTitle(smartBookmarkItemIds[0]);
// create a subfolder and move inside it
@ -76,6 +76,6 @@ add_task(function move_smart_bookmark_rename_and_restore() {
do_check_eq(smartBookmarkItemIds.length, smartBookmarksCount);
do_check_eq(PlacesUtils.bookmarks.getFolderIdForItem(smartBookmarkItemIds[0]), newParent);
do_check_eq(PlacesUtils.bookmarks.getItemTitle(smartBookmarkItemIds[0]), oldTitle);
do_log_info("check that pref has been bumped up");
do_print("check that pref has been bumped up");
do_check_true(Services.prefs.getIntPref("browser.places.smartBookmarksVersion") > 0);
});

View File

@ -63,7 +63,7 @@ function waitForImportAndSmartBookmarks(aCallback) {
function test_import()
{
do_log_info("Import from bookmarks.html if importBookmarksHTML is true.");
do_print("Import from bookmarks.html if importBookmarksHTML is true.");
remove_all_bookmarks();
// Sanity check: we should not have any bookmark on the toolbar.
@ -86,7 +86,7 @@ function waitForImportAndSmartBookmarks(aCallback) {
run_next_test();
});
// Force nsBrowserGlue::_initPlaces().
do_log_info("Simulate Places init");
do_print("Simulate Places init");
bg.QueryInterface(Ci.nsIObserver).observe(null,
TOPIC_BROWSERGLUE_TEST,
TOPICDATA_FORCE_PLACES_INIT);
@ -94,8 +94,8 @@ function waitForImportAndSmartBookmarks(aCallback) {
function test_import_noSmartBookmarks()
{
do_log_info("import from bookmarks.html, but don't create smart bookmarks \
if they are disabled");
do_print("import from bookmarks.html, but don't create smart bookmarks \
if they are disabled");
remove_all_bookmarks();
// Sanity check: we should not have any bookmark on the toolbar.
@ -119,7 +119,7 @@ function waitForImportAndSmartBookmarks(aCallback) {
run_next_test();
});
// Force nsBrowserGlue::_initPlaces().
do_log_info("Simulate Places init");
do_print("Simulate Places init");
bg.QueryInterface(Ci.nsIObserver).observe(null,
TOPIC_BROWSERGLUE_TEST,
TOPICDATA_FORCE_PLACES_INIT);
@ -127,8 +127,8 @@ function waitForImportAndSmartBookmarks(aCallback) {
function test_import_autoExport_updatedSmartBookmarks()
{
do_log_info("Import from bookmarks.html, but don't create smart bookmarks \
if autoExportHTML is true and they are at latest version");
do_print("Import from bookmarks.html, but don't create smart bookmarks \
if autoExportHTML is true and they are at latest version");
remove_all_bookmarks();
// Sanity check: we should not have any bookmark on the toolbar.
@ -154,7 +154,7 @@ function waitForImportAndSmartBookmarks(aCallback) {
run_next_test();
});
// Force nsBrowserGlue::_initPlaces()
do_log_info("Simulate Places init");
do_print("Simulate Places init");
bg.QueryInterface(Ci.nsIObserver).observe(null,
TOPIC_BROWSERGLUE_TEST,
TOPICDATA_FORCE_PLACES_INIT);
@ -162,8 +162,8 @@ function waitForImportAndSmartBookmarks(aCallback) {
function test_import_autoExport_oldSmartBookmarks()
{
do_log_info("Import from bookmarks.html, and create smart bookmarks if \
autoExportHTML is true and they are not at latest version.");
do_print("Import from bookmarks.html, and create smart bookmarks if \
autoExportHTML is true and they are not at latest version.");
remove_all_bookmarks();
// Sanity check: we should not have any bookmark on the toolbar.
@ -190,7 +190,7 @@ function waitForImportAndSmartBookmarks(aCallback) {
run_next_test();
});
// Force nsBrowserGlue::_initPlaces()
do_log_info("Simulate Places init");
do_print("Simulate Places init");
bg.QueryInterface(Ci.nsIObserver).observe(null,
TOPIC_BROWSERGLUE_TEST,
TOPICDATA_FORCE_PLACES_INIT);
@ -198,8 +198,8 @@ function waitForImportAndSmartBookmarks(aCallback) {
function test_restore()
{
do_log_info("restore from default bookmarks.html if \
restore_default_bookmarks is true.");
do_print("restore from default bookmarks.html if \
restore_default_bookmarks is true.");
remove_all_bookmarks();
// Sanity check: we should not have any bookmark on the toolbar.
@ -222,7 +222,7 @@ function waitForImportAndSmartBookmarks(aCallback) {
run_next_test();
});
// Force nsBrowserGlue::_initPlaces()
do_log_info("Simulate Places init");
do_print("Simulate Places init");
bg.QueryInterface(Ci.nsIObserver).observe(null,
TOPIC_BROWSERGLUE_TEST,
TOPICDATA_FORCE_PLACES_INIT);
@ -231,8 +231,8 @@ function waitForImportAndSmartBookmarks(aCallback) {
function test_restore_import()
{
do_log_info("setting both importBookmarksHTML and \
restore_default_bookmarks should restore defaults.");
do_print("setting both importBookmarksHTML and \
restore_default_bookmarks should restore defaults.");
remove_all_bookmarks();
// Sanity check: we should not have any bookmark on the toolbar.
@ -257,7 +257,7 @@ function waitForImportAndSmartBookmarks(aCallback) {
run_next_test();
});
// Force nsBrowserGlue::_initPlaces()
do_log_info("Simulate Places init");
do_print("Simulate Places init");
bg.QueryInterface(Ci.nsIObserver).observe(null,
TOPIC_BROWSERGLUE_TEST,
TOPICDATA_FORCE_PLACES_INIT);

View File

@ -38,7 +38,7 @@ function setupBehaviorAndMigrate(aDefaultBehavior, aAutocompleteEnabled = true)
};
add_task(function*() {
do_log_info("Migrate default.behavior = 0");
do_print("Migrate default.behavior = 0");
setupBehaviorAndMigrate(0);
Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
@ -52,7 +52,7 @@ add_task(function*() {
});
add_task(function*() {
do_log_info("Migrate default.behavior = 1");
do_print("Migrate default.behavior = 1");
setupBehaviorAndMigrate(1);
Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
@ -66,7 +66,7 @@ add_task(function*() {
});
add_task(function*() {
do_log_info("Migrate default.behavior = 2");
do_print("Migrate default.behavior = 2");
setupBehaviorAndMigrate(2);
Assert.equal(gGetBoolPref("browser.urlbar.suggest.history"), false,
@ -80,7 +80,7 @@ add_task(function*() {
});
add_task(function*() {
do_log_info("Migrate default.behavior = 3");
do_print("Migrate default.behavior = 3");
setupBehaviorAndMigrate(3);
Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
@ -94,7 +94,7 @@ add_task(function*() {
});
add_task(function*() {
do_log_info("Migrate default.behavior = 19");
do_print("Migrate default.behavior = 19");
setupBehaviorAndMigrate(19);
Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
@ -108,7 +108,7 @@ add_task(function*() {
});
add_task(function*() {
do_log_info("Migrate default.behavior = 33");
do_print("Migrate default.behavior = 33");
setupBehaviorAndMigrate(33);
Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
@ -122,7 +122,7 @@ add_task(function*() {
});
add_task(function*() {
do_log_info("Migrate default.behavior = 129");
do_print("Migrate default.behavior = 129");
setupBehaviorAndMigrate(129);
Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
@ -136,7 +136,7 @@ add_task(function*() {
});
add_task(function*() {
do_log_info("Migrate default.behavior = 0, autocomplete.enabled = false");
do_print("Migrate default.behavior = 0, autocomplete.enabled = false");
setupBehaviorAndMigrate(0, false);
Assert.equal(gGetBoolPref("browser.urlbar.suggest.history"), false,

View File

@ -6,6 +6,15 @@
const PAGE_1 = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page.";
const PAGE_2 = "data:text/html,<html><body>Another%20regular,%20everyday,%20normal%20page.";
// Turn off tab animations for testing
Services.prefs.setBoolPref("browser.tabs.animate", false);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("browser.tabs.animate");
});
// Allow tabs to restore on demand so we can test pending states
Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
/**
* Returns a Promise that resolves once a remote <xul:browser> has experienced
* a crash. Also does the job of cleaning up the minidump of the crash.
@ -58,6 +67,7 @@ function crashBrowser(browser) {
}
Services.obs.removeObserver(observer, 'ipc:content-shutdown');
info("Crash cleaned up");
resolve();
};
@ -67,6 +77,7 @@ function crashBrowser(browser) {
let aboutTabCrashedLoadPromise = new Promise((resolve, reject) => {
browser.addEventListener("AboutTabCrashedLoad", function onCrash() {
browser.removeEventListener("AboutTabCrashedLoad", onCrash, false);
info("about:tabcrashed loaded");
resolve();
}, false, true);
});
@ -75,7 +86,22 @@ function crashBrowser(browser) {
// evaluated.
let mm = browser.messageManager;
mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", false);
return Promise.all([crashCleanupPromise, aboutTabCrashedLoadPromise]);
return Promise.all([crashCleanupPromise, aboutTabCrashedLoadPromise]).then(() => {
let tab = gBrowser.getTabForBrowser(browser);
is(tab.getAttribute("crashed"), "true", "Tab should be marked as crashed");
});
}
function clickButton(browser, id) {
info("Clicking " + id);
let frame_script = (id) => {
let button = content.document.getElementById(id);
button.click();
};
let mm = browser.messageManager;
mm.loadFrameScript("data:,(" + frame_script.toString() + ")('" + id + "');", false);
}
/**
@ -194,8 +220,6 @@ add_task(function test_crash_page_not_in_history() {
// Crash the tab
yield crashBrowser(browser);
// Flush out any notifications from the crashed browser.
TabState.flush(browser);
// Check the tab state and make sure the tab crashed page isn't
// mentioned.
@ -225,13 +249,12 @@ add_task(function test_revived_history_from_remote() {
// Crash the tab
yield crashBrowser(browser);
// Flush out any notifications from the crashed browser.
TabState.flush(browser);
// Browse to a new site that will cause the browser to
// become remote again.
browser.loadURI(PAGE_2);
yield promiseTabRestored(newTab);
ok(!newTab.hasAttribute("crashed"), "Tab shouldn't be marked as crashed anymore.");
ok(browser.isRemoteBrowser, "Should be a remote browser");
TabState.flush(browser);
@ -265,13 +288,12 @@ add_task(function test_revived_history_from_non_remote() {
// Crash the tab
yield crashBrowser(browser);
// Flush out any notifications from the crashed browser.
TabState.flush(browser);
// Browse to a new site that will not cause the browser to
// become remote again.
browser.loadURI("about:mozilla");
yield promiseBrowserLoaded(browser);
ok(!newTab.hasAttribute("crashed"), "Tab shouldn't be marked as crashed anymore.");
ok(!browser.isRemoteBrowser, "Should not be a remote browser");
TabState.flush(browser);
@ -301,6 +323,14 @@ add_task(function test_revive_tab_from_session_store() {
browser.loadURI(PAGE_1);
yield promiseBrowserLoaded(browser);
let newTab2 = gBrowser.addTab();
let browser2 = newTab2.linkedBrowser;
ok(browser2.isRemoteBrowser, "Should be a remote browser");
yield promiseBrowserLoaded(browser2);
browser.loadURI(PAGE_1);
yield promiseBrowserLoaded(browser);
browser.loadURI(PAGE_2);
yield promiseBrowserLoaded(browser);
@ -308,12 +338,13 @@ add_task(function test_revive_tab_from_session_store() {
// Crash the tab
yield crashBrowser(browser);
// Flush out any notifications from the crashed browser.
TabState.flush(browser);
is(newTab2.getAttribute("crashed"), "true", "Second tab should be crashed too.");
// Use SessionStore to revive the tab
SessionStore.reviveCrashedTab(newTab);
yield promiseBrowserLoaded(browser);
clickButton(browser, "restoreTab");
yield promiseTabRestored(newTab);
ok(!newTab.hasAttribute("crashed"), "Tab shouldn't be marked as crashed anymore.");
is(newTab2.getAttribute("crashed"), "true", "Second tab should still be crashed though.");
// We can't just check browser.currentURI.spec, because from
// the outside, a crashed tab has the same URI as the page
@ -325,4 +356,90 @@ add_task(function test_revive_tab_from_session_store() {
yield promiseHistoryLength(browser, 2);
gBrowser.removeTab(newTab);
});
gBrowser.removeTab(newTab2);
});
/**
* Checks that we can revive a crashed tab back to the page that
* it was on when it crashed.
*/
add_task(function test_revive_all_tabs_from_session_store() {
let newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
let browser = newTab.linkedBrowser;
ok(browser.isRemoteBrowser, "Should be a remote browser");
yield promiseBrowserLoaded(browser);
browser.loadURI(PAGE_1);
yield promiseBrowserLoaded(browser);
let newTab2 = gBrowser.addTab(PAGE_1);
let browser2 = newTab2.linkedBrowser;
ok(browser2.isRemoteBrowser, "Should be a remote browser");
yield promiseBrowserLoaded(browser2);
browser.loadURI(PAGE_1);
yield promiseBrowserLoaded(browser);
browser.loadURI(PAGE_2);
yield promiseBrowserLoaded(browser);
TabState.flush(browser);
TabState.flush(browser2);
// Crash the tab
yield crashBrowser(browser);
is(newTab2.getAttribute("crashed"), "true", "Second tab should be crashed too.");
// Use SessionStore to revive all the tabs
clickButton(browser, "restoreAll");
yield promiseTabRestored(newTab);
ok(!newTab.hasAttribute("crashed"), "Tab shouldn't be marked as crashed anymore.");
ok(!newTab.hasAttribute("pending"), "Tab shouldn't be pending.");
ok(!newTab2.hasAttribute("crashed"), "Second tab shouldn't be marked as crashed anymore.");
ok(newTab2.hasAttribute("pending"), "Second tab should be pending.");
gBrowser.selectedTab = newTab2;
yield promiseTabRestored(newTab2);
ok(!newTab2.hasAttribute("pending"), "Second tab shouldn't be pending.");
// We can't just check browser.currentURI.spec, because from
// the outside, a crashed tab has the same URI as the page
// it crashed on (much like an about:neterror page). Instead,
// we have to use the documentURI on the content.
yield promiseContentDocumentURIEquals(browser, PAGE_2);
yield promiseContentDocumentURIEquals(browser2, PAGE_1);
// We should also have two entries in the browser history.
yield promiseHistoryLength(browser, 2);
gBrowser.removeTab(newTab);
gBrowser.removeTab(newTab2);
});
/**
* Checks that about:tabcrashed can close the current tab
*/
add_task(function test_close_tab_after_crash() {
let newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
let browser = newTab.linkedBrowser;
ok(browser.isRemoteBrowser, "Should be a remote browser");
yield promiseBrowserLoaded(browser);
browser.loadURI(PAGE_1);
yield promiseBrowserLoaded(browser);
TabState.flush(browser);
// Crash the tab
yield crashBrowser(browser);
let promise = promiseEvent(gBrowser.tabContainer, "TabClose");
// Click the close tab button
clickButton(browser, "closeTab");
yield promise;
is(gBrowser.tabs.length, 1, "Should have closed the tab");
});

View File

@ -488,15 +488,19 @@ function promiseDelayedStartupFinished(aWindow) {
return new Promise(resolve => whenDelayedStartupFinished(aWindow, resolve));
}
function promiseTabRestored(tab) {
function promiseEvent(element, eventType, isCapturing = false) {
return new Promise(resolve => {
tab.addEventListener("SSTabRestored", function onRestored() {
tab.removeEventListener("SSTabRestored", onRestored);
resolve();
});
element.addEventListener(eventType, function listener(event) {
element.removeEventListener(eventType, listener, isCapturing);
resolve(event);
}, isCapturing);
});
}
function promiseTabRestored(tab) {
return promiseEvent(tab, "SSTabRestored");
}
function sendMessage(browser, name, data = {}) {
browser.messageManager.sendAsyncMessage(name, data);
return promiseContentMessage(browser, name);

View File

@ -16,6 +16,16 @@ function test() {
UITourTest();
}
function runOffline(fun) {
return (done) => {
Services.io.offline = true;
fun(function onComplete() {
Services.io.offline = false;
done();
});
}
}
let tests = [
taskify(function* test_menu_show_hide() {
ise(loopButton.open, false, "Menu should initially be closed");
@ -94,7 +104,7 @@ let tests = [
});
});
},
function test_notifyLoopChatWindowOpenedClosed(done) {
runOffline(function test_notifyLoopChatWindowOpenedClosed(done) {
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowOpened", "Check Loop:ChatWindowOpened notification");
gContentAPI.observe((event, params) => {
@ -110,8 +120,8 @@ let tests = [
document.querySelector("#pinnedchats > chatbox").close();
});
LoopRooms.open("fakeTourRoom");
},
function test_notifyLoopRoomURLCopied(done) {
}),
runOffline(function test_notifyLoopRoomURLCopied(done) {
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowOpened", "Loop chat window should've opened");
gContentAPI.observe((event, params) => {
@ -131,8 +141,8 @@ let tests = [
});
setupFakeRoom();
LoopRooms.open("fakeTourRoom");
},
function test_notifyLoopRoomURLEmailed(done) {
}),
runOffline(function test_notifyLoopRoomURLEmailed(done) {
gContentAPI.observe((event, params) => {
is(event, "Loop:ChatWindowOpened", "Loop chat window should've opened");
gContentAPI.observe((event, params) => {
@ -162,7 +172,7 @@ let tests = [
});
});
LoopRooms.open("fakeTourRoom");
},
}),
taskify(function* test_arrow_panel_position() {
ise(loopButton.open, false, "Menu should initially be closed");
let popup = document.getElementById("UITourTooltip");
@ -274,6 +284,7 @@ if (Services.prefs.getBoolPref("loop.enabled")) {
Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
Services.prefs.clearUserPref("loop.gettingStarted.seen");
Services.prefs.clearUserPref("loop.gettingStarted.url");
Services.io.offline = false;
// Copied from browser/components/loop/test/mochitest/head.js
// Remove the iframe after each test. This also avoids mochitest complaining

View File

@ -1,5 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
PREF_JS_EXPORTS = $(srcdir)/webide-prefs.js

View File

@ -23,3 +23,8 @@ EXTRA_JS_MODULES.devtools.webide += [
'modules/tab-store.js',
'modules/utils.js'
]
JS_PREFERENCE_FILES += [
'webide-prefs.js',
]

View File

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

View File

@ -349,7 +349,11 @@ ChromeActions.prototype = {
return (!!prefBrowser && prefGfx);
},
supportsDocumentColors: function() {
return getBoolPref('browser.display.use_document_colors', true);
if (getIntPref('browser.display.document_color_use', 0) === 2 ||
!getBoolPref('browser.display.use_document_colors', true)) {
return false;
}
return true;
},
reportTelemetry: function (data) {
var probeInfo = JSON.parse(data);

View File

@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') {
(typeof window !== 'undefined' ? window : this).PDFJS = {};
}
PDFJS.version = '1.0.1040';
PDFJS.build = '997096f';
PDFJS.version = '1.0.1130';
PDFJS.build = 'e4f0ae2';
(function pdfjsWrapper() {
// Use strict in our context only - users might not want it
@ -341,6 +341,7 @@ function isValidUrl(url, allowRelative) {
case 'https':
case 'ftp':
case 'mailto':
case 'tel':
return true;
default:
return false;
@ -470,6 +471,8 @@ var XRefParseException = (function XRefParseExceptionClosure() {
function bytesToString(bytes) {
assert(bytes !== null && typeof bytes === 'object' &&
bytes.length !== undefined, 'Invalid argument for bytesToString');
var length = bytes.length;
var MAX_ARGUMENT_COUNT = 8192;
if (length < MAX_ARGUMENT_COUNT) {
@ -485,6 +488,7 @@ function bytesToString(bytes) {
}
function stringToBytes(str) {
assert(typeof str === 'string', 'Invalid argument for stringToBytes');
var length = str.length;
var bytes = new Uint8Array(length);
for (var i = 0; i < length; ++i) {
@ -1374,6 +1378,9 @@ PDFJS.disableStream = (PDFJS.disableStream === undefined ?
* Disable pre-fetching of PDF file data. When range requests are enabled PDF.js
* will automatically keep fetching more data even if it isn't needed to display
* the current page. This default behavior can be disabled.
*
* NOTE: It is also necessary to disable streaming, see above,
* in order for disabling of pre-fetching to work correctly.
* @var {boolean}
*/
PDFJS.disableAutoFetch = (PDFJS.disableAutoFetch === undefined ?
@ -1437,7 +1444,9 @@ PDFJS.maxCanvasPixels = (PDFJS.maxCanvasPixels === undefined ?
*
* @typedef {Object} DocumentInitParameters
* @property {string} url - The URL of the PDF.
* @property {TypedArray} data - A typed array with PDF data.
* @property {TypedArray|Array|string} data - Binary PDF data. Use typed arrays
* (Uint8Array) to improve the memory usage. If PDF data is BASE64-encoded,
* use atob() to convert it to a binary string first.
* @property {Object} httpHeaders - Basic authentication headers.
* @property {boolean} withCredentials - Indicates whether or not cross-site
* Access-Control requests should be made using credentials such as cookies
@ -1446,6 +1455,9 @@ PDFJS.maxCanvasPixels = (PDFJS.maxCanvasPixels === undefined ?
* @property {TypedArray} initialData - A typed array with the first portion or
* all of the pdf data. Used by the extension since some data is already
* loaded before the switch to range requests.
* @property {number} length - The PDF file length. It's used for progress
* reports and range requests operations.
* @property {PDFDataRangeTransport} range
*/
/**
@ -1462,68 +1474,226 @@ PDFJS.maxCanvasPixels = (PDFJS.maxCanvasPixels === undefined ?
* is used, which means it must follow the same origin rules that any XHR does
* e.g. No cross domain requests without CORS.
*
* @param {string|TypedArray|DocumentInitParameters} source Can be a url to
* where a PDF is located, a typed array (Uint8Array) already populated with
* data or parameter object.
* @param {string|TypedArray|DocumentInitParameters|PDFDataRangeTransport} src
* Can be a url to where a PDF is located, a typed array (Uint8Array)
* already populated with data or parameter object.
*
* @param {Object} pdfDataRangeTransport is optional. It is used if you want
* to manually serve range requests for data in the PDF. See viewer.js for
* an example of pdfDataRangeTransport's interface.
* @param {PDFDataRangeTransport} pdfDataRangeTransport (deprecated) It is used
* if you want to manually serve range requests for data in the PDF.
*
* @param {function} passwordCallback is optional. It is used to request a
* @param {function} passwordCallback (deprecated) It is used to request a
* password if wrong or no password was provided. The callback receives two
* parameters: function that needs to be called with new password and reason
* (see {PasswordResponses}).
*
* @param {function} progressCallback is optional. It is used to be able to
* @param {function} progressCallback (deprecated) It is used to be able to
* monitor the loading progress of the PDF file (necessary to implement e.g.
* a loading bar). The callback receives an {Object} with the properties:
* {number} loaded and {number} total.
*
* @return {Promise} A promise that is resolved with {@link PDFDocumentProxy}
* object.
* @return {PDFDocumentLoadingTask}
*/
PDFJS.getDocument = function getDocument(source,
PDFJS.getDocument = function getDocument(src,
pdfDataRangeTransport,
passwordCallback,
progressCallback) {
var workerInitializedCapability, workerReadyCapability, transport;
var task = new PDFDocumentLoadingTask();
if (typeof source === 'string') {
source = { url: source };
} else if (isArrayBuffer(source)) {
source = { data: source };
} else if (typeof source !== 'object') {
error('Invalid parameter in getDocument, need either Uint8Array, ' +
'string or a parameter object');
// Support of the obsolete arguments (for compatibility with API v1.0)
if (pdfDataRangeTransport) {
if (!(pdfDataRangeTransport instanceof PDFDataRangeTransport)) {
// Not a PDFDataRangeTransport instance, trying to add missing properties.
pdfDataRangeTransport = Object.create(pdfDataRangeTransport);
pdfDataRangeTransport.length = src.length;
pdfDataRangeTransport.initialData = src.initialData;
}
src = Object.create(src);
src.range = pdfDataRangeTransport;
}
task.onPassword = passwordCallback || null;
task.onProgress = progressCallback || null;
var workerInitializedCapability, transport;
var source;
if (typeof src === 'string') {
source = { url: src };
} else if (isArrayBuffer(src)) {
source = { data: src };
} else if (src instanceof PDFDataRangeTransport) {
source = { range: src };
} else {
if (typeof src !== 'object') {
error('Invalid parameter in getDocument, need either Uint8Array, ' +
'string or a parameter object');
}
if (!src.url && !src.data && !src.range) {
error('Invalid parameter object: need either .data, .range or .url');
}
source = src;
}
if (!source.url && !source.data) {
error('Invalid parameter array, need either .data or .url');
}
// copy/use all keys as is except 'url' -- full path is required
var params = {};
for (var key in source) {
if (key === 'url' && typeof window !== 'undefined') {
// The full path is required in the 'url' field.
params[key] = combineUrl(window.location.href, source[key]);
continue;
} else if (key === 'range') {
continue;
} else if (key === 'data' && !(source[key] instanceof Uint8Array)) {
// Converting string or array-like data to Uint8Array.
var pdfBytes = source[key];
if (typeof pdfBytes === 'string') {
params[key] = stringToBytes(pdfBytes);
} else if (typeof pdfBytes === 'object' && pdfBytes !== null &&
!isNaN(pdfBytes.length)) {
params[key] = new Uint8Array(pdfBytes);
} else {
error('Invalid PDF binary data: either typed array, string or ' +
'array-like object is expected in the data property.');
}
continue;
}
params[key] = source[key];
}
workerInitializedCapability = createPromiseCapability();
workerReadyCapability = createPromiseCapability();
transport = new WorkerTransport(workerInitializedCapability,
workerReadyCapability, pdfDataRangeTransport,
progressCallback);
transport = new WorkerTransport(workerInitializedCapability, source.range);
workerInitializedCapability.promise.then(function transportInitialized() {
transport.passwordCallback = passwordCallback;
transport.fetchDocument(params);
transport.fetchDocument(task, params);
});
return workerReadyCapability.promise;
return task;
};
/**
* PDF document loading operation.
* @class
*/
var PDFDocumentLoadingTask = (function PDFDocumentLoadingTaskClosure() {
/** @constructs PDFDocumentLoadingTask */
function PDFDocumentLoadingTask() {
this._capability = createPromiseCapability();
/**
* Callback to request a password if wrong or no password was provided.
* The callback receives two parameters: function that needs to be called
* with new password and reason (see {PasswordResponses}).
*/
this.onPassword = null;
/**
* Callback to be able to monitor the loading progress of the PDF file
* (necessary to implement e.g. a loading bar). The callback receives
* an {Object} with the properties: {number} loaded and {number} total.
*/
this.onProgress = null;
}
PDFDocumentLoadingTask.prototype =
/** @lends PDFDocumentLoadingTask.prototype */ {
/**
* @return {Promise}
*/
get promise() {
return this._capability.promise;
},
// TODO add cancel or abort method
/**
* Registers callbacks to indicate the document loading completion.
*
* @param {function} onFulfilled The callback for the loading completion.
* @param {function} onRejected The callback for the loading failure.
* @return {Promise} A promise that is resolved after the onFulfilled or
* onRejected callback.
*/
then: function PDFDocumentLoadingTask_then(onFulfilled, onRejected) {
return this.promise.then.apply(this.promise, arguments);
}
};
return PDFDocumentLoadingTask;
})();
/**
* Abstract class to support range requests file loading.
* @class
*/
var PDFDataRangeTransport = (function pdfDataRangeTransportClosure() {
/**
* @constructs PDFDataRangeTransport
* @param {number} length
* @param {Uint8Array} initialData
*/
function PDFDataRangeTransport(length, initialData) {
this.length = length;
this.initialData = initialData;
this._rangeListeners = [];
this._progressListeners = [];
this._progressiveReadListeners = [];
this._readyCapability = createPromiseCapability();
}
PDFDataRangeTransport.prototype =
/** @lends PDFDataRangeTransport.prototype */ {
addRangeListener:
function PDFDataRangeTransport_addRangeListener(listener) {
this._rangeListeners.push(listener);
},
addProgressListener:
function PDFDataRangeTransport_addProgressListener(listener) {
this._progressListeners.push(listener);
},
addProgressiveReadListener:
function PDFDataRangeTransport_addProgressiveReadListener(listener) {
this._progressiveReadListeners.push(listener);
},
onDataRange: function PDFDataRangeTransport_onDataRange(begin, chunk) {
var listeners = this._rangeListeners;
for (var i = 0, n = listeners.length; i < n; ++i) {
listeners[i](begin, chunk);
}
},
onDataProgress: function PDFDataRangeTransport_onDataProgress(loaded) {
this._readyCapability.promise.then(function () {
var listeners = this._progressListeners;
for (var i = 0, n = listeners.length; i < n; ++i) {
listeners[i](loaded);
}
}.bind(this));
},
onDataProgressiveRead:
function PDFDataRangeTransport_onDataProgress(chunk) {
this._readyCapability.promise.then(function () {
var listeners = this._progressiveReadListeners;
for (var i = 0, n = listeners.length; i < n; ++i) {
listeners[i](chunk);
}
}.bind(this));
},
transportReady: function PDFDataRangeTransport_transportReady() {
this._readyCapability.resolve();
},
requestDataRange:
function PDFDataRangeTransport_requestDataRange(begin, end) {
throw new Error('Abstract method PDFDataRangeTransport.requestDataRange');
}
};
return PDFDataRangeTransport;
})();
PDFJS.PDFDataRangeTransport = PDFDataRangeTransport;
/**
* Proxy to a PDFDocument in the worker thread. Also, contains commonly used
* properties that can be read synchronously.
@ -1639,7 +1809,7 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
return this.transport.downloadInfoCapability.promise;
},
/**
* @returns {Promise} A promise this is resolved with current stats about
* @return {Promise} A promise this is resolved with current stats about
* document structures (see {@link PDFDocumentStats}).
*/
getStats: function PDFDocumentProxy_getStats() {
@ -1703,7 +1873,7 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
* (default value is 'display').
* @property {Object} imageLayer - (optional) An object that has beginLayout,
* endLayout and appendImage functions.
* @property {function} continueCallback - (optional) A function that will be
* @property {function} continueCallback - (deprecated) A function that will be
* called each time the rendering is paused. To continue
* rendering call the function that is the first argument
* to the callback.
@ -1836,7 +2006,12 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
intentState.renderTasks = [];
}
intentState.renderTasks.push(internalRenderTask);
var renderTask = new RenderTask(internalRenderTask);
var renderTask = internalRenderTask.task;
// Obsolete parameter support
if (params.continueCallback) {
renderTask.onContinue = params.continueCallback;
}
var self = this;
intentState.displayReadyCapability.promise.then(
@ -2001,19 +2176,16 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
* @ignore
*/
var WorkerTransport = (function WorkerTransportClosure() {
function WorkerTransport(workerInitializedCapability, workerReadyCapability,
pdfDataRangeTransport, progressCallback) {
function WorkerTransport(workerInitializedCapability, pdfDataRangeTransport) {
this.pdfDataRangeTransport = pdfDataRangeTransport;
this.workerInitializedCapability = workerInitializedCapability;
this.workerReadyCapability = workerReadyCapability;
this.progressCallback = progressCallback;
this.commonObjs = new PDFObjects();
this.loadingTask = null;
this.pageCache = [];
this.pagePromises = [];
this.downloadInfoCapability = createPromiseCapability();
this.passwordCallback = null;
// If worker support isn't disabled explicit and the browser has worker
// support, create a new web worker and test if it/the browser fullfills
@ -2152,48 +2324,50 @@ var WorkerTransport = (function WorkerTransportClosure() {
this.numPages = data.pdfInfo.numPages;
var pdfDocument = new PDFDocumentProxy(pdfInfo, this);
this.pdfDocument = pdfDocument;
this.workerReadyCapability.resolve(pdfDocument);
this.loadingTask._capability.resolve(pdfDocument);
}, this);
messageHandler.on('NeedPassword',
function transportNeedPassword(exception) {
if (this.passwordCallback) {
return this.passwordCallback(updatePassword,
PasswordResponses.NEED_PASSWORD);
var loadingTask = this.loadingTask;
if (loadingTask.onPassword) {
return loadingTask.onPassword(updatePassword,
PasswordResponses.NEED_PASSWORD);
}
this.workerReadyCapability.reject(
loadingTask._capability.reject(
new PasswordException(exception.message, exception.code));
}, this);
messageHandler.on('IncorrectPassword',
function transportIncorrectPassword(exception) {
if (this.passwordCallback) {
return this.passwordCallback(updatePassword,
PasswordResponses.INCORRECT_PASSWORD);
var loadingTask = this.loadingTask;
if (loadingTask.onPassword) {
return loadingTask.onPassword(updatePassword,
PasswordResponses.INCORRECT_PASSWORD);
}
this.workerReadyCapability.reject(
loadingTask._capability.reject(
new PasswordException(exception.message, exception.code));
}, this);
messageHandler.on('InvalidPDF', function transportInvalidPDF(exception) {
this.workerReadyCapability.reject(
this.loadingTask._capability.reject(
new InvalidPDFException(exception.message));
}, this);
messageHandler.on('MissingPDF', function transportMissingPDF(exception) {
this.workerReadyCapability.reject(
this.loadingTask._capability.reject(
new MissingPDFException(exception.message));
}, this);
messageHandler.on('UnexpectedResponse',
function transportUnexpectedResponse(exception) {
this.workerReadyCapability.reject(
this.loadingTask._capability.reject(
new UnexpectedResponseException(exception.message, exception.status));
}, this);
messageHandler.on('UnknownError',
function transportUnknownError(exception) {
this.workerReadyCapability.reject(
this.loadingTask._capability.reject(
new UnknownErrorException(exception.message, exception.details));
}, this);
@ -2288,8 +2462,9 @@ var WorkerTransport = (function WorkerTransportClosure() {
}, this);
messageHandler.on('DocProgress', function transportDocProgress(data) {
if (this.progressCallback) {
this.progressCallback({
var loadingTask = this.loadingTask;
if (loadingTask.onProgress) {
loadingTask.onProgress({
loaded: data.loaded,
total: data.total
});
@ -2349,10 +2524,16 @@ var WorkerTransport = (function WorkerTransportClosure() {
});
},
fetchDocument: function WorkerTransport_fetchDocument(source) {
fetchDocument: function WorkerTransport_fetchDocument(loadingTask, source) {
this.loadingTask = loadingTask;
source.disableAutoFetch = PDFJS.disableAutoFetch;
source.disableStream = PDFJS.disableStream;
source.chunkedViewerLoading = !!this.pdfDataRangeTransport;
if (this.pdfDataRangeTransport) {
source.length = this.pdfDataRangeTransport.length;
source.initialData = this.pdfDataRangeTransport.initialData;
}
this.messageHandler.send('GetDocRequest', {
source: source,
disableRange: PDFJS.disableRange,
@ -2563,26 +2744,37 @@ var PDFObjects = (function PDFObjectsClosure() {
*/
var RenderTask = (function RenderTaskClosure() {
function RenderTask(internalRenderTask) {
this.internalRenderTask = internalRenderTask;
this._internalRenderTask = internalRenderTask;
/**
* Promise for rendering task completion.
* @type {Promise}
* Callback for incremental rendering -- a function that will be called
* each time the rendering is paused. To continue rendering call the
* function that is the first argument to the callback.
* @type {function}
*/
this.promise = this.internalRenderTask.capability.promise;
this.onContinue = null;
}
RenderTask.prototype = /** @lends RenderTask.prototype */ {
/**
* Promise for rendering task completion.
* @return {Promise}
*/
get promise() {
return this._internalRenderTask.capability.promise;
},
/**
* Cancels the rendering task. If the task is currently rendering it will
* not be cancelled until graphics pauses with a timeout. The promise that
* this object extends will resolved when cancelled.
*/
cancel: function RenderTask_cancel() {
this.internalRenderTask.cancel();
this._internalRenderTask.cancel();
},
/**
* Registers callback to indicate the rendering task completion.
* Registers callbacks to indicate the rendering task completion.
*
* @param {function} onFulfilled The callback for the rendering completion.
* @param {function} onRejected The callback for the rendering failure.
@ -2590,7 +2782,7 @@ var RenderTask = (function RenderTaskClosure() {
* onRejected callback.
*/
then: function RenderTask_then(onFulfilled, onRejected) {
return this.promise.then(onFulfilled, onRejected);
return this.promise.then.apply(this.promise, arguments);
}
};
@ -2617,6 +2809,7 @@ var InternalRenderTask = (function InternalRenderTaskClosure() {
this.graphicsReady = false;
this.cancelled = false;
this.capability = createPromiseCapability();
this.task = new RenderTask(this);
// caching this-bound methods
this._continueBound = this._continue.bind(this);
this._scheduleNextBound = this._scheduleNext.bind(this);
@ -2679,8 +2872,8 @@ var InternalRenderTask = (function InternalRenderTaskClosure() {
if (this.cancelled) {
return;
}
if (this.params.continueCallback) {
this.params.continueCallback(this._scheduleNextBound);
if (this.task.onContinue) {
this.task.onContinue.call(this.task, this._scheduleNextBound);
} else {
this._scheduleNext();
}

View File

@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') {
(typeof window !== 'undefined' ? window : this).PDFJS = {};
}
PDFJS.version = '1.0.1040';
PDFJS.build = '997096f';
PDFJS.version = '1.0.1130';
PDFJS.build = 'e4f0ae2';
(function pdfjsWrapper() {
// Use strict in our context only - users might not want it
@ -341,6 +341,7 @@ function isValidUrl(url, allowRelative) {
case 'https':
case 'ftp':
case 'mailto':
case 'tel':
return true;
default:
return false;
@ -470,6 +471,8 @@ var XRefParseException = (function XRefParseExceptionClosure() {
function bytesToString(bytes) {
assert(bytes !== null && typeof bytes === 'object' &&
bytes.length !== undefined, 'Invalid argument for bytesToString');
var length = bytes.length;
var MAX_ARGUMENT_COUNT = 8192;
if (length < MAX_ARGUMENT_COUNT) {
@ -485,6 +488,7 @@ function bytesToString(bytes) {
}
function stringToBytes(str) {
assert(typeof str === 'string', 'Invalid argument for stringToBytes');
var length = str.length;
var bytes = new Uint8Array(length);
for (var i = 0; i < length; ++i) {
@ -1449,6 +1453,9 @@ var ChunkedStream = (function ChunkedStreamClosure() {
getUint16: function ChunkedStream_getUint16() {
var b0 = this.getByte();
var b1 = this.getByte();
if (b0 === -1 || b1 === -1) {
return -1;
}
return (b0 << 8) + b1;
},
@ -14056,7 +14063,7 @@ var SpecialPUASymbols = {
'63731': 0x23A9, // braceleftbt (0xF8F3)
'63740': 0x23AB, // bracerighttp (0xF8FC)
'63741': 0x23AC, // bracerightmid (0xF8FD)
'63742': 0x23AD, // bracerightmid (0xF8FE)
'63742': 0x23AD, // bracerightbt (0xF8FE)
'63726': 0x23A1, // bracketlefttp (0xF8EE)
'63727': 0x23A2, // bracketleftex (0xF8EF)
'63728': 0x23A3, // bracketleftbt (0xF8F0)
@ -15996,7 +16003,7 @@ var Font = (function FontClosure() {
// to be used with the canvas.font.
var fontName = name.replace(/[,_]/g, '-');
var isStandardFont = !!stdFontMap[fontName] ||
(nonStdFontMap[fontName] && !!stdFontMap[nonStdFontMap[fontName]]);
!!(nonStdFontMap[fontName] && stdFontMap[nonStdFontMap[fontName]]);
fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName;
this.bold = (fontName.search(/bold/gi) !== -1);
@ -29595,6 +29602,102 @@ var Parser = (function ParserClosure() {
}
return ((stream.pos - 4) - startPos);
},
/**
* Find the EOI (end-of-image) marker 0xFFD9 of the stream.
* @returns {number} The inline stream length.
*/
findDCTDecodeInlineStreamEnd:
function Parser_findDCTDecodeInlineStreamEnd(stream) {
var startPos = stream.pos, foundEOI = false, b, markerLength, length;
while ((b = stream.getByte()) !== -1) {
if (b !== 0xFF) { // Not a valid marker.
continue;
}
switch (stream.getByte()) {
case 0x00: // Byte stuffing.
// 0xFF00 appears to be a very common byte sequence in JPEG images.
break;
case 0xFF: // Fill byte.
// Avoid skipping a valid marker, resetting the stream position.
stream.skip(-1);
break;
case 0xD9: // EOI
foundEOI = true;
break;
case 0xC0: // SOF0
case 0xC1: // SOF1
case 0xC2: // SOF2
case 0xC3: // SOF3
case 0xC5: // SOF5
case 0xC6: // SOF6
case 0xC7: // SOF7
case 0xC9: // SOF9
case 0xCA: // SOF10
case 0xCB: // SOF11
case 0xCD: // SOF13
case 0xCE: // SOF14
case 0xCF: // SOF15
case 0xC4: // DHT
case 0xCC: // DAC
case 0xDA: // SOS
case 0xDB: // DQT
case 0xDC: // DNL
case 0xDD: // DRI
case 0xDE: // DHP
case 0xDF: // EXP
case 0xE0: // APP0
case 0xE1: // APP1
case 0xE2: // APP2
case 0xE3: // APP3
case 0xE4: // APP4
case 0xE5: // APP5
case 0xE6: // APP6
case 0xE7: // APP7
case 0xE8: // APP8
case 0xE9: // APP9
case 0xEA: // APP10
case 0xEB: // APP11
case 0xEC: // APP12
case 0xED: // APP13
case 0xEE: // APP14
case 0xEF: // APP15
case 0xFE: // COM
// The marker should be followed by the length of the segment.
markerLength = stream.getUint16();
if (markerLength > 2) {
// |markerLength| contains the byte length of the marker segment,
// including its own length (2 bytes) and excluding the marker.
stream.skip(markerLength - 2); // Jump to the next marker.
} else {
// The marker length is invalid, resetting the stream position.
stream.skip(-2);
}
break;
}
if (foundEOI) {
break;
}
}
length = stream.pos - startPos;
if (b === -1) {
warn('Inline DCTDecode image stream: ' +
'EOI marker not found, searching for /EI/ instead.');
stream.skip(-length); // Reset the stream position.
return this.findDefaultInlineStreamEnd(stream);
}
this.inlineStreamSkipEI(stream);
return length;
},
/**
* Find the EOD (end-of-data) marker '~>' (i.e. TILDE + GT) of the stream.
* @returns {number} The inline stream length.
@ -29686,7 +29789,9 @@ var Parser = (function ParserClosure() {
// Parse image stream.
var startPos = stream.pos, length, i, ii;
if (filterName === 'ASCII85Decide' || filterName === 'A85') {
if (filterName === 'DCTDecode' || filterName === 'DCT') {
length = this.findDCTDecodeInlineStreamEnd(stream);
} else if (filterName === 'ASCII85Decide' || filterName === 'A85') {
length = this.findASCII85DecodeInlineStreamEnd(stream);
} else if (filterName === 'ASCIIHexDecode' || filterName === 'AHx') {
length = this.findASCIIHexDecodeInlineStreamEnd(stream);
@ -30613,6 +30718,9 @@ var Stream = (function StreamClosure() {
getUint16: function Stream_getUint16() {
var b0 = this.getByte();
var b1 = this.getByte();
if (b0 === -1 || b1 === -1) {
return -1;
}
return (b0 << 8) + b1;
},
getInt32: function Stream_getInt32() {
@ -30740,6 +30848,9 @@ var DecodeStream = (function DecodeStreamClosure() {
getUint16: function DecodeStream_getUint16() {
var b0 = this.getByte();
var b1 = this.getByte();
if (b0 === -1 || b1 === -1) {
return -1;
}
return (b0 << 8) + b1;
},
getInt32: function DecodeStream_getInt32() {
@ -34839,11 +34950,6 @@ var JpxImage = (function JpxImageClosure() {
context.QCC = [];
context.COC = [];
break;
case 0xFF55: // Tile-part lengths, main header (TLM)
var Ltlm = readUint16(data, position); // Marker segment length
// Skip tile length markers
position += Ltlm;
break;
case 0xFF5C: // Quantization default (QCD)
length = readUint16(data, position);
var qcd = {};
@ -35033,6 +35139,9 @@ var JpxImage = (function JpxImageClosure() {
length = tile.dataEnd - position;
parseTilePackets(context, data, position, length);
break;
case 0xFF55: // Tile-part lengths, main header (TLM)
case 0xFF57: // Packet length, main header (PLM)
case 0xFF58: // Packet length, tile-part header (PLT)
case 0xFF64: // Comment (COM)
length = readUint16(data, position);
// skipping content
@ -35373,7 +35482,7 @@ var JpxImage = (function JpxImageClosure() {
r = 0;
c = 0;
p = 0;
this.nextPacket = function JpxImage_nextPacket() {
// Section B.12.1.3 Resolution-position-component-layer
for (; r <= maxDecompositionLevelsCount; r++) {
@ -35457,7 +35566,7 @@ var JpxImage = (function JpxImageClosure() {
var componentsCount = siz.Csiz;
var precinctsSizes = getPrecinctSizesInImageScale(tile);
var l = 0, r = 0, c = 0, px = 0, py = 0;
this.nextPacket = function JpxImage_nextPacket() {
// Section B.12.1.5 Component-position-resolution-layer
for (; c < componentsCount; ++c) {
@ -37030,10 +37139,9 @@ var Jbig2Image = (function Jbig2ImageClosure() {
// At each pixel: Clear contextLabel pixels that are shifted
// out of the context, then add new ones.
// If j + n is out of range at the right image border, then
// the undefined value of bitmap[i - 2][j + n] is shifted to 0
contextLabel = ((contextLabel & OLD_PIXEL_MASK) << 1) |
(row2[j + 3] << 11) | (row1[j + 4] << 4) | pixel;
(j + 3 < width ? row2[j + 3] << 11 : 0) |
(j + 4 < width ? row1[j + 4] << 4 : 0) | pixel;
}
}

View File

@ -20,6 +20,7 @@
right: 0;
bottom: 0;
overflow: hidden;
opacity: 0.2;
}
.textLayer > div {
@ -55,6 +56,9 @@
background-color: rgb(0, 100, 0);
}
.textLayer ::selection { background: rgb(0,0,255); }
.textLayer ::-moz-selection { background: rgb(0,0,255); }
.pdfViewer .canvasWrapper {
overflow: hidden;
}
@ -1153,9 +1157,13 @@ html[dir='rtl'] .verticalToolbarSeparator {
margin-bottom: 10px;
}
#thumbnailView > a:last-of-type > .thumbnail:not([data-loaded]) {
margin-bottom: 9px;
}
.thumbnail:not([data-loaded]) {
border: 1px dashed rgba(255, 255, 255, 0.5);
margin-bottom: 10px;
margin: -1px -1px 4px -1px;
}
.thumbnailImage {
@ -1164,6 +1172,8 @@ html[dir='rtl'] .verticalToolbarSeparator {
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);
opacity: 0.8;
z-index: 99;
background-color: white;
background-clip: content-box;
}
.thumbnailSelectionRing {
@ -1294,19 +1304,12 @@ html[dir='rtl'] .attachmentsItem > button {
cursor: default;
}
/* TODO: file FF bug to support ::-moz-selection:window-inactive
so we can override the opaque grey background when the window is inactive;
see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */
::selection { background: rgba(0,0,255,0.3); }
::-moz-selection { background: rgba(0,0,255,0.3); }
.textLayer ::selection { background: rgb(0,0,255); }
.textLayer ::-moz-selection { background: rgb(0,0,255); }
.textLayer {
opacity: 0.2;
}
#errorWrapper {
background: none repeat scroll 0 0 #FF5555;
color: white;

File diff suppressed because it is too large Load Diff

View File

@ -44,7 +44,10 @@ RETRIEVE_WINDOWS_INSTALLER = 1
MOZ_LANGPACK_EID=langpack-$(AB_CD)@firefox.mozilla.org
PREF_JS_EXPORTS = $(call MERGE_FILE,firefox-l10n.js)
L10N_PREF_JS_EXPORTS = $(call MERGE_FILE,firefox-l10n.js)
L10N_PREF_JS_EXPORTS_PATH = $(FINAL_TARGET)/$(PREF_DIR)
L10N_PREF_JS_EXPORTS_FLAGS = $(PREF_PPFLAGS) --silence-missing-directive-warnings
PP_TARGETS += L10N_PREF_JS_EXPORTS
ifneq (,$(filter cocoa,$(MOZ_WIDGET_TOOLKIT)))
MOZ_PKG_MAC_DSSTORE=$(_ABS_DIST)/branding/dsstore

View File

@ -0,0 +1,11 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!ENTITY tabCrashed.header "Bad news first: This tab has crashed">
<!ENTITY tabCrashed.message "Now for the good news: You can just close this tab, restore it or restore all your crashed tabs.">
<!ENTITY tabCrashed.sendReport "Submit a crash report to help prevent more bad news">
<!ENTITY tabCrashed.reportSent "Crash report already submitted; thank you for helping make &brandShortName; better!">
<!ENTITY tabCrashed.closeTab "Close This Tab">
<!ENTITY tabCrashed.restoreTab "Restore This Tab">
<!ENTITY tabCrashed.restoreAll "Restore All Crashed Tabs">

View File

@ -795,11 +795,6 @@ just addresses the organization to follow, e.g. "This site is run by " -->
a CSS length value. -->
<!ENTITY pluginNotification.width "28em">
<!ENTITY tabCrashed.header "Tab crashed">
<!ENTITY tabCrashed.message "Well, this is embarrassing. We tried to display this Web page, but it's not responding.">
<!ENTITY tabCrashed.checkSendReport "Tell &vendorShortName; about this crash so they can fix it.">
<!ENTITY tabCrashed.tryAgain "Try Again">
<!ENTITY uiTour.infoPanel.close "Close">
<!ENTITY appMenuSidebars.label "Sidebars">

View File

@ -18,6 +18,7 @@
locale/browser/aboutHealthReport.dtd (%chrome/browser/aboutHealthReport.dtd)
#endif
locale/browser/aboutSessionRestore.dtd (%chrome/browser/aboutSessionRestore.dtd)
locale/browser/aboutTabCrashed.dtd (%chrome/browser/aboutTabCrashed.dtd)
#ifdef MOZ_SERVICES_SYNC
locale/browser/syncProgress.dtd (%chrome/browser/syncProgress.dtd)
locale/browser/syncCustomize.dtd (%chrome/browser/syncCustomize.dtd)

View File

@ -11,8 +11,11 @@ include $(topsrcdir)/config/config.mk
# l10s prefs file
# copying firefox-l10n.js over from LOCALE_SRCDIR or browser
PREF_JS_EXPORTS = $(firstword $(wildcard $(LOCALE_SRCDIR)/firefox-l10n.js) \
$(topsrcdir)/$(relativesrcdir)/en-US/firefox-l10n.js )
L10N_PREF_JS_EXPORTS = $(firstword $(wildcard $(LOCALE_SRCDIR)/firefox-l10n.js) \
$(srcdir)/en-US/firefox-l10n.js )
L10N_PREF_JS_EXPORTS_PATH = $(FINAL_TARGET)/$(PREF_DIR)
L10N_PREF_JS_EXPORTS_FLAGS = $(PREF_PPFLAGS) --silence-missing-directive-warnings
PP_TARGETS += L10N_PREF_JS_EXPORTS
include $(topsrcdir)/config/rules.mk

View File

@ -1,7 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
include $(topsrcdir)/config/config.mk
PREF_JS_EXPORTS = $(srcdir)/metro.js

View File

@ -4,3 +4,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
JS_PREFERENCE_FILES += [
'metro.js',
]

View File

@ -82,6 +82,7 @@ this.TabCrashReporter = {
if (this.browserMap.get(browser) == childID) {
this.browserMap.delete(browser);
browser.contentDocument.documentElement.classList.remove("crashDumpAvailable");
browser.contentDocument.documentElement.classList.add("crashDumpSubmitted");
}
}
}

View File

@ -1,108 +0,0 @@
body {
background-color: rgb(241, 244, 248);
margin-top: 2em;
font: message-box;
font-size: 100%;
}
p {
font-size: .8em;
}
#error-box {
background: url('chrome://global/skin/icons/information-24.png') no-repeat left 4px;
-moz-padding-start: 30px;
}
#error-box:-moz-locale-dir(rtl) {
background-position: right 4px;
}
#main-error-msg {
color: #4b4b4b;
font-weight: bold;
}
#report-box {
text-align: center;
width: 75%;
margin: 0 auto;
display: none;
}
.crashDumpAvailable #report-box {
display: block
}
#button-box {
text-align: center;
width: 75%;
margin: 0 auto;
}
@media all and (min-width: 300px) {
#error-box {
max-width: 50%;
margin: 0 auto;
background-image: url('chrome://global/skin/icons/information-32.png');
min-height: 36px;
-moz-padding-start: 38px;
}
button {
width: auto !important;
min-width: 150px;
}
}
@media all and (min-width: 780px) {
#error-box {
max-width: 30%;
}
}
button {
font: message-box;
font-size: 0.6875em;
-moz-appearance: none;
-moz-user-select: none;
width: 100%;
margin: 2px 0;
padding: 2px 6px;
line-height: 1.2;
background-color: hsla(210,30%,95%,.1);
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
background-clip: padding-box;
border: 1px solid hsla(210,15%,25%,.4);
border-color: hsla(210,15%,25%,.3) hsla(210,15%,25%,.35) hsla(210,15%,25%,.4);
border-radius: 3px;
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 1px 0 hsla(0,0%,100%,.1);
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
}
button:hover {
background-color: hsla(210,30%,95%,.8);
border-color: hsla(210,15%,25%,.45) hsla(210,15%,25%,.5) hsla(210,15%,25%,.55);
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 1px 0 hsla(0,0%,100%,.1),
0 0 3px hsla(210,15%,25%,.1);
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
transition-timing-function: ease;
}
button:hover:active {
background-color: hsla(210,15%,25%,.2);
box-shadow: 0 1px 1px hsla(210,15%,25%,.2) inset,
0 0 2px hsla(210,15%,25%,.4) inset;
transition-property: background-color, border-color, box-shadow;
transition-duration: 10ms;
transition-timing-function: linear;
}

View File

@ -191,6 +191,7 @@ browser.jar:
skin/classic/browser/tabbrowser/alltabs.png (tabbrowser/alltabs.png)
skin/classic/browser/tabbrowser/alltabs-inverted.png (tabbrowser/alltabs-inverted.png)
skin/classic/browser/tabbrowser/connecting.png (tabbrowser/connecting.png)
skin/classic/browser/tabbrowser/crashed.svg (../shared/tabbrowser/crashed.svg)
skin/classic/browser/tabbrowser/loading.png (tabbrowser/loading.png)
skin/classic/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png)
skin/classic/browser/tabbrowser/tab-arrow-left.png (tabbrowser/tab-arrow-left.png)

View File

@ -3092,6 +3092,14 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
opacity: .9;
}
/*
* Force the overlay to create a new stacking context so it always appears on
* top of the icon.
*/
.tab-icon-overlay {
opacity: 0.9999;
}
.tab-label:not([selected="true"]) {
opacity: .7;
}

View File

@ -301,6 +301,7 @@ browser.jar:
skin/classic/browser/tabbrowser/alltabs-box-bkgnd-icon.png (tabbrowser/alltabs-box-bkgnd-icon.png)
skin/classic/browser/tabbrowser/alltabs-box-bkgnd-icon-inverted.png (tabbrowser/alltabs-box-bkgnd-icon-inverted.png)
skin/classic/browser/tabbrowser/alltabs-box-bkgnd-icon-inverted@2x.png (tabbrowser/alltabs-box-bkgnd-icon-inverted@2x.png)
skin/classic/browser/tabbrowser/crashed.svg (../shared/tabbrowser/crashed.svg)
skin/classic/browser/tabbrowser/newtab.png (tabbrowser/newtab.png)
skin/classic/browser/tabbrowser/newtab@2x.png (tabbrowser/newtab@2x.png)
skin/classic/browser/tabbrowser/newtab-inverted.png (tabbrowser/newtab-inverted.png)

View File

@ -1,11 +1,11 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
.title {
background-image: url("chrome://browser/skin/tab-crashed.svg");
}
#report-box {
display: none;
#reportSent {
font-weight: bold;
}
.crashDumpAvailable #report-box {
display: block
}

View File

@ -157,7 +157,8 @@
/* End override @tabCurveHalfWidth@ and @tabCurveWidth@ */
#urlbar ::-moz-selection,
#navigator-toolbox .searchbar-textbox ::-moz-selection {
#navigator-toolbox .searchbar-textbox ::-moz-selection,
.browserContainer > findbar ::-moz-selection {
background-color: var(--chrome-selection-background-color);
color: var(--chrome-selection-color);
}
@ -182,6 +183,16 @@
color: var(--chrome-color);
}
.browserContainer > findbar {
background-image: none;
}
/* Default findbar text color doesn't look good - Bug 1125677 */
.browserContainer > findbar .findbar-find-status,
.browserContainer > findbar .found-matches {
color: inherit;
}
#navigator-toolbox .toolbarbutton-1,
.browserContainer > findbar .findbar-button,
#PlacesToolbar toolbarbutton.bookmark-item {

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="22 22 16 16" enable-background="new 22 22 16 16" xml:space="preserve">
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="30" y1="23" x2="30" y2="37">
<stop offset="0" style="stop-color:#E63B2E"/>
<stop offset="1" style="stop-color:#C33931"/>
</linearGradient>
<circle fill="url(#SVGID_1_)" cx="30" cy="30" r="7"/>
<g>
<path fill="#FFFFFF" d="M31.03,33.304c0,0.6-0.479,1.092-1.091,1.092c-0.6,0-1.079-0.492-1.079-1.092
c0-0.588,0.479-1.079,1.079-1.079C30.551,32.225,31.03,32.716,31.03,33.304z M29.171,31.133l-0.24-5.253h2.015l-0.24,5.253H29.171z
"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 970 B

View File

@ -65,6 +65,10 @@
-moz-padding-start: 9px;
}
.tab-content[pinned] {
-moz-padding-end: 3px;
}
.tab-throbber,
.tab-icon-image,
.tab-close-button {
@ -75,12 +79,26 @@
.tab-icon-image {
height: 16px;
width: 16px;
-moz-margin-end: 6px;
}
.tab-icon-image {
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
.tab-icon-overlay {
width: 16px;
height: 16px;
margin-top: 10px;
-moz-margin-start: -16px;
display: none;
}
.tab-icon-overlay[crashed] {
display: -moz-box;
list-style-image: url("chrome://browser/skin/tabbrowser/crashed.svg");
}
.tab-throbber[busy] {
list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png");
}
@ -89,11 +107,6 @@
list-style-image: url("chrome://browser/skin/tabbrowser/loading.png");
}
.tab-throbber:not([pinned]),
.tab-icon-image:not([pinned]) {
-moz-margin-end: 6px;
}
.tab-label {
-moz-margin-end: 0;
-moz-margin-start: 0;

View File

@ -219,6 +219,7 @@ browser.jar:
skin/classic/browser/tabbrowser/newtab.png (tabbrowser/newtab-XPVista7.png)
skin/classic/browser/tabbrowser/newtab-inverted.png (tabbrowser/newtab-inverted.png)
skin/classic/browser/tabbrowser/connecting.png (tabbrowser/connecting.png)
skin/classic/browser/tabbrowser/crashed.svg (../shared/tabbrowser/crashed.svg)
skin/classic/browser/tabbrowser/loading.png (tabbrowser/loading.png)
skin/classic/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png)
skin/classic/browser/tabbrowser/tab-active-middle@2x.png (tabbrowser/tab-active-middle@2x.png)
@ -689,6 +690,7 @@ browser.jar:
skin/classic/aero/browser/tabbrowser/newtab-XPVista7.png (tabbrowser/newtab-XPVista7.png)
skin/classic/aero/browser/tabbrowser/newtab-inverted.png (tabbrowser/newtab-inverted.png)
skin/classic/aero/browser/tabbrowser/connecting.png (tabbrowser/connecting.png)
skin/classic/aero/browser/tabbrowser/crashed.svg (../shared/tabbrowser/crashed.svg)
skin/classic/aero/browser/tabbrowser/loading.png (tabbrowser/loading.png)
skin/classic/aero/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png)
skin/classic/aero/browser/tabbrowser/tab-active-middle@2x.png (tabbrowser/tab-active-middle@2x.png)

View File

@ -116,7 +116,6 @@ nsNullPrincipal::Init(uint32_t aAppId, bool aInMozBrowser)
}
mURI = new nsNullPrincipalURI(str);
NS_ENSURE_TRUE(mURI, NS_ERROR_OUT_OF_MEMORY);
return NS_OK;
}

View File

@ -986,8 +986,6 @@ nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, uint32_t aAppId,
}
nsRefPtr<nsPrincipal> codebase = new nsPrincipal();
if (!codebase)
return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = codebase->Init(aURI, aAppId, aInMozBrowser);
if (NS_FAILED(rv))
@ -1269,7 +1267,6 @@ nsresult nsScriptSecurityManager::Init()
// Create our system principal singleton
nsRefPtr<nsSystemPrincipal> system = new nsSystemPrincipal();
NS_ENSURE_TRUE(system, NS_ERROR_OUT_OF_MEMORY);
mSystemPrincipal = system;

View File

@ -90,6 +90,7 @@ _MOZBUILD_EXTERNAL_VARIABLES := \
NO_DIST_INSTALL \
OS_LIBS \
PARALLEL_DIRS \
PREF_JS_EXPORTS \
PROGRAM \
PYTHON_UNIT_TESTS \
RESOURCE_FILES \

View File

@ -626,7 +626,7 @@ File::Constructor(const GlobalObject& aGlobal,
const ChromeFilePropertyBag& aBag,
ErrorResult& aRv)
{
if (!nsContentUtils::IsCallerChrome()) {
if (!nsContentUtils::ThreadsafeIsCallerChrome()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
@ -648,6 +648,7 @@ File::Constructor(const GlobalObject& aGlobal,
const ChromeFilePropertyBag& aBag,
ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
if (!nsContentUtils::IsCallerChrome()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
@ -672,7 +673,7 @@ File::Constructor(const GlobalObject& aGlobal,
const ChromeFilePropertyBag& aBag,
ErrorResult& aRv)
{
if (!nsContentUtils::IsCallerChrome()) {
if (!nsContentUtils::ThreadsafeIsCallerChrome()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}

View File

@ -112,6 +112,13 @@ GetObjectLog()
#define LOG(args) PR_LOG(GetObjectLog(), PR_LOG_DEBUG, args)
#define LOG_ENABLED() PR_LOG_TEST(GetObjectLog(), PR_LOG_DEBUG)
static bool
IsJavaMIME(const nsACString & aMIMEType)
{
return
nsPluginHost::GetSpecialType(aMIMEType) == nsPluginHost::eSpecialType_Java;
}
static bool
InActiveDocument(nsIContent *aContent)
{
@ -992,7 +999,7 @@ nsObjectLoadingContent::BuildParametersArray()
mCachedAttributes.AppendElement(param);
}
bool isJava = nsPluginHost::IsJavaMIMEType(mContentType.get());
bool isJava = IsJavaMIME(mContentType);
nsCString codebase;
if (isJava) {
@ -1584,8 +1591,8 @@ nsObjectLoadingContent::UpdateObjectParameters(bool aJavaURI)
if (aJavaURI || thisContent->NodeInfo()->Equals(nsGkAtoms::applet)) {
nsAdoptingCString javaMIME = Preferences::GetCString(kPrefJavaMIME);
newMime = javaMIME;
NS_ASSERTION(nsPluginHost::IsJavaMIMEType(newMime.get()),
"plugin.mime.java should be recognized by IsJavaMIMEType");
NS_ASSERTION(IsJavaMIME(newMime),
"plugin.mime.java should be recognized as java");
isJava = true;
} else {
nsAutoString rawTypeAttr;
@ -1593,7 +1600,7 @@ nsObjectLoadingContent::UpdateObjectParameters(bool aJavaURI)
if (!rawTypeAttr.IsEmpty()) {
typeAttr = rawTypeAttr;
CopyUTF16toUTF8(rawTypeAttr, newMime);
isJava = nsPluginHost::IsJavaMIMEType(newMime.get());
isJava = IsJavaMIME(newMime);
}
}
@ -1607,8 +1614,8 @@ nsObjectLoadingContent::UpdateObjectParameters(bool aJavaURI)
if (!classIDAttr.IsEmpty()) {
// Our classid support is limited to 'java:' ids
nsAdoptingCString javaMIME = Preferences::GetCString(kPrefJavaMIME);
NS_ASSERTION(nsPluginHost::IsJavaMIMEType(javaMIME.get()),
"plugin.mime.java should be recognized by IsJavaMIMEType");
NS_ASSERTION(IsJavaMIME(javaMIME),
"plugin.mime.java should be recognized as java");
if (StringBeginsWith(classIDAttr, NS_LITERAL_STRING("java:")) &&
PluginExistsForType(javaMIME)) {
newMime = javaMIME;
@ -1720,7 +1727,7 @@ nsObjectLoadingContent::UpdateObjectParameters(bool aJavaURI)
(caps & eAllowPluginSkipChannel) &&
IsPluginEnabledByExtension(newURI, newMime)) {
LOG(("OBJLC [%p]: Using extension as type hint (%s)", this, newMime.get()));
if (!isJava && nsPluginHost::IsJavaMIMEType(newMime.get())) {
if (!isJava && IsJavaMIME(newMime)) {
return UpdateObjectParameters(true);
}
}
@ -1836,7 +1843,7 @@ nsObjectLoadingContent::UpdateObjectParameters(bool aJavaURI)
}
} else {
newMime = channelType;
if (nsPluginHost::IsJavaMIMEType(newMime.get())) {
if (IsJavaMIME(newMime)) {
// Java does not load with a channel, and being java retroactively
// changes how we may have interpreted the codebase to construct this
// URI above. Because the behavior here is more or less undefined, play
@ -2107,7 +2114,7 @@ nsObjectLoadingContent::LoadObject(bool aNotify,
if (mType != eType_Null) {
bool allowLoad = true;
if (nsPluginHost::IsJavaMIMEType(mContentType.get())) {
if (IsJavaMIME(mContentType)) {
allowLoad = CheckJavaCodebase();
}
int16_t contentPolicy = nsIContentPolicy::ACCEPT;
@ -3017,7 +3024,8 @@ nsObjectLoadingContent::StopPluginInstance()
if (inst) {
const char* mime = nullptr;
if (NS_SUCCEEDED(inst->GetMIMEType(&mime)) && mime) {
if (strcmp(mime, "audio/x-pn-realaudio-plugin") == 0) {
if (nsPluginHost::GetSpecialType(nsDependentCString(mime)) ==
nsPluginHost::eSpecialType_RealPlayer) {
delayedStop = true;
}
}

View File

@ -2726,6 +2726,10 @@ UnwrapArgImpl(JS::Handle<JSObject*> src,
const nsIID &iid,
void **ppArg)
{
if (!NS_IsMainThread()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsISupports *iface = xpc::UnwrapReflectorToISupports(src);
if (iface) {
if (NS_FAILED(iface->QueryInterface(iid, ppArg))) {

View File

@ -633,6 +633,10 @@ DOMInterfaces = {
'headerFile': 'nsIMediaList.h',
},
'MediaKeyStatusMap' : {
'implicitJSContext': [ 'size', 'get', 'has' ]
},
'MediaStream': {
'headerFile': 'DOMMediaStream.h',
'nativeType': 'mozilla::DOMMediaStream'

View File

@ -57,3 +57,4 @@ skip-if = debug == false
skip-if = debug == false
[test_promise_rejections_from_jsimplemented.html]
skip-if = debug == false
[test_worker_UnwrapArg.html]

View File

@ -0,0 +1,58 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1127206
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1127206</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Bug 1127206 **/
SimpleTest.waitForExplicitFinish();
var blob = new Blob([
`try { new File({}); }
catch (e) {
postMessage("throwing on random object");
}
try { new File(new Blob(["abc"])); }
catch (e) {
postMessage("throwing on Blob");
}
try { new File("abc"); }
catch (e) {
postMessage("throwing on string");
}
postMessage('finishTest')`]);
var url = URL.createObjectURL(blob);
var w = new Worker(url);
var expectedResults = [
"throwing on random object",
"throwing on Blob",
"throwing on string",
];
var curIndex = 0;
w.onmessage = function(e) {
if (curIndex == expectedResults.length) {
is(e.data, "finishTest", "What message is this?");
SimpleTest.finish();
} else {
is(e.data, expectedResults[curIndex],
"Message " + (curIndex+1) + " should be correct");
++curIndex;
}
}
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1127206">Mozilla Bug 1127206</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -314,6 +314,17 @@ ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aMaxLength);
}
static inline uint32_t
GetBRLength(LineBreakType aLineBreakType)
{
#if defined(XP_WIN)
// Length of \r\n
return (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1;
#else
return 1;
#endif
}
/* static */ uint32_t
ContentEventHandler::GetTextLength(nsIContent* aContent,
LineBreakType aLineBreakType,
@ -344,12 +355,7 @@ ContentEventHandler::GetTextLength(nsIContent* aContent,
uint32_t length = std::min(text->GetLength(), aMaxLength);
return length + textLengthDifference;
} else if (IsContentBR(aContent)) {
#if defined(XP_WIN)
// Length of \r\n
return (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1;
#else
return 1;
#endif
return GetBRLength(aLineBreakType);
}
return 0;
}
@ -393,7 +399,6 @@ static nsresult GenerateFlatTextContent(nsRange* aRange,
return NS_OK;
}
nsAutoString tmpStr;
for (; !iter->IsDone(); iter->Next()) {
nsINode* node = iter->GetCurrentNode();
if (!node) {
@ -423,6 +428,171 @@ static nsresult GenerateFlatTextContent(nsRange* aRange,
return NS_OK;
}
static FontRange*
AppendFontRange(nsTArray<FontRange>& aFontRanges, uint32_t aBaseOffset)
{
FontRange* fontRange = aFontRanges.AppendElement();
fontRange->mStartOffset = aBaseOffset;
return fontRange;
}
/* static */ uint32_t
ContentEventHandler::GetTextLengthInRange(nsIContent* aContent,
uint32_t aXPStartOffset,
uint32_t aXPEndOffset,
LineBreakType aLineBreakType)
{
return aLineBreakType == LINE_BREAK_TYPE_NATIVE ?
GetNativeTextLength(aContent, aXPStartOffset, aXPEndOffset) :
aXPEndOffset - aXPStartOffset;
}
/* static */ void
ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges,
nsIContent* aContent,
int32_t aBaseOffset,
int32_t aXPStartOffset,
int32_t aXPEndOffset,
LineBreakType aLineBreakType)
{
nsIFrame* frame = aContent->GetPrimaryFrame();
if (!frame) {
// It is a non-rendered content, create an empty range for it.
AppendFontRange(aFontRanges, aBaseOffset);
return;
}
int32_t baseOffset = aBaseOffset;
nsTextFrame* curr = do_QueryFrame(frame);
MOZ_ASSERT(curr, "Not a text frame");
while (curr) {
int32_t frameXPStart = std::max(curr->GetContentOffset(), aXPStartOffset);
int32_t frameXPEnd = std::min(curr->GetContentEnd(), aXPEndOffset);
if (frameXPStart >= frameXPEnd) {
curr = static_cast<nsTextFrame*>(curr->GetNextContinuation());
continue;
}
gfxSkipCharsIterator iter = curr->EnsureTextRun(nsTextFrame::eInflated);
gfxTextRun* textRun = curr->GetTextRun(nsTextFrame::eInflated);
nsTextFrame* next = nullptr;
if (frameXPEnd < aXPEndOffset) {
next = static_cast<nsTextFrame*>(curr->GetNextContinuation());
while (next && next->GetTextRun(nsTextFrame::eInflated) == textRun) {
frameXPEnd = std::min(next->GetContentEnd(), aXPEndOffset);
next = frameXPEnd < aXPEndOffset ?
static_cast<nsTextFrame*>(next->GetNextContinuation()) : nullptr;
}
}
uint32_t skipStart = iter.ConvertOriginalToSkipped(frameXPStart);
uint32_t skipEnd = iter.ConvertOriginalToSkipped(frameXPEnd);
gfxTextRun::GlyphRunIterator runIter(
textRun, skipStart, skipEnd - skipStart);
int32_t lastXPEndOffset = frameXPStart;
while (runIter.NextRun()) {
gfxFont* font = runIter.GetGlyphRun()->mFont.get();
int32_t startXPOffset =
iter.ConvertSkippedToOriginal(runIter.GetStringStart());
// It is possible that the first glyph run has exceeded the frame,
// because the whole frame is filled by skipped chars.
if (startXPOffset >= frameXPEnd) {
break;
}
if (startXPOffset > lastXPEndOffset) {
// Create range for skipped leading chars.
AppendFontRange(aFontRanges, baseOffset);
baseOffset += GetTextLengthInRange(
aContent, lastXPEndOffset, startXPOffset, aLineBreakType);
lastXPEndOffset = startXPOffset;
}
FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
fontRange->mFontName = font->GetName();
fontRange->mFontSize = font->GetAdjustedSize();
// The converted original offset may exceed the range,
// hence we need to clamp it.
int32_t endXPOffset =
iter.ConvertSkippedToOriginal(runIter.GetStringEnd());
endXPOffset = std::min(frameXPEnd, endXPOffset);
baseOffset += GetTextLengthInRange(aContent, startXPOffset, endXPOffset,
aLineBreakType);
lastXPEndOffset = endXPOffset;
}
if (lastXPEndOffset < frameXPEnd) {
// Create range for skipped trailing chars. It also handles case
// that the whole frame contains only skipped chars.
AppendFontRange(aFontRanges, baseOffset);
baseOffset += GetTextLengthInRange(
aContent, lastXPEndOffset, frameXPEnd, aLineBreakType);
}
curr = next;
}
}
/* static */ nsresult
ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange,
FontRangeArray& aFontRanges,
uint32_t& aLength,
LineBreakType aLineBreakType)
{
MOZ_ASSERT(aFontRanges.IsEmpty(), "aRanges must be empty array");
nsINode* startNode = aRange->GetStartParent();
nsINode* endNode = aRange->GetEndParent();
if (NS_WARN_IF(!startNode || !endNode)) {
return NS_ERROR_FAILURE;
}
// baseOffset is the flattened offset of each content node.
int32_t baseOffset = 0;
nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
for (iter->Init(aRange); !iter->IsDone(); iter->Next()) {
nsINode* node = iter->GetCurrentNode();
if (NS_WARN_IF(!node)) {
break;
}
if (!node->IsContent()) {
continue;
}
nsIContent* content = node->AsContent();
if (content->IsNodeOfType(nsINode::eTEXT)) {
int32_t startOffset = content != startNode ? 0 : aRange->StartOffset();
int32_t endOffset = content != endNode ?
content->TextLength() : aRange->EndOffset();
AppendFontRanges(aFontRanges, content, baseOffset,
startOffset, endOffset, aLineBreakType);
baseOffset += GetTextLengthInRange(content, startOffset, endOffset,
aLineBreakType);
} else if (IsContentBR(content)) {
if (aFontRanges.IsEmpty()) {
MOZ_ASSERT(baseOffset == 0);
FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
nsIFrame* frame = content->GetPrimaryFrame();
if (frame) {
const nsFont& font = frame->GetParent()->StyleFont()->mFont;
const FontFamilyList& fontList = font.fontlist;
const FontFamilyName& fontName = fontList.IsEmpty() ?
FontFamilyName(fontList.GetDefaultFontType()) :
fontList.GetFontlist()[0];
fontName.AppendToString(fontRange->mFontName, false);
fontRange->mFontSize =
frame->PresContext()->AppUnitsToDevPixels(font.size);
}
}
baseOffset += GetBRLength(aLineBreakType);
}
}
aLength = baseOffset;
return NS_OK;
}
nsresult
ContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
bool aForward,
@ -697,6 +867,18 @@ ContentEventHandler::OnQueryTextContent(WidgetQueryContentEvent* aEvent)
rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType);
NS_ENSURE_SUCCESS(rv, rv);
if (aEvent->mWithFontRanges) {
uint32_t fontRangeLength;
rv = GenerateFlatFontRanges(range, aEvent->mReply.mFontRanges,
fontRangeLength, lineBreakType);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(fontRangeLength == aEvent->mReply.mString.Length(),
"Font ranges doesn't match the string");
}
aEvent->mSucceeded = true;
return NS_OK;

View File

@ -94,6 +94,12 @@ public:
// Get the native text length of a content node excluding any children
static uint32_t GetNativeTextLength(nsIContent* aContent,
uint32_t aMaxLength = UINT32_MAX);
// Get the text length of a given range of a content node in
// the given line break type.
static uint32_t GetTextLengthInRange(nsIContent* aContent,
uint32_t aXPStartOffset,
uint32_t aXPEndOffset,
LineBreakType aLineBreakType);
protected:
static uint32_t GetTextLength(nsIContent* aContent,
LineBreakType aLineBreakType,
@ -129,6 +135,18 @@ protected:
// true, it is expanded to forward.
nsresult ExpandToClusterBoundary(nsIContent* aContent, bool aForward,
uint32_t* aXPOffset);
typedef nsTArray<mozilla::FontRange> FontRangeArray;
static void AppendFontRanges(FontRangeArray& aFontRanges,
nsIContent* aContent,
int32_t aBaseOffset,
int32_t aXPStartOffset,
int32_t aXPEndOffset,
LineBreakType aLineBreakType);
static nsresult GenerateFlatFontRanges(nsRange* aRange,
FontRangeArray& aFontRanges,
uint32_t& aLength,
LineBreakType aLineBreakType);
};
} // namespace mozilla

View File

@ -1889,7 +1889,6 @@ HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded)
// back into the output stream.
out->mStream->GetStream()->ChangeExplicitBlockerCount(1);
if (mDecoder) {
mDecoder->SetAudioCaptured(true);
mDecoder->AddOutputStream(
out->mStream->GetStream()->AsProcessedStream(), aFinishWhenEnded);
}
@ -2705,7 +2704,6 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
// available immediately.
mDecoder->SetResource(aStream);
mDecoder->SetAudioChannel(mAudioChannel);
mDecoder->SetAudioCaptured(mAudioCaptured);
mDecoder->SetVolume(mMuted ? 0.0 : mVolume);
mDecoder->SetPreservesPitch(mPreservesPitch);
mDecoder->SetPlaybackRate(mPlaybackRate);

View File

@ -98,7 +98,7 @@ TimeRanges::GetEndTime()
}
void
TimeRanges::Normalize(double aError)
TimeRanges::Normalize(double aTolerance)
{
if (mRanges.Length() >= 2) {
nsAutoTArray<TimeRange,4> normalized;
@ -112,7 +112,7 @@ TimeRanges::Normalize(double aError)
current.mEnd >= mRanges[i].mEnd) {
continue;
}
if (current.mEnd + aError >= mRanges[i].mStart) {
if (current.mEnd + aTolerance >= mRanges[i].mStart) {
current.mEnd = mRanges[i].mEnd;
} else {
normalized.AppendElement(current);
@ -127,10 +127,10 @@ TimeRanges::Normalize(double aError)
}
void
TimeRanges::Union(const TimeRanges* aOtherRanges, double aError)
TimeRanges::Union(const TimeRanges* aOtherRanges, double aTolerance)
{
mRanges.AppendElements(aOtherRanges->mRanges);
Normalize(aError);
Normalize(aTolerance);
}
void
@ -156,10 +156,10 @@ TimeRanges::Intersection(const TimeRanges* aOtherRanges)
}
TimeRanges::index_type
TimeRanges::Find(double aTime, double aError /* = 0 */)
TimeRanges::Find(double aTime, double aTolerance /* = 0 */)
{
for (index_type i = 0; i < mRanges.Length(); ++i) {
if (aTime < mRanges[i].mEnd && (aTime + aError) >= mRanges[i].mStart) {
if (aTime < mRanges[i].mEnd && (aTime + aTolerance) >= mRanges[i].mStart) {
return i;
}
}

View File

@ -42,10 +42,10 @@ public:
double GetEndTime();
// See http://www.whatwg.org/html/#normalized-timeranges-object
void Normalize(double aError = 0.0);
void Normalize(double aTolerance = 0.0);
// Mutate this TimeRange to be the union of this and aOtherRanges.
void Union(const TimeRanges* aOtherRanges, double aError);
void Union(const TimeRanges* aOtherRanges, double aTolerance);
// Mutate this TimeRange to be the intersection of this and aOtherRanges.
void Intersection(const TimeRanges* aOtherRanges);
@ -91,7 +91,7 @@ public:
typedef nsTArray<TimeRange>::index_type index_type;
static const index_type NoIndex = index_type(-1);
index_type Find(double aTime, double aError = 0);
index_type Find(double aTime, double aTolerance = 0);
bool Contains(double aStart, double aEnd) {
index_type target = Find(aStart);

View File

@ -2496,7 +2496,10 @@ ContentChild::RecvGetProfile(nsCString* aProfile)
bool
ContentChild::RecvLoadPluginResult(const uint32_t& aPluginId, const bool& aResult)
{
bool finalResult = aResult && SendConnectPluginBridge(aPluginId);
nsresult rv;
bool finalResult = aResult &&
SendConnectPluginBridge(aPluginId, &rv) &&
NS_SUCCEEDED(rv);
plugins::PluginModuleContentParent::OnLoadPluginResult(aPluginId,
finalResult);
return true;

View File

@ -979,15 +979,17 @@ static nsIDocShell* GetOpenerDocShellHelper(Element* aFrameElement)
}
bool
ContentParent::RecvLoadPlugin(const uint32_t& aPluginId)
ContentParent::RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv)
{
return mozilla::plugins::SetupBridge(aPluginId, this);
*aRv = NS_OK;
return mozilla::plugins::SetupBridge(aPluginId, this, false, aRv);
}
bool
ContentParent::RecvConnectPluginBridge(const uint32_t& aPluginId)
ContentParent::RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv)
{
return mozilla::plugins::SetupBridge(aPluginId, this, true);
*aRv = NS_OK;
return mozilla::plugins::SetupBridge(aPluginId, this, true, aRv);
}
bool

View File

@ -153,8 +153,8 @@ public:
TabId* aTabId) MOZ_OVERRIDE;
virtual bool RecvBridgeToChildProcess(const ContentParentId& aCpId) MOZ_OVERRIDE;
virtual bool RecvLoadPlugin(const uint32_t& aPluginId) MOZ_OVERRIDE;
virtual bool RecvConnectPluginBridge(const uint32_t& aPluginId) MOZ_OVERRIDE;
virtual bool RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv) MOZ_OVERRIDE;
virtual bool RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv) MOZ_OVERRIDE;
virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch,
nsTArray<PluginTag>* aPlugins,
uint32_t* aNewPluginEpoch) MOZ_OVERRIDE;

View File

@ -594,7 +594,7 @@ parent:
* process. We use intr semantics here to ensure that the PluginModuleParent
* allocation message is dispatched before LoadPlugin returns.
*/
sync LoadPlugin(uint32_t pluginId);
sync LoadPlugin(uint32_t pluginId) returns (nsresult rv);
/**
* This call is used by asynchronous plugin instantiation to notify the
@ -602,7 +602,7 @@ parent:
* the specified plugin id. When this call returns, the requested bridge
* connection has been made.
*/
sync ConnectPluginBridge(uint32_t aPluginId);
sync ConnectPluginBridge(uint32_t aPluginId) returns (nsresult rv);
/**
* This call returns the set of plugins loaded in the chrome

View File

@ -2126,6 +2126,7 @@ TabParent::RecvGetDefaultScale(double* aValue)
bool
TabParent::RecvGetWidgetNativeData(WindowsHandle* aValue)
{
*aValue = 0;
nsCOMPtr<nsIContent> content = do_QueryInterface(mFrameElement);
if (content) {
nsIPresShell* shell = content->OwnerDoc()->GetShell();
@ -2136,11 +2137,10 @@ TabParent::RecvGetWidgetNativeData(WindowsHandle* aValue)
if (widget) {
*aValue = reinterpret_cast<WindowsHandle>(
widget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW));
return true;
}
}
}
return false;
return true;
}
bool

View File

@ -263,15 +263,6 @@ void MediaDecoder::SetVolume(double aVolume)
}
}
void MediaDecoder::SetAudioCaptured(bool aCaptured)
{
MOZ_ASSERT(NS_IsMainThread());
mInitialAudioCaptured = aCaptured;
if (mDecoderStateMachine) {
mDecoderStateMachine->SetAudioCaptured(aCaptured);
}
}
void MediaDecoder::ConnectDecodedStreamToOutputStream(OutputStreamData* aStream)
{
NS_ASSERTION(!aStream->mPort, "Already connected?");
@ -360,13 +351,6 @@ MediaDecoder::DecodedStreamGraphListener::NotifyEvent(MediaStreamGraph* aGraph,
}
}
void MediaDecoder::RecreateDecodedStreamIfNecessary(int64_t aStartTimeUSecs)
{
if (mInitialAudioCaptured) {
RecreateDecodedStream(aStartTimeUSecs);
}
}
void MediaDecoder::DestroyDecodedStream()
{
MOZ_ASSERT(NS_IsMainThread());
@ -470,9 +454,13 @@ void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream,
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (!mDecodedStream) {
RecreateDecodedStream(mDecoderStateMachine ?
int64_t(mDecoderStateMachine->GetCurrentTime()*USECS_PER_S) : 0);
if (mDecoderStateMachine) {
mDecoderStateMachine->SetAudioCaptured();
}
if (!GetDecodedStream()) {
int64_t t = mDecoderStateMachine ?
mDecoderStateMachine->GetCurrentTimeUs() : 0;
RecreateDecodedStream(t);
}
OutputStreamData* os = mOutputStreams.AppendElement();
os->Init(aStream, aFinishWhenEnded);
@ -672,7 +660,9 @@ void MediaDecoder::SetStateMachineParameters()
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mDecoderStateMachine->SetDuration(mDuration);
mDecoderStateMachine->SetVolume(mInitialVolume);
mDecoderStateMachine->SetAudioCaptured(mInitialAudioCaptured);
if (GetDecodedStream()) {
mDecoderStateMachine->SetAudioCaptured();
}
SetPlaybackRate(mInitialPlaybackRate);
mDecoderStateMachine->SetPreservesPitch(mInitialPreservesPitch);
if (mMinimizePreroll) {

View File

@ -374,9 +374,6 @@ public:
virtual void Pause();
// Adjust the speed of the playback, optionally with pitch correction,
virtual void SetVolume(double aVolume);
// Sets whether audio is being captured. If it is, we won't play any
// of our audio.
virtual void SetAudioCaptured(bool aCaptured);
virtual void NotifyWaitingForResourcesStatusChanged() MOZ_OVERRIDE;
@ -857,9 +854,6 @@ public:
// The decoder monitor must be held.
bool IsLogicallyPlaying();
// Re-create a decoded stream if audio being captured
void RecreateDecodedStreamIfNecessary(int64_t aStartTimeUSecs);
#ifdef MOZ_EME
// This takes the decoder monitor.
virtual nsresult SetCDMProxy(CDMProxy* aProxy) MOZ_OVERRIDE;
@ -1068,9 +1062,6 @@ protected:
// only.
int64_t mDuration;
// True when playback should start with audio captured (not playing).
bool mInitialAudioCaptured;
// True if the media is seekable (i.e. supports random access).
bool mMediaSeekable;

View File

@ -1387,11 +1387,11 @@ void MediaDecoderStateMachine::SetVolume(double volume)
}
}
void MediaDecoderStateMachine::SetAudioCaptured(bool aCaptured)
void MediaDecoderStateMachine::SetAudioCaptured()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (!mAudioCaptured && aCaptured && !mStopAudioThread) {
AssertCurrentThreadInMonitor();
if (!mAudioCaptured && !mStopAudioThread) {
// Make sure the state machine runs as soon as possible. That will
// stop the audio sink.
// If mStopAudioThread is true then we're already stopping the audio sink
@ -1405,7 +1405,7 @@ void MediaDecoderStateMachine::SetAudioCaptured(bool aCaptured)
ResyncAudioClock();
}
}
mAudioCaptured = aCaptured;
mAudioCaptured = true;
}
double MediaDecoderStateMachine::GetCurrentTime() const
@ -1418,6 +1418,16 @@ double MediaDecoderStateMachine::GetCurrentTime() const
return static_cast<double>(mCurrentFrameTime) / static_cast<double>(USECS_PER_S);
}
int64_t MediaDecoderStateMachine::GetCurrentTimeUs() const
{
NS_ASSERTION(NS_IsMainThread() ||
OnStateMachineThread() ||
OnDecodeThread(),
"Should be on main, decode, or state machine thread.");
return mCurrentFrameTime;
}
bool MediaDecoderStateMachine::IsRealTime() const {
return mScheduler->IsRealTime();
}
@ -1782,7 +1792,9 @@ MediaDecoderStateMachine::StartSeek(const SeekTarget& aTarget)
DECODER_LOG("Changed state to SEEKING (to %lld)", mSeekTarget.mTime);
SetState(DECODER_STATE_SEEKING);
mDecoder->RecreateDecodedStreamIfNecessary(seekTime - mStartTime);
if (mAudioCaptured) {
mDecoder->RecreateDecodedStream(seekTime - mStartTime);
}
ScheduleStateMachine();
}

View File

@ -155,7 +155,7 @@ public:
// Set the audio volume. The decoder monitor must be obtained before
// calling this.
void SetVolume(double aVolume);
void SetAudioCaptured(bool aCapture);
void SetAudioCaptured();
// Check if the decoder needs to become dormant state.
bool IsDormantNeeded();
@ -241,6 +241,7 @@ public:
// Called from the main thread to get the current frame time. The decoder
// monitor must be obtained before calling this.
double GetCurrentTime() const;
int64_t GetCurrentTimeUs() const;
// Clear the flag indicating that a playback position change event
// is currently queued. This is called from the main thread and must

View File

@ -267,35 +267,18 @@ CDMCallbackProxy::SessionError(const nsCString& aSessionId,
}
void
CDMCallbackProxy::KeyIdUsable(const nsCString& aSessionId,
const nsTArray<uint8_t>& aKeyId)
CDMCallbackProxy::KeyStatusChanged(const nsCString& aSessionId,
const nsTArray<uint8_t>& aKeyId,
GMPMediaKeyStatus aStatus)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
bool keysChange = false;
{
CDMCaps::AutoLock caps(mProxy->Capabilites());
keysChange = caps.SetKeyUsable(aKeyId, NS_ConvertUTF8toUTF16(aSessionId));
}
if (keysChange) {
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethodWithArg<nsString>(mProxy,
&CDMProxy::OnKeysChange,
NS_ConvertUTF8toUTF16(aSessionId));
NS_DispatchToMainThread(task);
}
}
void
CDMCallbackProxy::KeyIdNotUsable(const nsCString& aSessionId,
const nsTArray<uint8_t>& aKeyId)
{
MOZ_ASSERT(mProxy->IsOnGMPThread());
bool keysChange = false;
{
CDMCaps::AutoLock caps(mProxy->Capabilites());
keysChange = caps.SetKeyUnusable(aKeyId, NS_ConvertUTF8toUTF16(aSessionId));
keysChange = caps.SetKeyStatus(aKeyId,
NS_ConvertUTF8toUTF16(aSessionId),
aStatus);
}
if (keysChange) {
nsRefPtr<nsIRunnable> task;

View File

@ -43,11 +43,9 @@ public:
uint32_t aSystemCode,
const nsCString& aMessage) MOZ_OVERRIDE;
virtual void KeyIdUsable(const nsCString& aSessionId,
const nsTArray<uint8_t>& aKeyId) MOZ_OVERRIDE;
virtual void KeyIdNotUsable(const nsCString& aSessionId,
const nsTArray<uint8_t>& aKeyId) MOZ_OVERRIDE;
virtual void KeyStatusChanged(const nsCString& aSessionId,
const nsTArray<uint8_t>& aKeyId,
GMPMediaKeyStatus aStatus) MOZ_OVERRIDE;
virtual void SetCaps(uint64_t aCaps) MOZ_OVERRIDE;

View File

@ -5,7 +5,6 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CDMCaps.h"
#include "gmp-decryption.h"
#include "EMELog.h"
#include "nsThreadUtils.h"
#include "SamplesWaitingForKey.h"
@ -80,9 +79,9 @@ bool
CDMCaps::AutoLock::IsKeyUsable(const CencKeyId& aKeyId)
{
mData.mMonitor.AssertCurrentThreadOwns();
const auto& keys = mData.mUsableKeyIds;
const auto& keys = mData.mKeyStatuses;
for (size_t i = 0; i < keys.Length(); i++) {
if (keys[i].mId == aKeyId) {
if (keys[i].mId == aKeyId && keys[i].mStatus == kGMPUsable) {
return true;
}
}
@ -90,15 +89,32 @@ CDMCaps::AutoLock::IsKeyUsable(const CencKeyId& aKeyId)
}
bool
CDMCaps::AutoLock::SetKeyUsable(const CencKeyId& aKeyId,
const nsString& aSessionId)
CDMCaps::AutoLock::SetKeyStatus(const CencKeyId& aKeyId,
const nsString& aSessionId,
GMPMediaKeyStatus aStatus)
{
mData.mMonitor.AssertCurrentThreadOwns();
UsableKey key(aKeyId, aSessionId);
if (mData.mUsableKeyIds.Contains(key)) {
return false;
KeyStatus key(aKeyId, aSessionId, aStatus);
auto index = mData.mKeyStatuses.IndexOf(key);
if (aStatus == kGMPUnknown) {
// Return true if the element is found to notify key changes.
return mData.mKeyStatuses.RemoveElement(key);
}
mData.mUsableKeyIds.AppendElement(key);
if (index != mData.mKeyStatuses.NoIndex) {
if (mData.mKeyStatuses[index].mStatus == aStatus) {
return false;
}
mData.mKeyStatuses[index].mStatus = aStatus;
} else {
mData.mKeyStatuses.AppendElement(key);
}
if (aStatus != kGMPUsable) {
return true;
}
auto& waiters = mData.mWaitForKeys;
size_t i = 0;
while (i < waiters.Length()) {
@ -113,26 +129,6 @@ CDMCaps::AutoLock::SetKeyUsable(const CencKeyId& aKeyId,
return true;
}
bool
CDMCaps::AutoLock::SetKeyUnusable(const CencKeyId& aKeyId,
const nsString& aSessionId)
{
mData.mMonitor.AssertCurrentThreadOwns();
UsableKey key(aKeyId, aSessionId);
if (!mData.mUsableKeyIds.Contains(key)) {
return false;
}
auto& keys = mData.mUsableKeyIds;
for (size_t i = 0; i < keys.Length(); i++) {
if (keys[i].mId == aKeyId &&
keys[i].mSessionId == aSessionId) {
keys.RemoveElementAt(i);
break;
}
}
return true;
}
void
CDMCaps::AutoLock::NotifyWhenKeyIdUsable(const CencKeyId& aKey,
SamplesWaitingForKey* aListener)
@ -175,15 +171,15 @@ CDMCaps::AutoLock::CanDecryptVideo()
}
void
CDMCaps::AutoLock::GetUsableKeysForSession(const nsAString& aSessionId,
nsTArray<CencKeyId>& aOutKeyIds)
CDMCaps::AutoLock::GetKeyStatusesForSession(const nsAString& aSessionId,
nsTArray<KeyStatus>& aOutKeyStatuses)
{
for (size_t i = 0; i < mData.mUsableKeyIds.Length(); i++) {
const auto& key = mData.mUsableKeyIds[i];
for (size_t i = 0; i < mData.mKeyStatuses.Length(); i++) {
const auto& key = mData.mKeyStatuses[i];
if (key.mSessionId.Equals(aSessionId)) {
aOutKeyIds.AppendElement(key.mId);
aOutKeyStatuses.AppendElement(key);
}
}
}
} // namespace mozilla
} // namespace mozilla

View File

@ -14,6 +14,7 @@
#include "nsTArray.h"
#include "mozilla/Attributes.h"
#include "SamplesWaitingForKey.h"
#include "gmp-decryption.h"
namespace mozilla {
@ -24,6 +25,29 @@ public:
CDMCaps();
~CDMCaps();
struct KeyStatus {
KeyStatus(const CencKeyId& aId,
const nsString& aSessionId,
GMPMediaKeyStatus aStatus)
: mId(aId)
, mSessionId(aSessionId)
, mStatus(aStatus)
{}
KeyStatus(const KeyStatus& aOther)
: mId(aOther.mId)
, mSessionId(aOther.mSessionId)
, mStatus(aOther.mStatus)
{}
bool operator==(const KeyStatus& aOther) const {
return mId == aOther.mId &&
mSessionId == aOther.mSessionId;
};
CencKeyId mId;
nsString mSessionId;
GMPMediaKeyStatus mStatus;
};
// Locks the CDMCaps. It must be locked to access its shared state.
// Threadsafe when locked.
class MOZ_STACK_CLASS AutoLock {
@ -37,16 +61,12 @@ public:
bool IsKeyUsable(const CencKeyId& aKeyId);
// Returns true if setting this key usable results in the usable keys
// changing for this session, i.e. the key was not previously marked usable.
bool SetKeyUsable(const CencKeyId& aKeyId, const nsString& aSessionId);
// Returns true if key status changed,
// i.e. the key status changed from usable to expired.
bool SetKeyStatus(const CencKeyId& aKeyId, const nsString& aSessionId, GMPMediaKeyStatus aStatus);
// Returns true if setting this key unusable results in the usable keys
// changing for this session, i.e. the key was previously marked usable.
bool SetKeyUnusable(const CencKeyId& aKeyId, const nsString& aSessionId);
void GetUsableKeysForSession(const nsAString& aSessionId,
nsTArray<CencKeyId>& aOutKeyIds);
void GetKeyStatusesForSession(const nsAString& aSessionId,
nsTArray<KeyStatus>& aOutKeyStatuses);
// Sets the capabilities of the CDM. aCaps is the logical OR of the
// GMP_EME_CAP_* flags from gmp-decryption.h.
@ -85,25 +105,7 @@ private:
Monitor mMonitor;
struct UsableKey {
UsableKey(const CencKeyId& aId,
const nsString& aSessionId)
: mId(aId)
, mSessionId(aSessionId)
{}
UsableKey(const UsableKey& aOther)
: mId(aOther.mId)
, mSessionId(aOther.mSessionId)
{}
bool operator==(const UsableKey& aOther) const {
return mId == aOther.mId &&
mSessionId == aOther.mSessionId;
};
CencKeyId mId;
nsString mSessionId;
};
nsTArray<UsableKey> mUsableKeyIds;
nsTArray<KeyStatus> mKeyStatuses;
nsTArray<WaitForKeys> mWaitForKeys;

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