mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to b-i
This commit is contained in:
commit
bee9cd608f
2
CLOBBER
2
CLOBBER
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -76,3 +76,8 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
JS_PREFERENCE_FILES += [
|
||||
'b2g.js',
|
||||
]
|
||||
|
||||
|
@ -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
|
@ -4,3 +4,8 @@
|
||||
|
||||
DIST_SUBDIR = 'browser'
|
||||
export('DIST_SUBDIR')
|
||||
|
||||
JS_PREFERENCE_FILES += [
|
||||
'/b2g/app/b2g.js',
|
||||
]
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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',
|
||||
]
|
||||
|
8
browser/base/content/aboutTabCrashed.css
Normal file
8
browser/base/content/aboutTabCrashed.css
Normal 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;
|
||||
}
|
@ -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});
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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 := \
|
||||
|
@ -8,3 +8,8 @@ DIRS += ['content', 'locales']
|
||||
|
||||
DIST_SUBDIR = 'browser'
|
||||
export('DIST_SUBDIR')
|
||||
|
||||
JS_PREFERENCE_FILES += [
|
||||
'pref/firefox-branding.js',
|
||||
]
|
||||
|
||||
|
@ -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 := \
|
||||
|
@ -8,3 +8,8 @@ DIRS += ['content', 'locales']
|
||||
|
||||
DIST_SUBDIR = 'browser'
|
||||
export('DIST_SUBDIR')
|
||||
|
||||
JS_PREFERENCE_FILES += [
|
||||
'pref/firefox-branding.js',
|
||||
]
|
||||
|
||||
|
@ -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 := \
|
||||
|
@ -8,3 +8,8 @@ DIRS += ['content', 'locales']
|
||||
|
||||
DIST_SUBDIR = 'browser'
|
||||
export('DIST_SUBDIR')
|
||||
|
||||
JS_PREFERENCE_FILES += [
|
||||
'pref/firefox-branding.js',
|
||||
]
|
||||
|
||||
|
@ -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 := \
|
||||
|
@ -8,3 +8,8 @@ DIRS += ['content', 'locales']
|
||||
|
||||
DIST_SUBDIR = 'browser'
|
||||
export('DIST_SUBDIR')
|
||||
|
||||
JS_PREFERENCE_FILES += [
|
||||
'pref/firefox-branding.js',
|
||||
]
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
@ -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.
|
||||
*
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -68,7 +68,9 @@ describe("loop.roomViews", function () {
|
||||
videoMuted: false,
|
||||
failureReason: undefined,
|
||||
used: false,
|
||||
foo: "bar"
|
||||
foo: "bar",
|
||||
localVideoDimensions: {},
|
||||
remoteVideoDimensions: {}
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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 = {
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -23,3 +23,8 @@ EXTRA_JS_MODULES.devtools.webide += [
|
||||
'modules/tab-store.js',
|
||||
'modules/utils.js'
|
||||
]
|
||||
|
||||
JS_PREFERENCE_FILES += [
|
||||
'webide-prefs.js',
|
||||
]
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
138
browser/extensions/pdfjs/content/build/pdf.worker.js
vendored
138
browser/extensions/pdfjs/content/build/pdf.worker.js
vendored
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
@ -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
|
||||
|
11
browser/locales/en-US/chrome/browser/aboutTabCrashed.dtd
Normal file
11
browser/locales/en-US/chrome/browser/aboutTabCrashed.dtd
Normal 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">
|
@ -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">
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
@ -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',
|
||||
]
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
@ -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 {
|
||||
|
16
browser/themes/shared/tabbrowser/crashed.svg
Normal file
16
browser/themes/shared/tabbrowser/crashed.svg
Normal 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 |
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -90,6 +90,7 @@ _MOZBUILD_EXTERNAL_VARIABLES := \
|
||||
NO_DIST_INSTALL \
|
||||
OS_LIBS \
|
||||
PARALLEL_DIRS \
|
||||
PREF_JS_EXPORTS \
|
||||
PROGRAM \
|
||||
PYTHON_UNIT_TESTS \
|
||||
RESOURCE_FILES \
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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))) {
|
||||
|
@ -633,6 +633,10 @@ DOMInterfaces = {
|
||||
'headerFile': 'nsIMediaList.h',
|
||||
},
|
||||
|
||||
'MediaKeyStatusMap' : {
|
||||
'implicitJSContext': [ 'size', 'get', 'has' ]
|
||||
},
|
||||
|
||||
'MediaStream': {
|
||||
'headerFile': 'DOMMediaStream.h',
|
||||
'nativeType': 'mozilla::DOMMediaStream'
|
||||
|
@ -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]
|
||||
|
58
dom/bindings/test/test_worker_UnwrapArg.html
Normal file
58
dom/bindings/test/test_worker_UnwrapArg.html
Normal 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>
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user