Merge m-c to b2g-inbound. a=merge

This commit is contained in:
Ryan VanderMeulen 2015-07-27 10:43:09 -04:00
commit b87718f020
807 changed files with 13061 additions and 13252 deletions

View File

@ -355,10 +355,12 @@ ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
AccessibleWrap* accWrap = [self getGeckoAccessible];
if (accWrap) {
Accessible* acc = accWrap->GetChildAt(i);
return acc ? GetNativeFromGeckoAccessible(acc) : nil;
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
Accessible* child = accWrap->GetChildAt(i);
return child ? GetNativeFromGeckoAccessible(child) : nil;
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
ProxyAccessible* child = proxy->ChildAt(i);
return child ? GetNativeFromProxy(child) : nil;
}
return nil;
@ -849,12 +851,14 @@ ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
AccessibleWrap* accWrap = [self getGeckoAccessible];
if (!accWrap)
nsIntRect rect;
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
rect = accWrap->Bounds();
else if (ProxyAccessible* proxy = [self getProxyAccessible])
rect = proxy->Bounds();
else
return nil;
nsIntRect rect = accWrap->Bounds();
NSScreen* mainView = [[NSScreen screens] objectAtIndex:0];
CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mainView);
NSPoint p = NSMakePoint(static_cast<CGFloat>(rect.x) / scaleFactor,
@ -869,11 +873,14 @@ ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
AccessibleWrap* accWrap = [self getGeckoAccessible];
if (!accWrap)
nsIntRect rect;
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
rect = accWrap->Bounds();
else if (ProxyAccessible* proxy = [self getProxyAccessible])
rect = proxy->Bounds();
else
return nil;
nsIntRect rect = accWrap->Bounds();
CGFloat scaleFactor =
nsCocoaUtils::GetBackingScaleFactor([[NSScreen screens] objectAtIndex:0]);
return [NSValue valueWithSize:NSMakeSize(static_cast<CGFloat>(rect.width) / scaleFactor,

View File

@ -1118,9 +1118,6 @@ pref("services.mobileid.server.uri", "https://msisdn.services.mozilla.com");
pref("dom.mapped_arraybuffer.enabled", true);
#endif
// BroadcastChannel API
pref("dom.broadcastChannel.enabled", true);
// SystemUpdate API
pref("dom.system_update.enabled", true);

View File

@ -35,6 +35,6 @@ export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
GAIADIR=$topsrcdir/gaia
# Include Firefox OS fonts.
MOZTTDIR=$topsrcdir/moztt
MOZTTDIR=$topsrcdir/moz-tt
. "$topsrcdir/b2g/config/mozconfigs/common.override"

View File

@ -34,7 +34,7 @@ export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
GAIADIR=$topsrcdir/gaia
# Include Firefox OS fonts.
MOZTTDIR=$topsrcdir/moztt
MOZTTDIR=$topsrcdir/moz-tt
# Build simulator xpi and phone tweaks for b2g-desktop
FXOS_SIMULATOR=1

View File

@ -35,6 +35,6 @@ export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
GAIADIR=$topsrcdir/gaia
# Include Firefox OS fonts.
MOZTTDIR=$topsrcdir/moztt
MOZTTDIR=$topsrcdir/moz-tt
. "$topsrcdir/b2g/config/mozconfigs/common.override"

View File

@ -34,7 +34,7 @@ export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
GAIADIR=$topsrcdir/gaia
# Include Firefox OS fonts.
MOZTTDIR=$topsrcdir/moztt
MOZTTDIR=$topsrcdir/moz-tt
# Build simulator xpi and phone tweaks for b2g-desktop
FXOS_SIMULATOR=1

View File

@ -32,6 +32,6 @@ export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
GAIADIR=$topsrcdir/gaia
# Include Firefox OS fonts.
MOZTTDIR=$topsrcdir/moztt
MOZTTDIR=$topsrcdir/moz-tt
. "$topsrcdir/b2g/config/mozconfigs/common.override"

View File

@ -30,7 +30,7 @@ export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
GAIADIR=$topsrcdir/gaia
# Include Firefox OS fonts.
MOZTTDIR=$topsrcdir/moztt
MOZTTDIR=$topsrcdir/moz-tt
# Build simulator xpi and phone tweaks for b2g-desktop
FXOS_SIMULATOR=1

View File

@ -29,6 +29,6 @@ export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
GAIADIR=$topsrcdir/gaia
# Include Firefox OS fonts.
MOZTTDIR=$topsrcdir/moztt
MOZTTDIR=$topsrcdir/moz-tt
. "$topsrcdir/b2g/config/mozconfigs/common.override"

View File

@ -27,7 +27,7 @@ export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
GAIADIR=$topsrcdir/gaia
# Include Firefox OS fonts.
MOZTTDIR=$topsrcdir/moztt
MOZTTDIR=$topsrcdir/moz-tt
# Build simulator xpi and phone tweaks for b2g-desktop
FXOS_SIMULATOR=1

View File

@ -14,10 +14,10 @@
"unpack": true
},
{
"size": 31057326,
"digest": "b844c3e52be493d2cacafa58c4a924b89c9be8d2dcc2a7c71aed58c253d8035fba4d51df309f73e3c4342a1f3c3898a9a25c4815e2112888d1280f43c41c8e51",
"size": 31078810,
"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
"algorithm": "sha512",
"filename": "moztt.tar.bz2",
"filename": "moz-tt.tar.bz2",
"unpack": true
}
]

View File

@ -14,10 +14,10 @@
"unpack": true
},
{
"size": 31057326,
"digest": "b844c3e52be493d2cacafa58c4a924b89c9be8d2dcc2a7c71aed58c253d8035fba4d51df309f73e3c4342a1f3c3898a9a25c4815e2112888d1280f43c41c8e51",
"size": 31078810,
"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
"algorithm": "sha512",
"filename": "moztt.tar.bz2",
"filename": "moz-tt.tar.bz2",
"unpack": true
}
]

View File

@ -17,10 +17,10 @@
"unpack": true
},
{
"size": 31057326,
"digest": "b844c3e52be493d2cacafa58c4a924b89c9be8d2dcc2a7c71aed58c253d8035fba4d51df309f73e3c4342a1f3c3898a9a25c4815e2112888d1280f43c41c8e51",
"size": 31078810,
"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
"algorithm": "sha512",
"filename": "moztt.tar.bz2",
"filename": "moz-tt.tar.bz2",
"unpack": true
}
]

View File

@ -6,10 +6,10 @@
"filename": "mozmake.exe"
},
{
"size": 31057326,
"digest": "b844c3e52be493d2cacafa58c4a924b89c9be8d2dcc2a7c71aed58c253d8035fba4d51df309f73e3c4342a1f3c3898a9a25c4815e2112888d1280f43c41c8e51",
"size": 31078810,
"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
"algorithm": "sha512",
"filename": "moztt.tar.bz2",
"filename": "moz-tt.tar.bz2",
"unpack": true
},
{

View File

@ -8,4 +8,4 @@ ac_add_options --enable-default-toolkit=cairo-gtk2
ac_add_options --enable-application=b2g/dev
# Include Firefox OS fonts.
MOZTTDIR=$topsrcdir/moztt
MOZTTDIR=$topsrcdir/moz-tt

View File

@ -24,7 +24,7 @@ ac_add_options --enable-warnings-as-errors
export MOZ_PACKAGE_JSSHELL=1
# Include Firefox OS fonts.
MOZTTDIR=$topsrcdir/moztt
MOZTTDIR=$topsrcdir/moz-tt
. "$topsrcdir/build/mozconfig.common.override"
. "$topsrcdir/build/mozconfig.cache"

View File

@ -10,4 +10,4 @@ MOZ_AUTOMATION_SDK=0
ac_add_options --enable-application=b2g/dev
# Include Firefox OS fonts.
MOZTTDIR=$topsrcdir/moztt
MOZTTDIR=$topsrcdir/moz-tt

View File

@ -14,10 +14,10 @@
"unpack": true
},
{
"size": 31057326,
"digest": "b844c3e52be493d2cacafa58c4a924b89c9be8d2dcc2a7c71aed58c253d8035fba4d51df309f73e3c4342a1f3c3898a9a25c4815e2112888d1280f43c41c8e51",
"size": 31078810,
"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
"algorithm": "sha512",
"filename": "moztt.tar.bz2",
"filename": "moz-tt.tar.bz2",
"unpack": true
}
]

View File

@ -17,10 +17,10 @@
"unpack": true
},
{
"size": 31057326,
"digest": "b844c3e52be493d2cacafa58c4a924b89c9be8d2dcc2a7c71aed58c253d8035fba4d51df309f73e3c4342a1f3c3898a9a25c4815e2112888d1280f43c41c8e51",
"size": 31078810,
"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
"algorithm": "sha512",
"filename": "moztt.tar.bz2",
"filename": "moz-tt.tar.bz2",
"unpack": true
}
]

View File

@ -13,10 +13,10 @@
"unpack": true
},
{
"size": 31057326,
"digest": "b844c3e52be493d2cacafa58c4a924b89c9be8d2dcc2a7c71aed58c253d8035fba4d51df309f73e3c4342a1f3c3898a9a25c4815e2112888d1280f43c41c8e51",
"size": 31078810,
"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
"algorithm": "sha512",
"filename": "moztt.tar.bz2",
"filename": "moz-tt.tar.bz2",
"unpack": true
}
]

View File

@ -644,6 +644,7 @@
@RESPATH@/components/AppsService.manifest
@RESPATH@/components/Push.js
@RESPATH@/components/Push.manifest
@RESPATH@/components/PushClient.js
@RESPATH@/components/PushNotificationService.js
@RESPATH@/components/PushServiceLauncher.js
@ -710,6 +711,7 @@
@RESPATH@/components/nsUrlClassifierHashCompleter.js
@RESPATH@/components/nsUrlClassifierListManager.js
@RESPATH@/components/nsUrlClassifierLib.js
@RESPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js
@RESPATH@/components/url-classifier.xpt
; GNOME hooks

View File

@ -148,8 +148,13 @@ let wrapper = {
if (accountData.customizeSync) {
Services.prefs.setBoolPref(PREF_SYNC_SHOW_CUSTOMIZATION, true);
delete accountData.customizeSync;
}
delete accountData.customizeSync;
// sessionTokenContext is erroneously sent by the content server.
// https://github.com/mozilla/fxa-content-server/issues/2766
// To avoid having the FxA storage manager not knowing what to do with
// it we delete it here.
delete accountData.sessionTokenContext;
// We need to confirm a relink - see shouldAllowRelink for more
let newAccountEmail = accountData.email;

View File

@ -116,8 +116,12 @@ let TrackingProtection = {
// Add the current host in the 'trackingprotection' consumer of
// the permission manager using a normalized URI. This effectively
// places this host on the tracking protection allowlist.
Services.perms.add(normalizedUrl,
"trackingprotection", Services.perms.ALLOW_ACTION);
if (PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser)) {
PrivateBrowsingUtils.addToTrackingAllowlist(normalizedUrl);
} else {
Services.perms.add(normalizedUrl,
"trackingprotection", Services.perms.ALLOW_ACTION);
}
// Telemetry for disable protection.
this.eventsHistogram.add(1);
@ -133,8 +137,11 @@ let TrackingProtection = {
"https://" + gBrowser.selectedBrowser.currentURI.hostPort,
null, null);
Services.perms.remove(normalizedUrl,
"trackingprotection");
if (PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser)) {
PrivateBrowsingUtils.removeFromTrackingAllowlist(normalizedUrl);
} else {
Services.perms.remove(normalizedUrl, "trackingprotection");
}
// Telemetry for enable protection.
this.eventsHistogram.add(2);

View File

@ -431,6 +431,10 @@ tags = trackingprotection
support-files =
trackingPage.html
benignPage.html
[browser_trackingUI_5.js]
tags = trackingprotection
support-files =
trackingPage.html
[browser_typeAheadFind.js]
skip-if = buildapp == 'mulet'
[browser_unknownContentType_title.js]

View File

@ -0,0 +1,122 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that sites added to the Tracking Protection whitelist in private
// browsing mode don't persist once the private browsing window closes.
const PB_PREF = "privacy.trackingprotection.pbmode.enabled";
const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/general/trackingPage.html";
let TrackingProtection = null;
let browser = null;
let {UrlClassifierTestUtils} = Cu.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
registerCleanupFunction(function() {
TrackingProtection = browser = null;
UrlClassifierTestUtils.cleanupTestTrackers();
});
function hidden(sel) {
let win = browser.ownerGlobal;
let el = win.document.querySelector(sel);
let display = win.getComputedStyle(el).getPropertyValue("display", null);
return display === "none";
}
function clickButton(sel) {
let win = browser.ownerGlobal;
let el = win.document.querySelector(sel);
el.doCommand();
}
function testTrackingPage(window) {
info("Tracking content must be blocked");
ok(!TrackingProtection.container.hidden, "The container is visible");
is(TrackingProtection.content.getAttribute("state"), "blocked-tracking-content",
'content: state="blocked-tracking-content"');
is(TrackingProtection.icon.getAttribute("state"), "blocked-tracking-content",
'icon: state="blocked-tracking-content"');
ok(!hidden("#tracking-protection-icon"), "icon is visible");
ok(hidden("#tracking-action-block"), "blockButton is hidden");
ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
ok(!hidden("#tracking-action-unblock-private"), "unblockButtonPrivate is visible");
// Make sure that the blocked tracking elements message appears
ok(hidden("#tracking-not-detected"), "labelNoTracking is hidden");
ok(hidden("#tracking-loaded"), "labelTrackingLoaded is hidden");
ok(!hidden("#tracking-blocked"), "labelTrackingBlocked is visible");
}
function testTrackingPageUnblocked() {
info("Tracking content must be white-listed and not blocked");
ok(!TrackingProtection.container.hidden, "The container is visible");
is(TrackingProtection.content.getAttribute("state"), "loaded-tracking-content",
'content: state="loaded-tracking-content"');
is(TrackingProtection.icon.getAttribute("state"), "loaded-tracking-content",
'icon: state="loaded-tracking-content"');
ok(!hidden("#tracking-protection-icon"), "icon is visible");
ok(!hidden("#tracking-action-block"), "blockButton is visible");
ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
// Make sure that the blocked tracking elements message appears
ok(hidden("#tracking-not-detected"), "labelNoTracking is hidden");
ok(!hidden("#tracking-loaded"), "labelTrackingLoaded is visible");
ok(hidden("#tracking-blocked"), "labelTrackingBlocked is hidden");
}
add_task(function* testExceptionAddition() {
yield UrlClassifierTestUtils.addTestTrackers();
let privateWin = yield promiseOpenAndLoadWindow({private: true}, true);
browser = privateWin.gBrowser;
let tab = browser.selectedTab = browser.addTab();
TrackingProtection = browser.ownerGlobal.TrackingProtection;
yield pushPrefs([PB_PREF, true]);
ok(TrackingProtection.enabled, "TP is enabled after setting the pref");
info("Load a test page containing tracking elements");
yield promiseTabLoadEvent(tab, TRACKING_PAGE);
testTrackingPage(tab.ownerDocument.defaultView);
info("Disable TP for the page (which reloads the page)");
let tabReloadPromise = promiseTabLoadEvent(tab);
clickButton("#tracking-action-unblock");
yield tabReloadPromise;
testTrackingPageUnblocked();
info("Test that the exception is remembered across tabs in the same private window");
tab = browser.selectedTab = browser.addTab();
info("Load a test page containing tracking elements");
yield promiseTabLoadEvent(tab, TRACKING_PAGE);
testTrackingPageUnblocked();
yield promiseWindowClosed(privateWin);
});
add_task(function* testExceptionPersistence() {
info("Open another private browsing window");
let privateWin = yield promiseOpenAndLoadWindow({private: true}, true);
browser = privateWin.gBrowser;
let tab = browser.selectedTab = browser.addTab();
TrackingProtection = browser.ownerGlobal.TrackingProtection;
ok(TrackingProtection.enabled, "TP is still enabled");
info("Load a test page containing tracking elements");
yield promiseTabLoadEvent(tab, TRACKING_PAGE);
testTrackingPage(tab.ownerDocument.defaultView);
info("Disable TP for the page (which reloads the page)");
let tabReloadPromise = promiseTabLoadEvent(tab);
clickButton("#tracking-action-unblock");
yield tabReloadPromise;
testTrackingPageUnblocked();
privateWin.close();
});

View File

@ -1,7 +1,5 @@
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
@ -35,18 +33,14 @@ function promiseInitContentBlocklistSvc(aBrowser)
* @returns a Promise that resolves to true after the time has elapsed
*/
function waitForMs(aMs) {
let deferred = Promise.defer();
let startTime = Date.now();
setTimeout(done, aMs);
function done() {
deferred.resolve(true);
}
return deferred.promise;
return new Promise((resolve) => {
setTimeout(done, aMs);
function done() {
resolve(true);
}
});
}
// DOM Promise fails for unknown reasons here, so we're using
// resource://gre/modules/Promise.jsm.
function waitForEvent(subject, eventName, checkFn, useCapture, useUntrusted) {
return new Promise((resolve, reject) => {
subject.addEventListener(eventName, function listener(event) {
@ -84,33 +78,33 @@ function waitForEvent(subject, eventName, checkFn, useCapture, useUntrusted) {
* @rejects if a valid load event is not received within a meaningful interval
*/
function promiseTabLoadEvent(tab, url, eventType="load") {
let deferred = Promise.defer();
info("Wait tab event: " + eventType);
return new Promise((resolve, reject) => {
info("Wait tab event: " + eventType);
function handle(event) {
if (event.originalTarget != tab.linkedBrowser.contentDocument ||
event.target.location.href == "about:blank" ||
(url && event.target.location.href != url)) {
info("Skipping spurious '" + eventType + "'' event" +
" for " + event.target.location.href);
return;
function handle(event) {
if (event.originalTarget != tab.linkedBrowser.contentDocument ||
event.target.location.href == "about:blank" ||
(url && event.target.location.href != url)) {
info("Skipping spurious '" + eventType + "'' event" +
" for " + event.target.location.href);
return;
}
clearTimeout(timeout);
tab.linkedBrowser.removeEventListener(eventType, handle, true);
info("Tab event received: " + eventType);
resolve(event);
}
clearTimeout(timeout);
tab.linkedBrowser.removeEventListener(eventType, handle, true);
info("Tab event received: " + eventType);
deferred.resolve(event);
}
let timeout = setTimeout(() => {
tab.linkedBrowser.removeEventListener(eventType, handle, true);
deferred.reject(new Error("Timed out while waiting for a '" + eventType + "'' event"));
}, 30000);
let timeout = setTimeout(() => {
tab.linkedBrowser.removeEventListener(eventType, handle, true);
reject(new Error("Timed out while waiting for a '" + eventType + "'' event"));
}, 30000);
tab.linkedBrowser.addEventListener(eventType, handle, true, true);
if (url) {
tab.linkedBrowser.loadURI(url);
}
return deferred.promise;
tab.linkedBrowser.addEventListener(eventType, handle, true, true);
if (url) {
tab.linkedBrowser.loadURI(url);
}
});
}
function waitForCondition(condition, nextTest, errorMsg, aTries, aWait) {
@ -139,11 +133,11 @@ function waitForCondition(condition, nextTest, errorMsg, aTries, aWait) {
// Waits for a conditional function defined by the caller to return true.
function promiseForCondition(aConditionFn, aMessage, aTries, aWait) {
let deferred = Promise.defer();
waitForCondition(aConditionFn, deferred.resolve,
(aMessage || "Condition didn't pass."),
aTries, aWait);
return deferred.promise;
return new Promise((resolve) => {
waitForCondition(aConditionFn, resolve,
(aMessage || "Condition didn't pass."),
aTries, aWait);
});
}
// Returns the chrome side nsIPluginTag for this plugin
@ -298,17 +292,15 @@ function resetBlocklist() {
// Insure there's a popup notification present. This test does not indicate
// open state. aBrowser can be undefined.
function promisePopupNotification(aName, aBrowser) {
let deferred = Promise.defer();
return new Promise((resolve) => {
waitForCondition(() => PopupNotifications.getNotification(aName, aBrowser),
() => {
ok(!!PopupNotifications.getNotification(aName, aBrowser),
aName + " notification appeared");
waitForCondition(() => PopupNotifications.getNotification(aName, aBrowser),
() => {
ok(!!PopupNotifications.getNotification(aName, aBrowser),
aName + " notification appeared");
deferred.resolve();
}, "timeout waiting for popup notification " + aName);
return deferred.promise;
resolve();
}, "timeout waiting for popup notification " + aName);
});
}
/**
@ -361,9 +353,9 @@ function waitForNotificationBar(notificationID, browser, callback) {
}
function promiseForNotificationBar(notificationID, browser) {
let deferred = Promise.defer();
waitForNotificationBar(notificationID, browser, deferred.resolve);
return deferred.promise;
return new Promise((resolve) => {
waitForNotificationBar(notificationID, browser, resolve);
});
}
/**
@ -386,9 +378,9 @@ function waitForNotificationShown(notification, callback) {
}
function promiseForNotificationShown(notification) {
let deferred = Promise.defer();
waitForNotificationShown(notification, deferred.resolve);
return deferred.promise;
return new Promise((resolve) => {
waitForNotificationShown(notification, resolve);
});
}
/**

View File

@ -957,9 +957,8 @@ const CustomizableWidgets = [
type: "custom",
label: "loop-call-button3.label",
tooltiptext: "loop-call-button3.tooltiptext",
privateBrowsingTooltiptext: "loop-call-button3-pb.tooltiptext",
defaultArea: CustomizableUI.AREA_NAVBAR,
// Not in private browsing, see bug 1108187.
showInPrivateBrowsing: false,
introducedInVersion: 4,
onBuild: function(aDocument) {
// If we're not supposed to see the button, return zip.
@ -967,13 +966,21 @@ const CustomizableWidgets = [
return null;
}
let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(aDocument.defaultView);
let node = aDocument.createElementNS(kNSXUL, "toolbarbutton");
node.setAttribute("id", this.id);
node.classList.add("toolbarbutton-1");
node.classList.add("chromeclass-toolbar-additional");
node.classList.add("badged-button");
node.setAttribute("label", CustomizableUI.getLocalizedProperty(this, "label"));
node.setAttribute("tooltiptext", CustomizableUI.getLocalizedProperty(this, "tooltiptext"));
if (isWindowPrivate)
node.setAttribute("disabled", "true");
let tooltiptext = isWindowPrivate ?
CustomizableUI.getLocalizedProperty(this, "privateBrowsingTooltiptext",
[CustomizableUI.getLocalizedProperty(this, "label")]) :
CustomizableUI.getLocalizedProperty(this, "tooltiptext");
node.setAttribute("tooltiptext", tooltiptext);
node.setAttribute("removable", "true");
node.addEventListener("command", function(event) {
aDocument.defaultView.LoopUI.togglePanel(event);

View File

@ -119,7 +119,7 @@ function configureFxAccountIdentity() {
let storageManager = new MockFxaStorageManager();
// and init storage with our user.
storageManager.initialize(user);
return new AccountState(this, storageManager);
return new AccountState(storageManager);
},
getCertificate(data, keyPair, mustBeValidUntil) {
this.cert = {

View File

@ -570,13 +570,15 @@ loop.conversationViews = (function(mozL10n) {
var OngoingConversationView = React.createClass({displayName: "OngoingConversationView",
mixins: [
loop.store.StoreMixin("conversationStore"),
sharedMixins.MediaSetupMixin
],
propTypes: {
// local
audio: React.PropTypes.object,
// We pass conversationStore here rather than use the mixin, to allow
// easy configurability for the ui-showcase.
conversationStore: React.PropTypes.instanceOf(loop.store.ConversationStore).isRequired,
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
// The poster URLs are for UI-showcase testing and development.
localPosterUrl: React.PropTypes.string,
@ -597,7 +599,17 @@ loop.conversationViews = (function(mozL10n) {
},
getInitialState: function() {
return this.getStoreState();
return this.props.conversationStore.getStoreState();
},
componentWillMount: function() {
this.props.conversationStore.on("change", function() {
this.setState(this.props.conversationStore.getStoreState());
}, this);
},
componentWillUnmount: function() {
this.props.conversationStore.off("change", null, this);
},
componentDidMount: function() {
@ -633,6 +645,30 @@ loop.conversationViews = (function(mozL10n) {
}));
},
/**
* Should we render a visual cue to the user (e.g. a spinner) that a local
* stream is on its way from the camera?
*
* @returns {boolean}
* @private
*/
_isLocalLoading: function () {
return !this.state.localSrcVideoObject && !this.props.localPosterUrl;
},
/**
* Should we render a visual cue to the user (e.g. a spinner) that a remote
* stream is on its way from the other user?
*
* @returns {boolean}
* @private
*/
_isRemoteLoading: function() {
return !!(!this.state.remoteSrcVideoObject &&
!this.props.remotePosterUrl &&
!this.state.mediaConnected);
},
shouldRenderRemoteVideo: function() {
if (this.props.mediaConnected) {
// If remote video is not enabled, we're muted, so we'll show an avatar
@ -646,41 +682,32 @@ loop.conversationViews = (function(mozL10n) {
},
render: function() {
var localStreamClasses = React.addons.classSet({
local: true,
"local-stream": true,
"local-stream-audio": !this.props.video.enabled
});
return (
React.createElement("div", {className: "video-layout-wrapper"},
React.createElement("div", {className: "conversation"},
React.createElement("div", {className: "media nested"},
React.createElement("div", {className: "video_wrapper remote_wrapper"},
React.createElement("div", {className: "video_inner remote focus-stream"},
React.createElement(sharedViews.MediaView, {displayAvatar: !this.shouldRenderRemoteVideo(),
isLoading: false,
mediaType: "remote",
posterUrl: this.props.remotePosterUrl,
srcVideoObject: this.state.remoteSrcVideoObject})
)
),
React.createElement("div", {className: localStreamClasses},
React.createElement(sharedViews.MediaView, {displayAvatar: !this.props.video.enabled,
isLoading: false,
mediaType: "local",
posterUrl: this.props.localPosterUrl,
srcVideoObject: this.state.localSrcVideoObject})
)
),
React.createElement(loop.shared.views.ConversationToolbar, {
audio: this.props.audio,
dispatcher: this.props.dispatcher,
edit: { visible: false, enabled: false},
hangup: this.hangup,
publishStream: this.publishStream,
video: this.props.video})
)
React.createElement("div", {className: "desktop-call-wrapper"},
React.createElement(sharedViews.MediaLayoutView, {
dispatcher: this.props.dispatcher,
displayScreenShare: false,
isLocalLoading: this._isLocalLoading(),
isRemoteLoading: this._isRemoteLoading(),
isScreenShareLoading: false,
localPosterUrl: this.props.localPosterUrl,
localSrcVideoObject: this.state.localSrcVideoObject,
localVideoMuted: !this.props.video.enabled,
matchMedia: this.state.matchMedia || window.matchMedia.bind(window),
remotePosterUrl: this.props.remotePosterUrl,
remoteSrcVideoObject: this.state.remoteSrcVideoObject,
renderRemoteVideo: this.shouldRenderRemoteVideo(),
screenSharePosterUrl: null,
screenShareVideoObject: this.state.screenShareVideoObject,
showContextRoomName: false,
useDesktopPaths: true}),
React.createElement(loop.shared.views.ConversationToolbar, {
audio: this.props.audio,
dispatcher: this.props.dispatcher,
edit: { visible: false, enabled: false},
hangup: this.hangup,
publishStream: this.publishStream,
video: this.props.video})
)
);
}
@ -778,6 +805,7 @@ loop.conversationViews = (function(mozL10n) {
case CALL_STATES.ONGOING: {
return (React.createElement(OngoingConversationView, {
audio: {enabled: !this.state.audioMuted},
conversationStore: this.getStore(),
dispatcher: this.props.dispatcher,
mediaConnected: this.state.mediaConnected,
remoteSrcVideoObject: this.state.remoteSrcVideoObject,

View File

@ -570,13 +570,15 @@ loop.conversationViews = (function(mozL10n) {
var OngoingConversationView = React.createClass({
mixins: [
loop.store.StoreMixin("conversationStore"),
sharedMixins.MediaSetupMixin
],
propTypes: {
// local
audio: React.PropTypes.object,
// We pass conversationStore here rather than use the mixin, to allow
// easy configurability for the ui-showcase.
conversationStore: React.PropTypes.instanceOf(loop.store.ConversationStore).isRequired,
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
// The poster URLs are for UI-showcase testing and development.
localPosterUrl: React.PropTypes.string,
@ -597,7 +599,17 @@ loop.conversationViews = (function(mozL10n) {
},
getInitialState: function() {
return this.getStoreState();
return this.props.conversationStore.getStoreState();
},
componentWillMount: function() {
this.props.conversationStore.on("change", function() {
this.setState(this.props.conversationStore.getStoreState());
}, this);
},
componentWillUnmount: function() {
this.props.conversationStore.off("change", null, this);
},
componentDidMount: function() {
@ -633,6 +645,30 @@ loop.conversationViews = (function(mozL10n) {
}));
},
/**
* Should we render a visual cue to the user (e.g. a spinner) that a local
* stream is on its way from the camera?
*
* @returns {boolean}
* @private
*/
_isLocalLoading: function () {
return !this.state.localSrcVideoObject && !this.props.localPosterUrl;
},
/**
* Should we render a visual cue to the user (e.g. a spinner) that a remote
* stream is on its way from the other user?
*
* @returns {boolean}
* @private
*/
_isRemoteLoading: function() {
return !!(!this.state.remoteSrcVideoObject &&
!this.props.remotePosterUrl &&
!this.state.mediaConnected);
},
shouldRenderRemoteVideo: function() {
if (this.props.mediaConnected) {
// If remote video is not enabled, we're muted, so we'll show an avatar
@ -646,41 +682,32 @@ loop.conversationViews = (function(mozL10n) {
},
render: function() {
var localStreamClasses = React.addons.classSet({
local: true,
"local-stream": true,
"local-stream-audio": !this.props.video.enabled
});
return (
<div className="video-layout-wrapper">
<div className="conversation">
<div className="media nested">
<div className="video_wrapper remote_wrapper">
<div className="video_inner remote focus-stream">
<sharedViews.MediaView displayAvatar={!this.shouldRenderRemoteVideo()}
isLoading={false}
mediaType="remote"
posterUrl={this.props.remotePosterUrl}
srcVideoObject={this.state.remoteSrcVideoObject} />
</div>
</div>
<div className={localStreamClasses}>
<sharedViews.MediaView displayAvatar={!this.props.video.enabled}
isLoading={false}
mediaType="local"
posterUrl={this.props.localPosterUrl}
srcVideoObject={this.state.localSrcVideoObject} />
</div>
</div>
<loop.shared.views.ConversationToolbar
audio={this.props.audio}
dispatcher={this.props.dispatcher}
edit={{ visible: false, enabled: false }}
hangup={this.hangup}
publishStream={this.publishStream}
video={this.props.video} />
</div>
<div className="desktop-call-wrapper">
<sharedViews.MediaLayoutView
dispatcher={this.props.dispatcher}
displayScreenShare={false}
isLocalLoading={this._isLocalLoading()}
isRemoteLoading={this._isRemoteLoading()}
isScreenShareLoading={false}
localPosterUrl={this.props.localPosterUrl}
localSrcVideoObject={this.state.localSrcVideoObject}
localVideoMuted={!this.props.video.enabled}
matchMedia={this.state.matchMedia || window.matchMedia.bind(window)}
remotePosterUrl={this.props.remotePosterUrl}
remoteSrcVideoObject={this.state.remoteSrcVideoObject}
renderRemoteVideo={this.shouldRenderRemoteVideo()}
screenSharePosterUrl={null}
screenShareVideoObject={this.state.screenShareVideoObject}
showContextRoomName={false}
useDesktopPaths={true} />
<loop.shared.views.ConversationToolbar
audio={this.props.audio}
dispatcher={this.props.dispatcher}
edit={{ visible: false, enabled: false }}
hangup={this.hangup}
publishStream={this.publishStream}
video={this.props.video} />
</div>
);
}
@ -778,6 +805,7 @@ loop.conversationViews = (function(mozL10n) {
case CALL_STATES.ONGOING: {
return (<OngoingConversationView
audio={{enabled: !this.state.audioMuted}}
conversationStore={this.getStore()}
dispatcher={this.props.dispatcher}
mediaConnected={this.state.mediaConnected}
remoteSrcVideoObject={this.state.remoteSrcVideoObject}

View File

@ -665,7 +665,7 @@ loop.roomViews = (function(mozL10n) {
* @returns {boolean}
* @private
*/
_shouldRenderLocalLoading: function () {
_isLocalLoading: function () {
return this.state.roomState === ROOM_STATES.MEDIA_WAIT &&
!this.state.localSrcVideoObject;
},
@ -677,7 +677,7 @@ loop.roomViews = (function(mozL10n) {
* @returns {boolean}
* @private
*/
_shouldRenderRemoteLoading: function() {
_isRemoteLoading: function() {
return !!(this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
!this.state.remoteSrcVideoObject &&
!this.state.mediaConnected);
@ -741,63 +741,54 @@ loop.roomViews = (function(mozL10n) {
return null;
}
default: {
return (
React.createElement("div", {className: "room-conversation-wrapper"},
React.createElement("div", {className: "video-layout-wrapper"},
React.createElement("div", {className: "conversation room-conversation"},
React.createElement("div", {className: "media nested"},
React.createElement(DesktopRoomInvitationView, {
dispatcher: this.props.dispatcher,
error: this.state.error,
mozLoop: this.props.mozLoop,
onAddContextClick: this.handleAddContextClick,
onEditContextClose: this.handleEditContextClose,
roomData: roomData,
savingContext: this.state.savingContext,
show: shouldRenderInvitationOverlay,
showEditContext: shouldRenderInvitationOverlay && shouldRenderEditContextView,
socialShareProviders: this.state.socialShareProviders}),
React.createElement("div", {className: "video_wrapper remote_wrapper"},
React.createElement("div", {className: "video_inner remote focus-stream"},
React.createElement(sharedViews.MediaView, {displayAvatar: !this.shouldRenderRemoteVideo(),
isLoading: this._shouldRenderRemoteLoading(),
mediaType: "remote",
posterUrl: this.props.remotePosterUrl,
srcVideoObject: this.state.remoteSrcVideoObject})
)
),
React.createElement("div", {className: localStreamClasses},
React.createElement(sharedViews.MediaView, {displayAvatar: this.state.videoMuted,
isLoading: this._shouldRenderLocalLoading(),
mediaType: "local",
posterUrl: this.props.localPosterUrl,
srcVideoObject: this.state.localSrcVideoObject})
),
React.createElement(DesktopRoomEditContextView, {
dispatcher: this.props.dispatcher,
error: this.state.error,
mozLoop: this.props.mozLoop,
onClose: this.handleEditContextClose,
roomData: roomData,
savingContext: this.state.savingContext,
show: !shouldRenderInvitationOverlay && shouldRenderEditContextView})
),
React.createElement(sharedViews.ConversationToolbar, {
audio: {enabled: !this.state.audioMuted, visible: true},
dispatcher: this.props.dispatcher,
edit: { visible: this.state.contextEnabled, enabled: !this.state.showEditContext},
hangup: this.leaveRoom,
onEditClick: this.handleEditContextClick,
publishStream: this.publishStream,
screenShare: screenShareData,
video: {enabled: !this.state.videoMuted, visible: true}})
)
),
React.createElement(sharedViews.chat.TextChatView, {
React.createElement("div", {className: "room-conversation-wrapper desktop-room-wrapper"},
React.createElement(sharedViews.MediaLayoutView, {
dispatcher: this.props.dispatcher,
showRoomName: false,
useDesktopPaths: true})
displayScreenShare: false,
isLocalLoading: this._isLocalLoading(),
isRemoteLoading: this._isRemoteLoading(),
isScreenShareLoading: false,
localPosterUrl: this.props.localPosterUrl,
localSrcVideoObject: this.state.localSrcVideoObject,
localVideoMuted: this.state.videoMuted,
matchMedia: this.state.matchMedia || window.matchMedia.bind(window),
remotePosterUrl: this.props.remotePosterUrl,
remoteSrcVideoObject: this.state.remoteSrcVideoObject,
renderRemoteVideo: this.shouldRenderRemoteVideo(),
screenSharePosterUrl: null,
screenShareVideoObject: this.state.screenShareVideoObject,
showContextRoomName: false,
useDesktopPaths: true},
React.createElement(DesktopRoomInvitationView, {
dispatcher: this.props.dispatcher,
error: this.state.error,
mozLoop: this.props.mozLoop,
onAddContextClick: this.handleAddContextClick,
onEditContextClose: this.handleEditContextClose,
roomData: roomData,
savingContext: this.state.savingContext,
show: shouldRenderInvitationOverlay,
showEditContext: shouldRenderInvitationOverlay && shouldRenderEditContextView,
socialShareProviders: this.state.socialShareProviders}),
React.createElement(DesktopRoomEditContextView, {
dispatcher: this.props.dispatcher,
error: this.state.error,
mozLoop: this.props.mozLoop,
onClose: this.handleEditContextClose,
roomData: roomData,
savingContext: this.state.savingContext,
show: !shouldRenderInvitationOverlay && shouldRenderEditContextView})
),
React.createElement(sharedViews.ConversationToolbar, {
audio: {enabled: !this.state.audioMuted, visible: true},
dispatcher: this.props.dispatcher,
edit: { visible: this.state.contextEnabled, enabled: !this.state.showEditContext},
hangup: this.leaveRoom,
onEditClick: this.handleEditContextClick,
publishStream: this.publishStream,
screenShare: screenShareData,
video: {enabled: !this.state.videoMuted, visible: true}})
)
);
}

View File

@ -665,7 +665,7 @@ loop.roomViews = (function(mozL10n) {
* @returns {boolean}
* @private
*/
_shouldRenderLocalLoading: function () {
_isLocalLoading: function () {
return this.state.roomState === ROOM_STATES.MEDIA_WAIT &&
!this.state.localSrcVideoObject;
},
@ -677,7 +677,7 @@ loop.roomViews = (function(mozL10n) {
* @returns {boolean}
* @private
*/
_shouldRenderRemoteLoading: function() {
_isRemoteLoading: function() {
return !!(this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
!this.state.remoteSrcVideoObject &&
!this.state.mediaConnected);
@ -741,63 +741,54 @@ loop.roomViews = (function(mozL10n) {
return null;
}
default: {
return (
<div className="room-conversation-wrapper">
<div className="video-layout-wrapper">
<div className="conversation room-conversation">
<div className="media nested">
<DesktopRoomInvitationView
dispatcher={this.props.dispatcher}
error={this.state.error}
mozLoop={this.props.mozLoop}
onAddContextClick={this.handleAddContextClick}
onEditContextClose={this.handleEditContextClose}
roomData={roomData}
savingContext={this.state.savingContext}
show={shouldRenderInvitationOverlay}
showEditContext={shouldRenderInvitationOverlay && shouldRenderEditContextView}
socialShareProviders={this.state.socialShareProviders} />
<div className="video_wrapper remote_wrapper">
<div className="video_inner remote focus-stream">
<sharedViews.MediaView displayAvatar={!this.shouldRenderRemoteVideo()}
isLoading={this._shouldRenderRemoteLoading()}
mediaType="remote"
posterUrl={this.props.remotePosterUrl}
srcVideoObject={this.state.remoteSrcVideoObject} />
</div>
</div>
<div className={localStreamClasses}>
<sharedViews.MediaView displayAvatar={this.state.videoMuted}
isLoading={this._shouldRenderLocalLoading()}
mediaType="local"
posterUrl={this.props.localPosterUrl}
srcVideoObject={this.state.localSrcVideoObject} />
</div>
<DesktopRoomEditContextView
dispatcher={this.props.dispatcher}
error={this.state.error}
mozLoop={this.props.mozLoop}
onClose={this.handleEditContextClose}
roomData={roomData}
savingContext={this.state.savingContext}
show={!shouldRenderInvitationOverlay && shouldRenderEditContextView} />
</div>
<sharedViews.ConversationToolbar
audio={{enabled: !this.state.audioMuted, visible: true}}
dispatcher={this.props.dispatcher}
edit={{ visible: this.state.contextEnabled, enabled: !this.state.showEditContext }}
hangup={this.leaveRoom}
onEditClick={this.handleEditContextClick}
publishStream={this.publishStream}
screenShare={screenShareData}
video={{enabled: !this.state.videoMuted, visible: true}} />
</div>
</div>
<sharedViews.chat.TextChatView
<div className="room-conversation-wrapper desktop-room-wrapper">
<sharedViews.MediaLayoutView
dispatcher={this.props.dispatcher}
showRoomName={false}
useDesktopPaths={true} />
displayScreenShare={false}
isLocalLoading={this._isLocalLoading()}
isRemoteLoading={this._isRemoteLoading()}
isScreenShareLoading={false}
localPosterUrl={this.props.localPosterUrl}
localSrcVideoObject={this.state.localSrcVideoObject}
localVideoMuted={this.state.videoMuted}
matchMedia={this.state.matchMedia || window.matchMedia.bind(window)}
remotePosterUrl={this.props.remotePosterUrl}
remoteSrcVideoObject={this.state.remoteSrcVideoObject}
renderRemoteVideo={this.shouldRenderRemoteVideo()}
screenSharePosterUrl={null}
screenShareVideoObject={this.state.screenShareVideoObject}
showContextRoomName={false}
useDesktopPaths={true}>
<DesktopRoomInvitationView
dispatcher={this.props.dispatcher}
error={this.state.error}
mozLoop={this.props.mozLoop}
onAddContextClick={this.handleAddContextClick}
onEditContextClose={this.handleEditContextClose}
roomData={roomData}
savingContext={this.state.savingContext}
show={shouldRenderInvitationOverlay}
showEditContext={shouldRenderInvitationOverlay && shouldRenderEditContextView}
socialShareProviders={this.state.socialShareProviders} />
<DesktopRoomEditContextView
dispatcher={this.props.dispatcher}
error={this.state.error}
mozLoop={this.props.mozLoop}
onClose={this.handleEditContextClose}
roomData={roomData}
savingContext={this.state.savingContext}
show={!shouldRenderInvitationOverlay && shouldRenderEditContextView} />
</sharedViews.MediaLayoutView>
<sharedViews.ConversationToolbar
audio={{enabled: !this.state.audioMuted, visible: true}}
dispatcher={this.props.dispatcher}
edit={{ visible: this.state.contextEnabled, enabled: !this.state.showEditContext }}
hangup={this.leaveRoom}
onEditClick={this.handleEditContextClick}
publishStream={this.publishStream}
screenShare={screenShareData}
video={{enabled: !this.state.videoMuted, visible: true}} />
</div>
);
}

View File

@ -504,28 +504,6 @@
text-align: center;
}
.fx-embedded .local-stream {
position: absolute;
right: 3px;
bottom: 5px;
/* next two lines are workaround for lack of object-fit; see bug 1020445 */
max-width: 140px;
width: 30%;
height: 28%;
max-height: 105px;
}
.fx-embedded .local-stream.room-preview {
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
height: 100%;
width: 100%;
max-width: none;
max-height: none;
}
.conversation .media.nested .focus-stream {
display: inline-block;
position: absolute; /* workaround for lack of object-fit; see bug 1020445 */
@ -592,15 +570,11 @@
z-index: 1;
}
.remote .avatar {
.remote > .avatar {
/* make visually distinct from local avatar */
opacity: 0.25;
}
.fx-embedded .media.nested {
min-height: 200px;
}
.fx-embedded-call-identifier {
display: inline;
width: 100%;
@ -675,7 +649,9 @@
* */
html, .fx-embedded, #main,
.video-layout-wrapper,
.conversation {
.conversation,
.desktop-call-wrapper,
.desktop-room-wrapper {
height: 100%;
}
@ -935,7 +911,6 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
border-top: 2px solid #444;
border-bottom: 2px solid #444;
padding: .5rem;
max-height: 400px;
position: absolute;
left: 0;
bottom: 0;
@ -951,7 +926,7 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
overflow-x: hidden;
overflow-y: auto;
/* Make the context view float atop the video elements. */
z-index: 2;
z-index: 3;
}
.room-invitation-overlay .room-context {
@ -1087,12 +1062,12 @@ html[dir="rtl"] .room-context-btn-close {
.standalone-room-wrapper > .media-layout {
/* 50px is the header, 64px for toolbar, 3em is the footer. */
height: calc(100% - 50px - 64px - 3em);
margin: 0 10px;
}
.media-layout > .media-wrapper {
display: flex;
flex-flow: column wrap;
margin: 0 10px;
height: 100%;
}
@ -1139,6 +1114,14 @@ html[dir="rtl"] .room-context-btn-close {
height: calc(100% - 300px);
}
.desktop-call-wrapper > .media-layout > .media-wrapper > .text-chat-view,
.desktop-room-wrapper > .media-layout > .media-wrapper > .text-chat-view {
/* Account for height of .conversation-toolbar on desktop */
/* When we change the toolbar in bug 1184559 we can remove this. */
margin-top: 26px;
height: calc(100% - 150px - 26px);
}
/* Temporarily slaved from .media-wrapper until we use it in more places
to avoid affecting the conversation window on desktop. */
.media-wrapper > .text-chat-view > .text-chat-entries {
@ -1204,7 +1187,7 @@ html[dir="rtl"] .room-context-btn-close {
/* Temporarily slaved from .media-wrapper until we use it in more places
to avoid affecting the conversation window on desktop. */
.media-wrapper > .text-chat-view > .text-chat-entries {
.text-chat-view > .text-chat-entries {
/* 40px is the height of .text-chat-box. */
height: calc(100% - 40px);
width: 100%;
@ -1215,20 +1198,26 @@ html[dir="rtl"] .room-context-btn-close {
height: 100%;
}
.media-wrapper > .local {
.media-wrapper > .focus-stream > .local {
/* Position over the remote video */
position: absolute;
/* Make sure its on top */
z-index: 1001;
z-index: 2;
margin: 3px;
right: 0;
/* 29px is (30% of 50px high header) + (height toolbar (38px) +
height footer (25px) - height header (50px)) */
bottom: calc(30% + 29px);
bottom: 0;
width: 120px;
height: 120px;
}
.standalone-room-wrapper > .media-layout > .media-wrapper > .local {
/* Add 10px for the margin on standalone */
right: 10px;
}
html[dir="rtl"] .media-wrapper > .local {
right: auto;
left: 0;
@ -1247,6 +1236,15 @@ html[dir="rtl"] .room-context-btn-close {
height: 30%;
}
.desktop-call-wrapper > .media-layout > .media-wrapper > .text-chat-view,
.desktop-room-wrapper > .media-layout > .media-wrapper > .text-chat-view {
/* When we change the toolbar in bug 1184559 we can remove this. */
/* Reset back to 0 for .conversation-toolbar override on desktop */
margin-top: 0;
/* This is temp, to echo the .media-wrapper > .text-chat-view above */
height: 30%;
}
.media-wrapper.receiving-screen-share > .screen {
order: 1;
}
@ -1288,6 +1286,47 @@ html[dir="rtl"] .room-context-btn-close {
}
}
/* e.g. very narrow widths similar to conversation window */
@media screen and (max-width:300px) {
.media-layout > .media-wrapper {
flex-flow: column nowrap;
}
.media-wrapper > .focus-stream > .local {
position: absolute;
right: 0;
/* 30% is the height of the text chat. As we have a margin,
we don't need to worry about any offset for a border */
bottom: 0;
margin: 3px;
object-fit: contain;
/* These make the avatar look reasonable and the local
video not too big */
width: 25%;
height: 25%;
}
.media-wrapper:not(.showing-remote-streams) > .focus-stream > .no-video {
display: none;
}
.media-wrapper:not(.showing-remote-streams) > .focus-stream > .local {
position: relative;
margin: 0;
right: auto;
left: auto;
bottom: auto;
width: 100%;
height: 100%;
background-color: black;
}
.media-wrapper > .focus-stream {
flex: 1 1 auto;
height: auto;
}
}
.standalone > #main > .room-conversation-wrapper > .media-layout > .conversation-toolbar {
border: none;
}
@ -1415,37 +1454,12 @@ html[dir="rtl"] .standalone .room-conversation-wrapper .room-inner-info-area {
height: auto;
}
/* Text chat in rooms styles */
.fx-embedded .room-conversation-wrapper {
display: flex;
flex-flow: column nowrap;
}
.fx-embedded .video-layout-wrapper {
flex: 1 1 auto;
}
/* Text chat in styles */
.text-chat-view {
background: white;
}
.fx-embedded .text-chat-view {
flex: 1 0 auto;
display: flex;
flex-flow: column nowrap;
}
.fx-embedded .text-chat-entries {
flex: 1 1 auto;
max-height: 120px;
min-height: 60px;
}
.fx-embedded .text-chat-view > .text-chat-entries-empty {
display: none;
}
.text-chat-box {
flex: 0 0 auto;
max-height: 40px;
@ -1740,6 +1754,47 @@ html[dir="rtl"] .text-chat-entry.received .text-chat-arrow {
}
}
/* e.g. very narrow widths similar to conversation window */
@media screen and (max-width:300px) {
.text-chat-view {
flex: 0 0 auto;
display: flex;
flex-flow: column nowrap;
/* 120px max-height of .text-chat-entries plus 40px of .text-chat-box */
max-height: 160px;
/* 60px min-height of .text-chat-entries plus 40px of .text-chat-box */
min-height: 100px;
/* The !important is to override the values defined above which have more
specificity when we fix bug 1184559, we should be able to remove it,
but this should be tests first. */
height: auto !important;
}
.text-chat-entries {
/* The !important is to override the values defined above which have more
specificity when we fix bug 1184559, we should be able to remove it,
but this should be tests first. */
flex: 1 1 auto !important;
max-height: 120px;
min-height: 60px;
}
.text-chat-entries-empty.text-chat-disabled {
display: none;
}
/* When the text chat entries are not present, then hide the entries view
and just show the chat box. */
.text-chat-entries-empty {
max-height: 40px;
min-height: 40px;
}
.text-chat-entries-empty > .text-chat-entries {
display: none;
}
}
.self-view-hidden-message {
/* Not displayed by default; display is turned on elsewhere when the
* self-view is actually hidden.

View File

@ -580,21 +580,6 @@ loop.store.ActiveRoomStore = (function() {
* @param {sharedActions.ConnectionFailure} actionData
*/
connectionFailure: function(actionData) {
/**
* XXX This is a workaround for desktop machines that do not have a
* camera installed. As we don't yet have device enumeration, when
* we do, this can be removed (bug 1138851), and the sdk should handle it.
*/
if (this._isDesktop &&
actionData.reason === FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA &&
this.getStoreState().videoMuted === false) {
// We failed to publish with media, so due to the bug, we try again without
// video.
this.setStoreState({videoMuted: true});
this._sdkDriver.retryPublishWithoutVideo();
return;
}
var exitState = this._storeState.roomState === ROOM_STATES.FAILED ?
this._storeState.failureExitState : this._storeState.roomState;

View File

@ -146,21 +146,6 @@ loop.store = loop.store || {};
* @param {sharedActions.ConnectionFailure} actionData The action data.
*/
connectionFailure: function(actionData) {
/**
* XXX This is a workaround for desktop machines that do not have a
* camera installed. As we don't yet have device enumeration, when
* we do, this can be removed (bug 1138851), and the sdk should handle it.
*/
if (this._isDesktop &&
actionData.reason === FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA &&
this.getStoreState().videoMuted === false) {
// We failed to publish with media, so due to the bug, we try again without
// video.
this.setStoreState({videoMuted: true});
this.sdkDriver.retryPublishWithoutVideo();
return;
}
this._endSession();
this.setStoreState({
callState: CALL_STATES.TERMINATED,

View File

@ -61,15 +61,26 @@ loop.OTSdkDriver = (function() {
/**
* XXX This is a workaround for desktop machines that do not have a
* camera installed. As we don't yet have device enumeration, when
* we do, this can be removed (bug 1138851), and the sdk should handle it.
* camera installed. The SDK doesn't currently do use the new device
* enumeration apis, when it does (bug 1138851), we can drop this part.
*/
if (this._isDesktop && !window.MediaStreamTrack.getSources) {
if (this._isDesktop) {
// If there's no getSources function, the sdk defines its own and caches
// the result. So here we define the "normal" one which doesn't get cached, so
// we can change it later.
// the result. So here we define our own one which wraps around the
// real device enumeration api.
window.MediaStreamTrack.getSources = function(callback) {
callback([{kind: "audio"}, {kind: "video"}]);
navigator.mediaDevices.enumerateDevices().then(function(devices) {
var result = [];
devices.forEach(function(device) {
if (device.kind === "audioinput") {
result.push({kind: "audio"});
}
if (device.kind === "videoinput") {
result.push({kind: "video"});
}
});
callback(result);
});
};
}
};
@ -109,21 +120,13 @@ loop.OTSdkDriver = (function() {
this.sdk.on("exception", this._onOTException.bind(this));
// At this state we init the publisher, even though we might be waiting for
// the initial connect of the session. This saves time when setting up
// the media.
this._publishLocalStreams();
},
/**
* Internal function to publish a local stream.
* XXX This can be simplified when bug 1138851 is actioned.
*/
_publishLocalStreams: function() {
// We expect the local video to be muted automatically by the SDK. Hence
// we don't mute it manually here.
this._mockPublisherEl = document.createElement("div");
// At this state we init the publisher, even though we might be waiting for
// the initial connect of the session. This saves time when setting up
// the media.
this.publisher = this.sdk.initPublisher(this._mockPublisherEl,
_.extend(this._getDataChannelSettings, this._getCopyPublisherConfig));
@ -135,17 +138,6 @@ loop.OTSdkDriver = (function() {
this._onAccessDialogOpened.bind(this));
},
/**
* Forces the sdk into not using video, and starts publishing again.
* XXX This is part of the work around that will be removed by bug 1138851.
*/
retryPublishWithoutVideo: function() {
window.MediaStreamTrack.getSources = function(callback) {
callback([{kind: "audio"}]);
};
this._publishLocalStreams();
},
/**
* Handles the setMute action. Informs the published stream to mute
* or unmute audio as appropriate.

View File

@ -150,8 +150,7 @@ loop.shared.views.chat = (function(mozL10n) {
var lastTimestamp = 0;
var entriesClasses = React.addons.classSet({
"text-chat-entries": true,
"text-chat-entries-empty": !this.props.messageList.length
"text-chat-entries": true
});
return (
@ -382,7 +381,8 @@ loop.shared.views.chat = (function(mozL10n) {
var textChatViewClasses = React.addons.classSet({
"text-chat-view": true,
"text-chat-disabled": !this.state.textChatEnabled
"text-chat-disabled": !this.state.textChatEnabled,
"text-chat-entries-empty": !messageList.length
});
return (

View File

@ -150,8 +150,7 @@ loop.shared.views.chat = (function(mozL10n) {
var lastTimestamp = 0;
var entriesClasses = React.addons.classSet({
"text-chat-entries": true,
"text-chat-entries-empty": !this.props.messageList.length
"text-chat-entries": true
});
return (
@ -382,7 +381,8 @@ loop.shared.views.chat = (function(mozL10n) {
var textChatViewClasses = React.addons.classSet({
"text-chat-view": true,
"text-chat-disabled": !this.state.textChatEnabled
"text-chat-disabled": !this.state.textChatEnabled,
"text-chat-entries-empty": !messageList.length
});
return (

View File

@ -946,6 +946,7 @@ loop.shared.views = (function(_, mozL10n) {
var MediaLayoutView = React.createClass({displayName: "MediaLayoutView",
propTypes: {
children: React.PropTypes.node,
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
displayScreenShare: React.PropTypes.bool.isRequired,
isLocalLoading: React.PropTypes.bool.isRequired,
@ -955,6 +956,9 @@ loop.shared.views = (function(_, mozL10n) {
localPosterUrl: React.PropTypes.string,
localSrcVideoObject: React.PropTypes.object,
localVideoMuted: React.PropTypes.bool.isRequired,
// Passing in matchMedia, allows it to be overriden for ui-showcase's
// benefit. We expect either the override or window.matchMedia.
matchMedia: React.PropTypes.func.isRequired,
remotePosterUrl: React.PropTypes.string,
remoteSrcVideoObject: React.PropTypes.object,
renderRemoteVideo: React.PropTypes.bool.isRequired,
@ -964,6 +968,60 @@ loop.shared.views = (function(_, mozL10n) {
useDesktopPaths: React.PropTypes.bool.isRequired
},
isLocalMediaAbsolutelyPositioned: function(matchMedia) {
if (!matchMedia) {
matchMedia = this.props.matchMedia;
}
return matchMedia &&
// The screen width is less than 640px and we are not screen sharing.
((matchMedia("screen and (max-width:640px)").matches &&
!this.props.displayScreenShare) ||
// or the screen width is less than 300px.
(matchMedia("screen and (max-width:300px)").matches));
},
getInitialState: function() {
return {
localMediaAboslutelyPositioned: this.isLocalMediaAbsolutelyPositioned()
};
},
componentWillReceiveProps: function(nextProps) {
// This is all for the ui-showcase's benefit.
if (this.props.matchMedia != nextProps.matchMedia) {
this.updateLocalMediaState(null, nextProps.matchMedia);
}
},
componentDidMount: function() {
window.addEventListener("resize", this.updateLocalMediaState);
},
componentWillUnmount: function() {
window.removeEventListener("resize", this.updateLocalMediaState);
},
updateLocalMediaState: function(event, matchMedia) {
var newState = this.isLocalMediaAbsolutelyPositioned(matchMedia);
if (this.state.localMediaAboslutelyPositioned != newState) {
this.setState({
localMediaAboslutelyPositioned: newState
});
}
},
renderLocalVideo: function() {
return (
React.createElement("div", {className: "local"},
React.createElement(MediaView, {displayAvatar: this.props.localVideoMuted,
isLoading: this.props.isLocalLoading,
mediaType: "local",
posterUrl: this.props.localPosterUrl,
srcVideoObject: this.props.localSrcVideoObject})
)
);
},
render: function() {
var remoteStreamClasses = React.addons.classSet({
"remote": true,
@ -979,7 +1037,9 @@ loop.shared.views = (function(_, mozL10n) {
"media-wrapper": true,
"receiving-screen-share": this.props.displayScreenShare,
"showing-local-streams": this.props.localSrcVideoObject ||
this.props.localPosterUrl
this.props.localPosterUrl,
"showing-remote-streams": this.props.remoteSrcVideoObject ||
this.props.remotePosterUrl || this.props.isRemoteLoading
});
return (
@ -993,7 +1053,10 @@ loop.shared.views = (function(_, mozL10n) {
isLoading: this.props.isRemoteLoading,
mediaType: "remote",
posterUrl: this.props.remotePosterUrl,
srcVideoObject: this.props.remoteSrcVideoObject})
srcVideoObject: this.props.remoteSrcVideoObject}),
this.state.localMediaAboslutelyPositioned ?
this.renderLocalVideo() : null,
this.props.children
),
React.createElement("div", {className: screenShareStreamClasses},
React.createElement(MediaView, {displayAvatar: false,
@ -1006,13 +1069,8 @@ loop.shared.views = (function(_, mozL10n) {
dispatcher: this.props.dispatcher,
showRoomName: this.props.showContextRoomName,
useDesktopPaths: false}),
React.createElement("div", {className: "local"},
React.createElement(MediaView, {displayAvatar: this.props.localVideoMuted,
isLoading: this.props.isLocalLoading,
mediaType: "local",
posterUrl: this.props.localPosterUrl,
srcVideoObject: this.props.localSrcVideoObject})
)
this.state.localMediaAboslutelyPositioned ?
null : this.renderLocalVideo()
)
)
);

View File

@ -946,6 +946,7 @@ loop.shared.views = (function(_, mozL10n) {
var MediaLayoutView = React.createClass({
propTypes: {
children: React.PropTypes.node,
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
displayScreenShare: React.PropTypes.bool.isRequired,
isLocalLoading: React.PropTypes.bool.isRequired,
@ -955,6 +956,9 @@ loop.shared.views = (function(_, mozL10n) {
localPosterUrl: React.PropTypes.string,
localSrcVideoObject: React.PropTypes.object,
localVideoMuted: React.PropTypes.bool.isRequired,
// Passing in matchMedia, allows it to be overriden for ui-showcase's
// benefit. We expect either the override or window.matchMedia.
matchMedia: React.PropTypes.func.isRequired,
remotePosterUrl: React.PropTypes.string,
remoteSrcVideoObject: React.PropTypes.object,
renderRemoteVideo: React.PropTypes.bool.isRequired,
@ -964,6 +968,60 @@ loop.shared.views = (function(_, mozL10n) {
useDesktopPaths: React.PropTypes.bool.isRequired
},
isLocalMediaAbsolutelyPositioned: function(matchMedia) {
if (!matchMedia) {
matchMedia = this.props.matchMedia;
}
return matchMedia &&
// The screen width is less than 640px and we are not screen sharing.
((matchMedia("screen and (max-width:640px)").matches &&
!this.props.displayScreenShare) ||
// or the screen width is less than 300px.
(matchMedia("screen and (max-width:300px)").matches));
},
getInitialState: function() {
return {
localMediaAboslutelyPositioned: this.isLocalMediaAbsolutelyPositioned()
};
},
componentWillReceiveProps: function(nextProps) {
// This is all for the ui-showcase's benefit.
if (this.props.matchMedia != nextProps.matchMedia) {
this.updateLocalMediaState(null, nextProps.matchMedia);
}
},
componentDidMount: function() {
window.addEventListener("resize", this.updateLocalMediaState);
},
componentWillUnmount: function() {
window.removeEventListener("resize", this.updateLocalMediaState);
},
updateLocalMediaState: function(event, matchMedia) {
var newState = this.isLocalMediaAbsolutelyPositioned(matchMedia);
if (this.state.localMediaAboslutelyPositioned != newState) {
this.setState({
localMediaAboslutelyPositioned: newState
});
}
},
renderLocalVideo: function() {
return (
<div className="local">
<MediaView displayAvatar={this.props.localVideoMuted}
isLoading={this.props.isLocalLoading}
mediaType="local"
posterUrl={this.props.localPosterUrl}
srcVideoObject={this.props.localSrcVideoObject} />
</div>
);
},
render: function() {
var remoteStreamClasses = React.addons.classSet({
"remote": true,
@ -979,7 +1037,9 @@ loop.shared.views = (function(_, mozL10n) {
"media-wrapper": true,
"receiving-screen-share": this.props.displayScreenShare,
"showing-local-streams": this.props.localSrcVideoObject ||
this.props.localPosterUrl
this.props.localPosterUrl,
"showing-remote-streams": this.props.remoteSrcVideoObject ||
this.props.remotePosterUrl || this.props.isRemoteLoading
});
return (
@ -994,6 +1054,9 @@ loop.shared.views = (function(_, mozL10n) {
mediaType="remote"
posterUrl={this.props.remotePosterUrl}
srcVideoObject={this.props.remoteSrcVideoObject} />
{ this.state.localMediaAboslutelyPositioned ?
this.renderLocalVideo() : null }
{ this.props.children }
</div>
<div className={screenShareStreamClasses}>
<MediaView displayAvatar={false}
@ -1006,13 +1069,8 @@ loop.shared.views = (function(_, mozL10n) {
dispatcher={this.props.dispatcher}
showRoomName={this.props.showContextRoomName}
useDesktopPaths={false} />
<div className="local">
<MediaView displayAvatar={this.props.localVideoMuted}
isLoading={this.props.isLocalLoading}
mediaType="local"
posterUrl={this.props.localPosterUrl}
srcVideoObject={this.props.localSrcVideoObject} />
</div>
{ this.state.localMediaAboslutelyPositioned ?
null : this.renderLocalVideo() }
</div>
</div>
);

View File

@ -256,11 +256,12 @@ loop.standaloneRoomViews = (function(mozL10n) {
mixins: [
Backbone.Events,
sharedMixins.MediaSetupMixin,
sharedMixins.RoomsAudioMixin,
loop.store.StoreMixin("activeRoomStore")
sharedMixins.RoomsAudioMixin
],
propTypes: {
// We pass conversationStore here rather than use the mixin, to allow
// easy configurability for the ui-showcase.
activeRoomStore: React.PropTypes.oneOfType([
React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
@ -282,6 +283,16 @@ loop.standaloneRoomViews = (function(mozL10n) {
});
},
componentWillMount: function() {
this.props.activeRoomStore.on("change", function() {
this.setState(this.props.activeRoomStore.getStoreState());
}, this);
},
componentWillUnmount: function() {
this.props.activeRoomStore.off("change", null, this);
},
componentDidMount: function() {
// Adding a class to the document body element from here to ease styling it.
document.body.classList.add("is-standalone-room");
@ -429,7 +440,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
*/
_isScreenShareLoading: function() {
return this.state.receivingScreenShare &&
!this.state.screenShareVideoObject;
!this.state.screenShareVideoObject &&
!this.props.screenSharePosterUrl;
},
render: function() {
@ -456,6 +468,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
localPosterUrl: this.props.localPosterUrl,
localSrcVideoObject: this.state.localSrcVideoObject,
localVideoMuted: this.state.videoMuted,
matchMedia: this.state.matchMedia || window.matchMedia.bind(window),
remotePosterUrl: this.props.remotePosterUrl,
remoteSrcVideoObject: this.state.remoteSrcVideoObject,
renderRemoteVideo: this.shouldRenderRemoteVideo(),

View File

@ -256,11 +256,12 @@ loop.standaloneRoomViews = (function(mozL10n) {
mixins: [
Backbone.Events,
sharedMixins.MediaSetupMixin,
sharedMixins.RoomsAudioMixin,
loop.store.StoreMixin("activeRoomStore")
sharedMixins.RoomsAudioMixin
],
propTypes: {
// We pass conversationStore here rather than use the mixin, to allow
// easy configurability for the ui-showcase.
activeRoomStore: React.PropTypes.oneOfType([
React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
@ -282,6 +283,16 @@ loop.standaloneRoomViews = (function(mozL10n) {
});
},
componentWillMount: function() {
this.props.activeRoomStore.on("change", function() {
this.setState(this.props.activeRoomStore.getStoreState());
}, this);
},
componentWillUnmount: function() {
this.props.activeRoomStore.off("change", null, this);
},
componentDidMount: function() {
// Adding a class to the document body element from here to ease styling it.
document.body.classList.add("is-standalone-room");
@ -429,7 +440,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
*/
_isScreenShareLoading: function() {
return this.state.receivingScreenShare &&
!this.state.screenShareVideoObject;
!this.state.screenShareVideoObject &&
!this.props.screenSharePosterUrl;
},
render: function() {
@ -456,6 +468,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
localPosterUrl={this.props.localPosterUrl}
localSrcVideoObject={this.state.localSrcVideoObject}
localVideoMuted={this.state.videoMuted}
matchMedia={this.state.matchMedia || window.matchMedia.bind(window)}
remotePosterUrl={this.props.remotePosterUrl}
remoteSrcVideoObject={this.state.remoteSrcVideoObject}
renderRemoteVideo={this.shouldRenderRemoteVideo()}

View File

@ -474,7 +474,9 @@ describe("loop.conversationViews", function () {
describe("OngoingConversationView", function() {
function mountTestComponent(extraProps) {
var props = _.extend({
dispatcher: dispatcher
conversationStore: conversationStore,
dispatcher: dispatcher,
matchMedia: window.matchMedia
}, extraProps);
return TestUtils.renderIntoDocument(
React.createElement(loop.conversationViews.OngoingConversationView, props));
@ -489,15 +491,6 @@ describe("loop.conversationViews", function () {
sinon.match.hasOwn("name", "setupStreamElements"));
});
it("should display an avatar for remote video when the stream is not enabled", function() {
view = mountTestComponent({
mediaConnected: true,
remoteVideoEnabled: false
});
TestUtils.findRenderedComponentWithType(view, sharedViews.AvatarView);
});
it("should display the remote video when the stream is enabled", function() {
conversationStore.setStoreState({
remoteSrcVideoObject: { fake: 1 }
@ -511,16 +504,6 @@ describe("loop.conversationViews", function () {
expect(view.getDOMNode().querySelector(".remote video")).not.eql(null);
});
it("should display an avatar for local video when the stream is not enabled", function() {
view = mountTestComponent({
video: {
enabled: false
}
});
TestUtils.findRenderedComponentWithType(view, sharedViews.AvatarView);
});
it("should display the local video when the stream is enabled", function() {
conversationStore.setStoreState({
localSrcVideoObject: { fake: 1 }

View File

@ -95,7 +95,7 @@
describe("Unexpected Warnings Check", function() {
it("should long only the warnings we expect", function() {
chai.expect(caughtWarnings.length).to.eql(27);
chai.expect(caughtWarnings.length).to.eql(28);
});
});

View File

@ -598,21 +598,6 @@ describe("loop.roomViews", function () {
});
describe("Mute", function() {
it("should render local media as audio-only if video is muted",
function() {
activeRoomStore.setStoreState({
roomState: ROOM_STATES.SESSION_CONNECTED,
videoMuted: true
});
view = mountTestComponent();
expect(view.getDOMNode().querySelector(".local-stream-audio"))
.not.eql(null);
});
});
describe("Edit Context", function() {
it("should show the form when the edit button is clicked", function() {
view = mountTestComponent();

View File

@ -99,7 +99,7 @@ class Test1BrowserCall(MarionetteTestCase):
self.switch_to_chatbox()
# expect a video container on desktop side
media_container = self.wait_for_element_displayed(By.CLASS_NAME, "media")
media_container = self.wait_for_element_displayed(By.CLASS_NAME, "media-layout")
self.assertEqual(media_container.tag_name, "div", "expect a video container")
self.check_video(".local-video")

View File

@ -21,6 +21,7 @@
"gMozLoopAPI": true,
"mockDb": true,
"mockPushHandler": true,
"OpenBrowserWindow": true,
"promiseDeletedOAuthParams": false,
"promiseOAuthGetRegistration": false,
"promiseOAuthParamsSetup": false,

View File

@ -167,3 +167,19 @@ add_task(function* test_screen_share() {
MozLoopService.setScreenShareState("1", false);
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
});
add_task(function* test_private_browsing_window() {
let win = OpenBrowserWindow({ private: true });
yield new Promise(resolve => {
win.addEventListener("load", function listener() {
win.removeEventListener("load", listener);
resolve();
});
});
let button = win.LoopUI.toolbarButton.node;
Assert.ok(button, "Loop button should be present");
Assert.ok(button.getAttribute("disabled"), "Disabled attribute should be set");
win.close();
});

View File

@ -916,26 +916,6 @@ describe("loop.store.ActiveRoomStore", function () {
});
});
it("should retry publishing if on desktop, and in the videoMuted state", function() {
store._isDesktop = true;
store.connectionFailure(new sharedActions.ConnectionFailure({
reason: FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA
}));
sinon.assert.calledOnce(fakeSdkDriver.retryPublishWithoutVideo);
});
it("should set videoMuted to try when retrying publishing", function() {
store._isDesktop = true;
store.connectionFailure(new sharedActions.ConnectionFailure({
reason: FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA
}));
expect(store.getStoreState().videoMuted).eql(true);
});
it("should store the failure reason", function() {
store.connectionFailure(connectionFailureAction);

View File

@ -147,26 +147,6 @@ describe("loop.store.ConversationStore", function () {
store.setStoreState({windowId: "42"});
});
it("should retry publishing if on desktop, and in the videoMuted state", function() {
store._isDesktop = true;
store.connectionFailure(new sharedActions.ConnectionFailure({
reason: FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA
}));
sinon.assert.calledOnce(sdkDriver.retryPublishWithoutVideo);
});
it("should set videoMuted to try when retrying publishing", function() {
store._isDesktop = true;
store.connectionFailure(new sharedActions.ConnectionFailure({
reason: FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA
}));
expect(store.getStoreState().videoMuted).eql(true);
});
it("should disconnect the session", function() {
store.connectionFailure(
new sharedActions.ConnectionFailure({reason: "fake"}));

View File

@ -133,43 +133,6 @@ describe("loop.OTSdkDriver", function () {
});
});
describe("#retryPublishWithoutVideo", function() {
beforeEach(function() {
sdk.initPublisher.returns(publisher);
driver.setupStreamElements(new sharedActions.SetupStreamElements({
publisherConfig: publisherConfig
}));
});
it("should make MediaStreamTrack.getSources return without a video source", function(done) {
driver.retryPublishWithoutVideo();
window.MediaStreamTrack.getSources(function(sources) {
expect(sources.some(function(src) {
return src.kind === "video";
})).eql(false);
done();
});
});
it("should call initPublisher", function() {
driver.retryPublishWithoutVideo();
var expectedConfig = _.extend({
channels: {
text: {}
}
}, publisherConfig);
sinon.assert.calledTwice(sdk.initPublisher);
sinon.assert.calledWith(sdk.initPublisher,
sinon.match.instanceOf(HTMLDivElement),
expectedConfig);
});
});
describe("#setMute", function() {
beforeEach(function() {
sdk.initPublisher.returns(publisher);

View File

@ -56,27 +56,6 @@ describe("loop.shared.views.TextChatView", function () {
store.setStoreState({ textChatEnabled: true });
});
it("should add an empty class when the list is empty", function() {
view = mountTestComponent({
messageList: []
});
expect(view.getDOMNode().classList.contains("text-chat-entries-empty")).eql(true);
});
it("should not add an empty class when the list is has items", function() {
view = mountTestComponent({
messageList: [{
type: CHAT_MESSAGE_TYPES.RECEIVED,
contentType: CHAT_CONTENT_TYPES.TEXT,
message: "Hello!",
receivedTimestamp: "2015-06-25T17:53:55.357Z"
}]
});
expect(view.getDOMNode().classList.contains("text-chat-entries-empty")).eql(false);
});
it("should render message entries when message were sent/ received", function() {
view = mountTestComponent({
messageList: [{
@ -297,6 +276,41 @@ describe("loop.shared.views.TextChatView", function () {
fakeServer.restore();
});
it("should add a disabled class when text chat is disabled", function() {
view = mountTestComponent();
store.setStoreState({ textChatEnabled: false });
expect(view.getDOMNode().classList.contains("text-chat-disabled")).eql(true);
});
it("should not a disabled class when text chat is enabled", function() {
view = mountTestComponent();
store.setStoreState({ textChatEnabled: true });
expect(view.getDOMNode().classList.contains("text-chat-disabled")).eql(false);
});
it("should add an empty class when the entries list is empty", function() {
view = mountTestComponent();
expect(view.getDOMNode().classList.contains("text-chat-entries-empty")).eql(true);
});
it("should not add an empty class when the entries list is has items", function() {
view = mountTestComponent();
store.sendTextChatMessage({
contentType: CHAT_CONTENT_TYPES.TEXT,
message: "Hello!",
sentTimestamp: "1970-01-01T00:02:00.000Z",
receivedTimestamp: "1970-01-01T00:02:00.000Z"
});
expect(view.getDOMNode().classList.contains("text-chat-entries-empty")).eql(false);
});
it("should show timestamps from msgs sent more than 1 min apart", function() {
view = mountTestComponent();
@ -326,12 +340,6 @@ describe("loop.shared.views.TextChatView", function () {
.to.eql(2);
});
it("should display the view if no messages and text chat is enabled", function() {
view = mountTestComponent();
expect(view.getDOMNode()).not.eql(null);
});
it("should render message entries when message were sent/ received", function() {
view = mountTestComponent();

View File

@ -1057,6 +1057,7 @@ describe("loop.shared.views", function() {
isRemoteLoading: false,
isScreenShareLoading: false,
localVideoMuted: false,
matchMedia: window.matchMedia,
renderRemoteVideo: false,
showContextRoomName: false,
useDesktopPaths: false
@ -1144,5 +1145,35 @@ describe("loop.shared.views", function() {
expect(view.getDOMNode().querySelector(".media-wrapper")
.classList.contains("showing-local-streams")).eql(true);
});
it("should not mark the wrapper as showing remote streams when not displaying a stream", function() {
view = mountTestComponent({
remoteSrcVideoObject: null,
remotePosterUrl: null
});
expect(view.getDOMNode().querySelector(".media-wrapper")
.classList.contains("showing-remote-streams")).eql(false);
});
it("should mark the wrapper as showing remote streams when displaying a stream", function() {
view = mountTestComponent({
remoteSrcVideoObject: {},
remotePosterUrl: null
});
expect(view.getDOMNode().querySelector(".media-wrapper")
.classList.contains("showing-remote-streams")).eql(true);
});
it("should mark the wrapper as showing remote streams when displaying a poster url", function() {
view = mountTestComponent({
remoteSrcVideoObject: {},
remotePosterUrl: "fake/url"
});
expect(view.getDOMNode().querySelector(".media-wrapper")
.classList.contains("showing-remote-streams")).eql(true);
});
});
});

View File

@ -75,13 +75,25 @@
var dispatcher = new loop.Dispatcher();
var mockSDK = _.extend({
var MockSDK = function() {
dispatcher.register(this, [
"setupStreamElements"
]);
};
MockSDK.prototype = {
setupStreamElements: function() {
// Dummy function to stop warnings.
},
sendTextChatMessage: function(message) {
dispatcher.dispatch(new loop.shared.actions.ReceivedTextChatMessage({
message: message.message
}));
}
}, Backbone.Events);
};
var mockSDK = new MockSDK();
/**
* Every view that uses an activeRoomStore needs its own; if they shared
@ -116,7 +128,6 @@
});
store.forcedUpdate = function forcedUpdate(contentWindow) {
// Since this is called by setTimeout, we don't want to lose any
// exceptions if there's a problem and we need to debug, so...
try {
@ -136,6 +147,17 @@
camera: {height: 480, orientation: 0, width: 640}
},
remoteVideoEnabled: options.remoteVideoEnabled,
// Override the matchMedia, this is so that the correct version is
// used for the frame.
//
// Currently, we use an icky hack, and the showcase conspires with
// react-frame-component to set iframe.contentWindow.matchMedia onto
// the store. Once React context matures a bit (somewhere between
// 0.14 and 1.0, apparently):
//
// https://facebook.github.io/react/blog/2015/02/24/streamlining-react-elements.html#solution-make-context-parent-based-instead-of-owner-based
//
// we should be able to use those to clean this up.
matchMedia: contentWindow.matchMedia.bind(contentWindow),
roomState: options.roomState,
videoMuted: !!options.videoMuted
@ -185,6 +207,10 @@
roomState: ROOM_STATES.HAS_PARTICIPANTS
});
var updatingMobileActiveRoomStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS
});
var localFaceMuteRoomStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS,
videoMuted: true
@ -201,12 +227,19 @@
receivingScreenShare: true
});
var updatingSharingRoomMobileStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS,
receivingScreenShare: true
});
var loadingRemoteLoadingScreenStore = makeActiveRoomStore({
mediaConnected: false,
receivingScreenShare: true,
roomState: ROOM_STATES.HAS_PARTICIPANTS,
remoteSrcVideoObject: false
});
var loadingScreenSharingRoomStore = makeActiveRoomStore({
receivingScreenShare: true,
roomState: ROOM_STATES.HAS_PARTICIPANTS
});
@ -234,7 +267,10 @@
});
var invitationRoomStore = new loop.store.RoomStore(dispatcher, {
mozLoop: navigator.mozLoop
mozLoop: navigator.mozLoop,
activeRoomStore: makeActiveRoomStore({
roomState: ROOM_STATES.INIT
})
});
var roomStore = new loop.store.RoomStore(dispatcher, {
@ -253,6 +289,20 @@
})
});
var desktopRoomStoreMedium = new loop.store.RoomStore(dispatcher, {
mozLoop: navigator.mozLoop,
activeRoomStore: makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS
})
});
var desktopRoomStoreLarge = new loop.store.RoomStore(dispatcher, {
mozLoop: navigator.mozLoop,
activeRoomStore: makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS
})
});
var desktopLocalFaceMuteActiveRoomStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS,
videoMuted: true
@ -272,15 +322,59 @@
activeRoomStore: desktopRemoteFaceMuteActiveRoomStore
});
var conversationStore = new loop.store.ConversationStore(dispatcher, {
client: {},
mozLoop: navigator.mozLoop,
sdkDriver: mockSDK
});
var textChatStore = new loop.store.TextChatStore(dispatcher, {
sdkDriver: mockSDK
});
/**
* Every view that uses an conversationStore needs its own; if they shared
* a conversation store, they'd interfere with each other.
*
* @param options
* @returns {loop.store.ConversationStore}
*/
function makeConversationStore() {
var roomDispatcher = new loop.Dispatcher();
var store = new loop.store.ConversationStore(dispatcher, {
client: {},
mozLoop: navigator.mozLoop,
sdkDriver: mockSDK
});
store.forcedUpdate = function forcedUpdate(contentWindow) {
// Since this is called by setTimeout, we don't want to lose any
// exceptions if there's a problem and we need to debug, so...
try {
var newStoreState = {
// Override the matchMedia, this is so that the correct version is
// used for the frame.
//
// Currently, we use an icky hack, and the showcase conspires with
// react-frame-component to set iframe.contentWindow.matchMedia onto
// the store. Once React context matures a bit (somewhere between
// 0.14 and 1.0, apparently):
//
// https://facebook.github.io/react/blog/2015/02/24/streamlining-react-elements.html#solution-make-context-parent-based-instead-of-owner-based
//
// we should be able to use those to clean this up.
matchMedia: contentWindow.matchMedia.bind(contentWindow)
};
store.setStoreState(newStoreState);
} catch (ex) {
console.error("exception in forcedUpdate:", ex);
}
};
return store;
}
var conversationStores = [];
for (var index = 0; index < 5; index++) {
conversationStores[index] = makeConversationStore();
}
// Update the text chat store with the room info.
textChatStore.updateRoomInfo(new sharedActions.UpdateRoomInfo({
roomName: "A Very Long Conversation Name",
@ -341,7 +435,7 @@
loop.store.StoreMixin.register({
activeRoomStore: activeRoomStore,
conversationStore: conversationStore,
conversationStore: conversationStores[0],
textChatStore: textChatStore
});
@ -360,14 +454,6 @@
requestCallUrlInfo: noop
};
var mockConversationModel = new loop.shared.models.ConversationModel({
callerId: "Mrs Jones",
urlCreationDate: (new Date() / 1000).toString()
}, {
sdk: mockSDK
});
mockConversationModel.startSession = noop;
var mockWebSocket = new loop.CallConnectionWebSocket({
url: "fake",
callId: "fakeId",
@ -763,7 +849,7 @@
React.createElement("div", {className: "fx-embedded"},
React.createElement(CallFailedView, {dispatcher: dispatcher,
outgoing: false,
store: conversationStore})
store: conversationStores[0]})
)
),
React.createElement(Example, {dashed: true,
@ -772,7 +858,7 @@
React.createElement("div", {className: "fx-embedded"},
React.createElement(CallFailedView, {dispatcher: dispatcher,
outgoing: true,
store: conversationStore})
store: conversationStores[1]})
)
),
React.createElement(Example, {dashed: true,
@ -781,18 +867,22 @@
React.createElement("div", {className: "fx-embedded"},
React.createElement(CallFailedView, {dispatcher: dispatcher, emailLinkError: true,
outgoing: true,
store: conversationStore})
store: conversationStores[0]})
)
)
),
React.createElement(Section, {name: "OngoingConversationView"},
React.createElement(FramedExample, {height: 254,
summary: "Desktop ongoing conversation window",
width: 298},
React.createElement(FramedExample, {
dashed: true,
height: 394,
onContentsRendered: conversationStores[0].forcedUpdate,
summary: "Desktop ongoing conversation window",
width: 298},
React.createElement("div", {className: "fx-embedded"},
React.createElement(OngoingConversationView, {
audio: {enabled: true},
conversationStore: conversationStores[0],
dispatcher: dispatcher,
localPosterUrl: "sample-img/video-screen-local.png",
mediaConnected: true,
@ -802,28 +892,55 @@
)
),
React.createElement(FramedExample, {height: 600,
summary: "Desktop ongoing conversation window large",
width: 800},
React.createElement("div", {className: "fx-embedded"},
React.createElement(OngoingConversationView, {
audio: {enabled: true},
dispatcher: dispatcher,
localPosterUrl: "sample-img/video-screen-local.png",
mediaConnected: true,
remotePosterUrl: "sample-img/video-screen-remote.png",
remoteVideoEnabled: true,
video: {enabled: true}})
)
React.createElement(FramedExample, {
dashed: true,
height: 400,
onContentsRendered: conversationStores[1].forcedUpdate,
summary: "Desktop ongoing conversation window (medium)",
width: 600},
React.createElement("div", {className: "fx-embedded"},
React.createElement(OngoingConversationView, {
audio: {enabled: true},
conversationStore: conversationStores[1],
dispatcher: dispatcher,
localPosterUrl: "sample-img/video-screen-local.png",
mediaConnected: true,
remotePosterUrl: "sample-img/video-screen-remote.png",
remoteVideoEnabled: true,
video: {enabled: true}})
)
),
React.createElement(FramedExample, {height: 254,
React.createElement(FramedExample, {
height: 600,
onContentsRendered: conversationStores[2].forcedUpdate,
summary: "Desktop ongoing conversation window (large)",
width: 800},
React.createElement("div", {className: "fx-embedded"},
React.createElement(OngoingConversationView, {
audio: {enabled: true},
conversationStore: conversationStores[2],
dispatcher: dispatcher,
localPosterUrl: "sample-img/video-screen-local.png",
mediaConnected: true,
remotePosterUrl: "sample-img/video-screen-remote.png",
remoteVideoEnabled: true,
video: {enabled: true}})
)
),
React.createElement(FramedExample, {
dashed: true,
height: 394,
onContentsRendered: conversationStores[3].forcedUpdate,
summary: "Desktop ongoing conversation window - local face mute",
width: 298},
React.createElement("div", {className: "fx-embedded"},
React.createElement(OngoingConversationView, {
audio: {enabled: true},
conversationStore: conversationStores[3],
dispatcher: dispatcher,
localPosterUrl: "sample-img/video-screen-local.png",
mediaConnected: true,
remotePosterUrl: "sample-img/video-screen-remote.png",
remoteVideoEnabled: true,
@ -831,15 +948,19 @@
)
),
React.createElement(FramedExample, {height: 254,
React.createElement(FramedExample, {
dashed: true, height: 394,
onContentsRendered: conversationStores[4].forcedUpdate,
summary: "Desktop ongoing conversation window - remote face mute",
width: 298},
React.createElement("div", {className: "fx-embedded"},
React.createElement(OngoingConversationView, {
audio: {enabled: true},
conversationStore: conversationStores[4],
dispatcher: dispatcher,
localPosterUrl: "sample-img/video-screen-local.png",
mediaConnected: true,
remotePosterUrl: "sample-img/video-screen-remote.png",
remoteVideoEnabled: false,
video: {enabled: true}})
)
@ -894,7 +1015,8 @@
React.createElement(Section, {name: "DesktopRoomConversationView"},
React.createElement(FramedExample, {
height: 254,
height: 398,
onContentsRendered: invitationRoomStore.activeRoomStore.forcedUpdate,
summary: "Desktop room conversation (invitation, text-chat inclusion/scrollbars don't happen in real client)",
width: 298},
React.createElement("div", {className: "fx-embedded"},
@ -911,6 +1033,7 @@
React.createElement(FramedExample, {
dashed: true,
height: 394,
onContentsRendered: desktopRoomStoreLoading.activeRoomStore.forcedUpdate,
summary: "Desktop room conversation (loading)",
width: 298},
/* Hide scrollbars here. Rotating loading div overflows and causes
@ -927,8 +1050,12 @@
)
),
React.createElement(FramedExample, {height: 254,
summary: "Desktop room conversation"},
React.createElement(FramedExample, {
dashed: true,
height: 394,
onContentsRendered: roomStore.activeRoomStore.forcedUpdate,
summary: "Desktop room conversation",
width: 298},
React.createElement("div", {className: "fx-embedded"},
React.createElement(DesktopRoomConversationView, {
dispatcher: dispatcher,
@ -941,10 +1068,48 @@
)
),
React.createElement(FramedExample, {dashed: true,
height: 394,
summary: "Desktop room conversation local face-mute",
width: 298},
React.createElement(FramedExample, {
dashed: true,
height: 482,
onContentsRendered: desktopRoomStoreMedium.activeRoomStore.forcedUpdate,
summary: "Desktop room conversation (medium)",
width: 602},
React.createElement("div", {className: "fx-embedded"},
React.createElement(DesktopRoomConversationView, {
dispatcher: dispatcher,
localPosterUrl: "sample-img/video-screen-local.png",
mozLoop: navigator.mozLoop,
onCallTerminated: function(){},
remotePosterUrl: "sample-img/video-screen-remote.png",
roomState: ROOM_STATES.HAS_PARTICIPANTS,
roomStore: desktopRoomStoreMedium})
)
),
React.createElement(FramedExample, {
dashed: true,
height: 485,
onContentsRendered: desktopRoomStoreLarge.activeRoomStore.forcedUpdate,
summary: "Desktop room conversation (large)",
width: 646},
React.createElement("div", {className: "fx-embedded"},
React.createElement(DesktopRoomConversationView, {
dispatcher: dispatcher,
localPosterUrl: "sample-img/video-screen-local.png",
mozLoop: navigator.mozLoop,
onCallTerminated: function(){},
remotePosterUrl: "sample-img/video-screen-remote.png",
roomState: ROOM_STATES.HAS_PARTICIPANTS,
roomStore: desktopRoomStoreLarge})
)
),
React.createElement(FramedExample, {
dashed: true,
height: 394,
onContentsRendered: desktopLocalFaceMuteRoomStore.activeRoomStore.forcedUpdate,
summary: "Desktop room conversation local face-mute",
width: 298},
React.createElement("div", {className: "fx-embedded"},
React.createElement(DesktopRoomConversationView, {
dispatcher: dispatcher,
@ -955,7 +1120,9 @@
)
),
React.createElement(FramedExample, {dashed: true, height: 394,
React.createElement(FramedExample, {dashed: true,
height: 394,
onContentsRendered: desktopRemoteFaceMuteRoomStore.activeRoomStore.forcedUpdate,
summary: "Desktop room conversation remote face-mute",
width: 298},
React.createElement("div", {className: "fx-embedded"},
@ -964,6 +1131,7 @@
localPosterUrl: "sample-img/video-screen-local.png",
mozLoop: navigator.mozLoop,
onCallTerminated: function(){},
remotePosterUrl: "sample-img/video-screen-remote.png",
roomStore: desktopRemoteFaceMuteRoomStore})
)
)
@ -1081,8 +1249,7 @@
isFirefox: true,
localPosterUrl: "sample-img/video-screen-local.png",
remotePosterUrl: "sample-img/video-screen-remote.png",
roomState: ROOM_STATES.HAS_PARTICIPANTS,
screenSharePosterUrl: "sample-img/video-screen-baz.png"})
roomState: ROOM_STATES.HAS_PARTICIPANTS})
)
),
@ -1102,8 +1269,7 @@
isFirefox: true,
localPosterUrl: "sample-img/video-screen-local.png",
remotePosterUrl: "sample-img/video-screen-remote.png",
roomState: ROOM_STATES.HAS_PARTICIPANTS,
screenSharePosterUrl: "sample-img/video-screen-baz.png"})
roomState: ROOM_STATES.HAS_PARTICIPANTS})
)
),
@ -1171,12 +1337,12 @@
cssClass: "standalone",
dashed: true,
height: 480,
onContentsRendered: updatingActiveRoomStore.forcedUpdate,
onContentsRendered: updatingMobileActiveRoomStore.forcedUpdate,
summary: "Standalone room conversation (has-participants, 600x480)",
width: 600},
React.createElement("div", {className: "standalone"},
React.createElement(StandaloneRoomView, {
activeRoomStore: updatingActiveRoomStore,
activeRoomStore: updatingMobileActiveRoomStore,
dispatcher: dispatcher,
isFirefox: true,
localPosterUrl: "sample-img/video-screen-local.png",
@ -1189,12 +1355,12 @@
cssClass: "standalone",
dashed: true,
height: 480,
onContentsRendered: updatingSharingRoomStore.forcedUpdate,
onContentsRendered: updatingSharingRoomMobileStore.forcedUpdate,
summary: "Standalone room convo (has-participants, receivingScreenShare, 600x480)",
width: 600},
React.createElement("div", {className: "standalone", cssClass: "standalone"},
React.createElement(StandaloneRoomView, {
activeRoomStore: updatingSharingRoomStore,
activeRoomStore: updatingSharingRoomMobileStore,
dispatcher: dispatcher,
isFirefox: true,
localPosterUrl: "sample-img/video-screen-local.png",
@ -1282,7 +1448,7 @@
// This simulates the mocha layout for errors which means we can run
// this alongside our other unit tests but use the same harness.
var expectedWarningsCount = 23;
var expectedWarningsCount = 18;
var warningsMismatch = caughtWarnings.length !== expectedWarningsCount;
if (uncaughtError || warningsMismatch) {
$("#results").append("<div class='failures'><em>" +

View File

@ -75,13 +75,25 @@
var dispatcher = new loop.Dispatcher();
var mockSDK = _.extend({
var MockSDK = function() {
dispatcher.register(this, [
"setupStreamElements"
]);
};
MockSDK.prototype = {
setupStreamElements: function() {
// Dummy function to stop warnings.
},
sendTextChatMessage: function(message) {
dispatcher.dispatch(new loop.shared.actions.ReceivedTextChatMessage({
message: message.message
}));
}
}, Backbone.Events);
};
var mockSDK = new MockSDK();
/**
* Every view that uses an activeRoomStore needs its own; if they shared
@ -116,7 +128,6 @@
});
store.forcedUpdate = function forcedUpdate(contentWindow) {
// Since this is called by setTimeout, we don't want to lose any
// exceptions if there's a problem and we need to debug, so...
try {
@ -136,6 +147,17 @@
camera: {height: 480, orientation: 0, width: 640}
},
remoteVideoEnabled: options.remoteVideoEnabled,
// Override the matchMedia, this is so that the correct version is
// used for the frame.
//
// Currently, we use an icky hack, and the showcase conspires with
// react-frame-component to set iframe.contentWindow.matchMedia onto
// the store. Once React context matures a bit (somewhere between
// 0.14 and 1.0, apparently):
//
// https://facebook.github.io/react/blog/2015/02/24/streamlining-react-elements.html#solution-make-context-parent-based-instead-of-owner-based
//
// we should be able to use those to clean this up.
matchMedia: contentWindow.matchMedia.bind(contentWindow),
roomState: options.roomState,
videoMuted: !!options.videoMuted
@ -185,6 +207,10 @@
roomState: ROOM_STATES.HAS_PARTICIPANTS
});
var updatingMobileActiveRoomStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS
});
var localFaceMuteRoomStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS,
videoMuted: true
@ -201,12 +227,19 @@
receivingScreenShare: true
});
var updatingSharingRoomMobileStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS,
receivingScreenShare: true
});
var loadingRemoteLoadingScreenStore = makeActiveRoomStore({
mediaConnected: false,
receivingScreenShare: true,
roomState: ROOM_STATES.HAS_PARTICIPANTS,
remoteSrcVideoObject: false
});
var loadingScreenSharingRoomStore = makeActiveRoomStore({
receivingScreenShare: true,
roomState: ROOM_STATES.HAS_PARTICIPANTS
});
@ -234,7 +267,10 @@
});
var invitationRoomStore = new loop.store.RoomStore(dispatcher, {
mozLoop: navigator.mozLoop
mozLoop: navigator.mozLoop,
activeRoomStore: makeActiveRoomStore({
roomState: ROOM_STATES.INIT
})
});
var roomStore = new loop.store.RoomStore(dispatcher, {
@ -253,6 +289,20 @@
})
});
var desktopRoomStoreMedium = new loop.store.RoomStore(dispatcher, {
mozLoop: navigator.mozLoop,
activeRoomStore: makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS
})
});
var desktopRoomStoreLarge = new loop.store.RoomStore(dispatcher, {
mozLoop: navigator.mozLoop,
activeRoomStore: makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS
})
});
var desktopLocalFaceMuteActiveRoomStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS,
videoMuted: true
@ -272,15 +322,59 @@
activeRoomStore: desktopRemoteFaceMuteActiveRoomStore
});
var conversationStore = new loop.store.ConversationStore(dispatcher, {
client: {},
mozLoop: navigator.mozLoop,
sdkDriver: mockSDK
});
var textChatStore = new loop.store.TextChatStore(dispatcher, {
sdkDriver: mockSDK
});
/**
* Every view that uses an conversationStore needs its own; if they shared
* a conversation store, they'd interfere with each other.
*
* @param options
* @returns {loop.store.ConversationStore}
*/
function makeConversationStore() {
var roomDispatcher = new loop.Dispatcher();
var store = new loop.store.ConversationStore(dispatcher, {
client: {},
mozLoop: navigator.mozLoop,
sdkDriver: mockSDK
});
store.forcedUpdate = function forcedUpdate(contentWindow) {
// Since this is called by setTimeout, we don't want to lose any
// exceptions if there's a problem and we need to debug, so...
try {
var newStoreState = {
// Override the matchMedia, this is so that the correct version is
// used for the frame.
//
// Currently, we use an icky hack, and the showcase conspires with
// react-frame-component to set iframe.contentWindow.matchMedia onto
// the store. Once React context matures a bit (somewhere between
// 0.14 and 1.0, apparently):
//
// https://facebook.github.io/react/blog/2015/02/24/streamlining-react-elements.html#solution-make-context-parent-based-instead-of-owner-based
//
// we should be able to use those to clean this up.
matchMedia: contentWindow.matchMedia.bind(contentWindow)
};
store.setStoreState(newStoreState);
} catch (ex) {
console.error("exception in forcedUpdate:", ex);
}
};
return store;
}
var conversationStores = [];
for (var index = 0; index < 5; index++) {
conversationStores[index] = makeConversationStore();
}
// Update the text chat store with the room info.
textChatStore.updateRoomInfo(new sharedActions.UpdateRoomInfo({
roomName: "A Very Long Conversation Name",
@ -341,7 +435,7 @@
loop.store.StoreMixin.register({
activeRoomStore: activeRoomStore,
conversationStore: conversationStore,
conversationStore: conversationStores[0],
textChatStore: textChatStore
});
@ -360,14 +454,6 @@
requestCallUrlInfo: noop
};
var mockConversationModel = new loop.shared.models.ConversationModel({
callerId: "Mrs Jones",
urlCreationDate: (new Date() / 1000).toString()
}, {
sdk: mockSDK
});
mockConversationModel.startSession = noop;
var mockWebSocket = new loop.CallConnectionWebSocket({
url: "fake",
callId: "fakeId",
@ -763,7 +849,7 @@
<div className="fx-embedded">
<CallFailedView dispatcher={dispatcher}
outgoing={false}
store={conversationStore} />
store={conversationStores[0]} />
</div>
</Example>
<Example dashed={true}
@ -772,7 +858,7 @@
<div className="fx-embedded">
<CallFailedView dispatcher={dispatcher}
outgoing={true}
store={conversationStore} />
store={conversationStores[1]} />
</div>
</Example>
<Example dashed={true}
@ -781,18 +867,22 @@
<div className="fx-embedded">
<CallFailedView dispatcher={dispatcher} emailLinkError={true}
outgoing={true}
store={conversationStore} />
store={conversationStores[0]} />
</div>
</Example>
</Section>
<Section name="OngoingConversationView">
<FramedExample height={254}
summary="Desktop ongoing conversation window"
width={298}>
<FramedExample
dashed={true}
height={394}
onContentsRendered={conversationStores[0].forcedUpdate}
summary="Desktop ongoing conversation window"
width={298}>
<div className="fx-embedded">
<OngoingConversationView
audio={{enabled: true}}
conversationStore={conversationStores[0]}
dispatcher={dispatcher}
localPosterUrl="sample-img/video-screen-local.png"
mediaConnected={true}
@ -802,28 +892,55 @@
</div>
</FramedExample>
<FramedExample height={600}
summary="Desktop ongoing conversation window large"
width={800}>
<div className="fx-embedded">
<OngoingConversationView
audio={{enabled: true}}
dispatcher={dispatcher}
localPosterUrl="sample-img/video-screen-local.png"
mediaConnected={true}
remotePosterUrl="sample-img/video-screen-remote.png"
remoteVideoEnabled={true}
video={{enabled: true}} />
</div>
<FramedExample
dashed={true}
height={400}
onContentsRendered={conversationStores[1].forcedUpdate}
summary="Desktop ongoing conversation window (medium)"
width={600}>
<div className="fx-embedded">
<OngoingConversationView
audio={{enabled: true}}
conversationStore={conversationStores[1]}
dispatcher={dispatcher}
localPosterUrl="sample-img/video-screen-local.png"
mediaConnected={true}
remotePosterUrl="sample-img/video-screen-remote.png"
remoteVideoEnabled={true}
video={{enabled: true}} />
</div>
</FramedExample>
<FramedExample height={254}
<FramedExample
height={600}
onContentsRendered={conversationStores[2].forcedUpdate}
summary="Desktop ongoing conversation window (large)"
width={800}>
<div className="fx-embedded">
<OngoingConversationView
audio={{enabled: true}}
conversationStore={conversationStores[2]}
dispatcher={dispatcher}
localPosterUrl="sample-img/video-screen-local.png"
mediaConnected={true}
remotePosterUrl="sample-img/video-screen-remote.png"
remoteVideoEnabled={true}
video={{enabled: true}} />
</div>
</FramedExample>
<FramedExample
dashed={true}
height={394}
onContentsRendered={conversationStores[3].forcedUpdate}
summary="Desktop ongoing conversation window - local face mute"
width={298} >
<div className="fx-embedded">
<OngoingConversationView
audio={{enabled: true}}
conversationStore={conversationStores[3]}
dispatcher={dispatcher}
localPosterUrl="sample-img/video-screen-local.png"
mediaConnected={true}
remotePosterUrl="sample-img/video-screen-remote.png"
remoteVideoEnabled={true}
@ -831,15 +948,19 @@
</div>
</FramedExample>
<FramedExample height={254}
<FramedExample
dashed={true} height={394}
onContentsRendered={conversationStores[4].forcedUpdate}
summary="Desktop ongoing conversation window - remote face mute"
width={298} >
<div className="fx-embedded">
<OngoingConversationView
audio={{enabled: true}}
conversationStore={conversationStores[4]}
dispatcher={dispatcher}
localPosterUrl="sample-img/video-screen-local.png"
mediaConnected={true}
remotePosterUrl="sample-img/video-screen-remote.png"
remoteVideoEnabled={false}
video={{enabled: true}} />
</div>
@ -894,7 +1015,8 @@
<Section name="DesktopRoomConversationView">
<FramedExample
height={254}
height={398}
onContentsRendered={invitationRoomStore.activeRoomStore.forcedUpdate}
summary="Desktop room conversation (invitation, text-chat inclusion/scrollbars don't happen in real client)"
width={298}>
<div className="fx-embedded">
@ -911,6 +1033,7 @@
<FramedExample
dashed={true}
height={394}
onContentsRendered={desktopRoomStoreLoading.activeRoomStore.forcedUpdate}
summary="Desktop room conversation (loading)"
width={298}>
{/* Hide scrollbars here. Rotating loading div overflows and causes
@ -927,8 +1050,12 @@
</div>
</FramedExample>
<FramedExample height={254}
summary="Desktop room conversation">
<FramedExample
dashed={true}
height={394}
onContentsRendered={roomStore.activeRoomStore.forcedUpdate}
summary="Desktop room conversation"
width={298}>
<div className="fx-embedded">
<DesktopRoomConversationView
dispatcher={dispatcher}
@ -941,10 +1068,48 @@
</div>
</FramedExample>
<FramedExample dashed={true}
height={394}
summary="Desktop room conversation local face-mute"
width={298}>
<FramedExample
dashed={true}
height={482}
onContentsRendered={desktopRoomStoreMedium.activeRoomStore.forcedUpdate}
summary="Desktop room conversation (medium)"
width={602}>
<div className="fx-embedded">
<DesktopRoomConversationView
dispatcher={dispatcher}
localPosterUrl="sample-img/video-screen-local.png"
mozLoop={navigator.mozLoop}
onCallTerminated={function(){}}
remotePosterUrl="sample-img/video-screen-remote.png"
roomState={ROOM_STATES.HAS_PARTICIPANTS}
roomStore={desktopRoomStoreMedium} />
</div>
</FramedExample>
<FramedExample
dashed={true}
height={485}
onContentsRendered={desktopRoomStoreLarge.activeRoomStore.forcedUpdate}
summary="Desktop room conversation (large)"
width={646}>
<div className="fx-embedded">
<DesktopRoomConversationView
dispatcher={dispatcher}
localPosterUrl="sample-img/video-screen-local.png"
mozLoop={navigator.mozLoop}
onCallTerminated={function(){}}
remotePosterUrl="sample-img/video-screen-remote.png"
roomState={ROOM_STATES.HAS_PARTICIPANTS}
roomStore={desktopRoomStoreLarge} />
</div>
</FramedExample>
<FramedExample
dashed={true}
height={394}
onContentsRendered={desktopLocalFaceMuteRoomStore.activeRoomStore.forcedUpdate}
summary="Desktop room conversation local face-mute"
width={298}>
<div className="fx-embedded">
<DesktopRoomConversationView
dispatcher={dispatcher}
@ -955,7 +1120,9 @@
</div>
</FramedExample>
<FramedExample dashed={true} height={394}
<FramedExample dashed={true}
height={394}
onContentsRendered={desktopRemoteFaceMuteRoomStore.activeRoomStore.forcedUpdate}
summary="Desktop room conversation remote face-mute"
width={298} >
<div className="fx-embedded">
@ -964,6 +1131,7 @@
localPosterUrl="sample-img/video-screen-local.png"
mozLoop={navigator.mozLoop}
onCallTerminated={function(){}}
remotePosterUrl="sample-img/video-screen-remote.png"
roomStore={desktopRemoteFaceMuteRoomStore} />
</div>
</FramedExample>
@ -1081,8 +1249,7 @@
isFirefox={true}
localPosterUrl="sample-img/video-screen-local.png"
remotePosterUrl="sample-img/video-screen-remote.png"
roomState={ROOM_STATES.HAS_PARTICIPANTS}
screenSharePosterUrl="sample-img/video-screen-baz.png" />
roomState={ROOM_STATES.HAS_PARTICIPANTS} />
</div>
</FramedExample>
@ -1102,8 +1269,7 @@
isFirefox={true}
localPosterUrl="sample-img/video-screen-local.png"
remotePosterUrl="sample-img/video-screen-remote.png"
roomState={ROOM_STATES.HAS_PARTICIPANTS}
screenSharePosterUrl="sample-img/video-screen-baz.png" />
roomState={ROOM_STATES.HAS_PARTICIPANTS} />
</div>
</FramedExample>
@ -1171,12 +1337,12 @@
cssClass="standalone"
dashed={true}
height={480}
onContentsRendered={updatingActiveRoomStore.forcedUpdate}
onContentsRendered={updatingMobileActiveRoomStore.forcedUpdate}
summary="Standalone room conversation (has-participants, 600x480)"
width={600}>
<div className="standalone">
<StandaloneRoomView
activeRoomStore={updatingActiveRoomStore}
activeRoomStore={updatingMobileActiveRoomStore}
dispatcher={dispatcher}
isFirefox={true}
localPosterUrl="sample-img/video-screen-local.png"
@ -1189,12 +1355,12 @@
cssClass="standalone"
dashed={true}
height={480}
onContentsRendered={updatingSharingRoomStore.forcedUpdate}
onContentsRendered={updatingSharingRoomMobileStore.forcedUpdate}
summary="Standalone room convo (has-participants, receivingScreenShare, 600x480)"
width={600} >
<div className="standalone" cssClass="standalone">
<StandaloneRoomView
activeRoomStore={updatingSharingRoomStore}
activeRoomStore={updatingSharingRoomMobileStore}
dispatcher={dispatcher}
isFirefox={true}
localPosterUrl="sample-img/video-screen-local.png"
@ -1282,7 +1448,7 @@
// This simulates the mocha layout for errors which means we can run
// this alongside our other unit tests but use the same harness.
var expectedWarningsCount = 23;
var expectedWarningsCount = 18;
var warningsMismatch = caughtWarnings.length !== expectedWarningsCount;
if (uncaughtError || warningsMismatch) {
$("#results").append("<div class='failures'><em>" +

View File

@ -4,7 +4,8 @@
* 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/. */
/* globals overlays, StyleInspectorMenu */
/* globals overlays, StyleInspectorMenu, loader, clipboardHelper,
_Iterator, StopIteration */
"use strict";
@ -49,8 +50,7 @@ const HTML_NS = "http://www.w3.org/1999/xhtml";
*
* @constructor
*/
function UpdateProcess(aWin, aGenerator, aOptions)
{
function UpdateProcess(aWin, aGenerator, aOptions) {
this.win = aWin;
this.iter = _Iterator(aGenerator);
this.onItem = aOptions.onItem || function() {};
@ -66,8 +66,7 @@ UpdateProcess.prototype = {
/**
* Schedule a new batch on the main loop.
*/
schedule: function UP_schedule()
{
schedule: function() {
if (this.canceled) {
return;
}
@ -78,8 +77,7 @@ UpdateProcess.prototype = {
* Cancel the running process. onItem will not be called again,
* and onCancel will be called.
*/
cancel: function UP_cancel()
{
cancel: function() {
if (this._timeout) {
this.win.clearTimeout(this._timeout);
this._timeout = 0;
@ -88,7 +86,7 @@ UpdateProcess.prototype = {
this.onCancel();
},
_timeoutHandler: function UP_timeoutHandler() {
_timeoutHandler: function() {
this._timeout = null;
try {
this._runBatch();
@ -104,10 +102,9 @@ UpdateProcess.prototype = {
}
},
_runBatch: function Y_runBatch()
{
_runBatch: function() {
let time = Date.now();
while(!this.canceled) {
while (!this.canceled) {
// Continue until iter.next() throws...
let next = this.iter.next();
this.onItem(next[1]);
@ -120,9 +117,9 @@ UpdateProcess.prototype = {
};
/**
* CssComputedView is a panel that manages the display of a table sorted by style.
* There should be one instance of CssComputedView per style display (of which there
* will generally only be one).
* CssComputedView is a panel that manages the display of a table
* sorted by style. There should be one instance of CssComputedView
* per style display (of which there will generally only be one).
*
* @param {Inspector} inspector toolbox panel
* @param {Document} document The document that will contain the computed view.
@ -142,8 +139,8 @@ function CssComputedView(inspector, document, pageStyle) {
this._outputParser = new OutputParser();
let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
getService(Ci.nsIXULChromeRegistry);
let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
.getService(Ci.nsIXULChromeRegistry);
this.getRTLAttr = chromeReg.isLocaleRTL("global") ? "rtl" : "ltr";
// Create bound methods.
@ -211,8 +208,7 @@ function CssComputedView(inspector, document, pageStyle) {
* @param {string} aName The key to lookup.
* @returns A localized version of the given key.
*/
CssComputedView.l10n = function CssComputedView_l10n(aName)
{
CssComputedView.l10n = function(aName) {
try {
return CssComputedView._strings.GetStringFromName(aName);
} catch (ex) {
@ -251,8 +247,7 @@ CssComputedView.prototype = {
this.pageStyle = pageStyle;
},
get includeBrowserStyles()
{
get includeBrowserStyles() {
return this.includeBrowserStylesCheckbox.checked;
},
@ -264,8 +259,9 @@ CssComputedView.prototype = {
},
/**
* Update the view with a new selected element.
* The CssComputedView panel will show the style information for the given element.
* Update the view with a new selected element. The CssComputedView panel
* will show the style information for the given element.
*
* @param {NodeFront} aElement The highlighted node to get styles for.
* @returns a promise that will be resolved when highlighting is complete.
*/
@ -383,8 +379,7 @@ CssComputedView.prototype = {
return {type, value};
},
_createPropertyViews: function()
{
_createPropertyViews: function() {
if (this._createViewsPromise) {
return this._createViewsPromise;
}
@ -396,7 +391,8 @@ CssComputedView.prototype = {
this.numVisibleProperties = 0;
let fragment = this.styleDocument.createDocumentFragment();
this._createViewsProcess = new UpdateProcess(this.styleWindow, CssComputedView.propertyNames, {
this._createViewsProcess = new UpdateProcess(
this.styleWindow, CssComputedView.propertyNames, {
onItem: (aPropertyName) => {
// Per-item callback.
let propView = new PropertyView(this, aPropertyName);
@ -426,8 +422,7 @@ CssComputedView.prototype = {
/**
* Refresh the panel content.
*/
refreshPanel: function CssComputedView_refreshPanel()
{
refreshPanel: function() {
if (!this.viewedElement) {
return promise.resolve();
}
@ -443,12 +438,12 @@ CssComputedView.prototype = {
onlyMatched: !this.includeBrowserStyles,
markMatched: true
})
]).then(([createViews, computed]) => {
]).then(([, computed]) => {
if (viewedElement !== this.viewedElement) {
return;
return promise.resolve();
}
this._matchedProperties = new Set;
this._matchedProperties = new Set();
for (let name in computed) {
if (computed[name].matched) {
this._matchedProperties.add(name);
@ -469,7 +464,8 @@ CssComputedView.prototype = {
this._darkStripe = true;
let deferred = promise.defer();
this._refreshProcess = new UpdateProcess(this.styleWindow, this.propertyViews, {
this._refreshProcess = new UpdateProcess(
this.styleWindow, this.propertyViews, {
onItem: (aPropView) => {
aPropView.refresh();
},
@ -511,8 +507,7 @@ CssComputedView.prototype = {
*
* @param {Event} aEvent the DOM Event object.
*/
_onFilterStyles: function(aEvent)
{
_onFilterStyles: function(aEvent) {
let win = this.styleWindow;
if (this._filterChangedTimeout) {
@ -580,8 +575,7 @@ CssComputedView.prototype = {
*
* @param {Event} aEvent the DOM Event object.
*/
_onIncludeBrowserStyles: function(aEvent)
{
_onIncludeBrowserStyles: function(aEvent) {
this.refreshSourceFilter();
this.refreshPanel();
},
@ -592,16 +586,14 @@ CssComputedView.prototype = {
* document or one of thedocument's stylesheets. If .checked is false we
* display all properties including those that come from UA stylesheets.
*/
refreshSourceFilter: function CssComputedView_setSourceFilter()
{
refreshSourceFilter: function() {
this._matchedProperties = null;
this._sourceFilter = this.includeBrowserStyles ?
CssLogic.FILTER.UA :
CssLogic.FILTER.USER;
},
_onSourcePrefChanged: function CssComputedView__onSourcePrefChanged()
{
_onSourcePrefChanged: function() {
for (let propView of this.propertyViews) {
propView.updateSourceLinks();
}
@ -611,8 +603,7 @@ CssComputedView.prototype = {
/**
* The CSS as displayed by the UI.
*/
createStyleViews: function CssComputedView_createStyleViews()
{
createStyleViews: function() {
if (CssComputedView.propertyNames) {
return;
}
@ -641,8 +632,8 @@ CssComputedView.prototype = {
this._createPropertyViews().then(null, e => {
if (!this._isDestroyed) {
console.warn("The creation of property views was cancelled because the " +
"computed-view was destroyed before it was done creating views");
console.warn("The creation of property views was cancelled because " +
"the computed-view was destroyed before it was done creating views");
} else {
console.error(e);
}
@ -654,18 +645,16 @@ CssComputedView.prototype = {
*
* @return {Set} If a property name is in the set, it has matching selectors.
*/
get matchedProperties()
{
return this._matchedProperties || new Set;
get matchedProperties() {
return this._matchedProperties || new Set();
},
/**
* Focus the window on mousedown.
*
* @param aEvent The event object
* @param event The event object
*/
focusWindow: function(aEvent)
{
focusWindow: function(event) {
let win = this.styleDocument.defaultView;
win.focus();
},
@ -707,8 +696,8 @@ CssComputedView.prototype = {
let win = this.styleDocument.defaultView;
let text = win.getSelection().toString().trim();
// Tidy up block headings by moving CSS property names and their values onto
// the same line and inserting a colon between them.
// Tidy up block headings by moving CSS property names and their
// values onto the same line and inserting a colon between them.
let textArray = text.split(/[\r\n]+/);
let result = "";
@ -737,8 +726,7 @@ CssComputedView.prototype = {
/**
* Destructor for CssComputedView.
*/
destroy: function CssComputedView_destroy()
{
destroy: function() {
this.viewedElement = null;
this._outputParser = null;
@ -819,8 +807,7 @@ PropertyInfo.prototype = {
* @param {string} aName the CSS property name for which this PropertyView
* instance will render the rules.
*/
function PropertyView(aTree, aName)
{
function PropertyView(aTree, aName) {
this.tree = aTree;
this.name = aName;
this.getRTLAttr = aTree.getRTLAttr;
@ -864,32 +851,28 @@ PropertyView.prototype = {
* @return {string} the computed style for the current property of the
* currently highlighted element.
*/
get value()
{
get value() {
return this.propertyInfo.value;
},
/**
* An easy way to access the CssPropertyInfo behind this PropertyView.
*/
get propertyInfo()
{
get propertyInfo() {
return this._propertyInfo;
},
/**
* Does the property have any matched selectors?
*/
get hasMatchedSelectors()
{
get hasMatchedSelectors() {
return this.tree.matchedProperties.has(this.name);
},
/**
* Should this property be visible?
*/
get visible()
{
get visible() {
if (!this.tree.viewedElement) {
return false;
}
@ -913,8 +896,7 @@ PropertyView.prototype = {
* Returns the className that should be assigned to the propertyView.
* @return string
*/
get propertyHeaderClassName()
{
get propertyHeaderClassName() {
if (this.visible) {
let isDark = this.tree._darkStripe = !this.tree._darkStripe;
return isDark ? "property-view row-striped" : "property-view";
@ -927,8 +909,7 @@ PropertyView.prototype = {
* container.
* @return string
*/
get propertyContentClassName()
{
get propertyContentClassName() {
if (this.visible) {
let isDark = this.tree._darkStripe;
return isDark ? "property-content row-striped" : "property-content";
@ -940,8 +921,7 @@ PropertyView.prototype = {
* Build the markup for on computed style
* @return Element
*/
buildMain: function PropertyView_buildMain()
{
buildMain: function() {
let doc = this.tree.styleDocument;
// Build the container element
@ -998,8 +978,7 @@ PropertyView.prototype = {
return this.element;
},
buildSelectorContainer: function PropertyView_buildSelectorContainer()
{
buildSelectorContainer: function() {
let doc = this.tree.styleDocument;
let element = doc.createElementNS(HTML_NS, "div");
element.setAttribute("class", this.propertyContentClassName);
@ -1013,8 +992,7 @@ PropertyView.prototype = {
/**
* Refresh the panel's CSS property value.
*/
refresh: function PropertyView_refresh()
{
refresh: function() {
this.element.className = this.propertyHeaderClassName;
this.element.nextElementSibling.className = this.propertyContentClassName;
@ -1051,8 +1029,7 @@ PropertyView.prototype = {
/**
* Refresh the panel matched rules.
*/
refreshMatchedSelectors: function PropertyView_refreshMatchedSelectors()
{
refreshMatchedSelectors: function() {
let hasMatchedSelectors = this.hasMatchedSelectors;
this.matchedSelectorsContainer.parentNode.hidden = !hasMatchedSelectors;
@ -1063,28 +1040,29 @@ PropertyView.prototype = {
}
if (this.matchedExpanded && hasMatchedSelectors) {
return this.tree.pageStyle.getMatchedSelectors(this.tree.viewedElement, this.name).then(matched => {
if (!this.matchedExpanded) {
return;
}
return this.tree.pageStyle
.getMatchedSelectors(this.tree.viewedElement, this.name)
.then(matched => {
if (!this.matchedExpanded) {
return promise.resolve(undefined);
}
this._matchedSelectorResponse = matched;
this._matchedSelectorResponse = matched;
return this._buildMatchedSelectors().then(() => {
this.matchedExpander.setAttribute("open", "");
this.tree.inspector.emit("computed-view-property-expanded");
});
}).then(null, console.error);
} else {
this.matchedSelectorsContainer.innerHTML = "";
this.matchedExpander.removeAttribute("open");
this.tree.inspector.emit("computed-view-property-collapsed");
return promise.resolve(undefined);
return this._buildMatchedSelectors().then(() => {
this.matchedExpander.setAttribute("open", "");
this.tree.inspector.emit("computed-view-property-expanded");
});
}).then(null, console.error);
}
this.matchedSelectorsContainer.innerHTML = "";
this.matchedExpander.removeAttribute("open");
this.tree.inspector.emit("computed-view-property-collapsed");
return promise.resolve(undefined);
},
get matchedSelectors()
{
get matchedSelectors() {
return this._matchedSelectorResponse;
},
@ -1130,13 +1108,13 @@ PropertyView.prototype = {
* Provide access to the matched SelectorViews that we are currently
* displaying.
*/
get matchedSelectorViews()
{
get matchedSelectorViews() {
if (!this._matchedSelectorViews) {
this._matchedSelectorViews = [];
this._matchedSelectorResponse.forEach(
function matchedSelectorViews_convert(aSelectorInfo) {
this._matchedSelectorViews.push(new SelectorView(this.tree, aSelectorInfo));
function(aSelectorInfo) {
let selectorView = new SelectorView(this.tree, aSelectorInfo);
this._matchedSelectorViews.push(selectorView);
}, this);
}
return this._matchedSelectorViews;
@ -1146,8 +1124,7 @@ PropertyView.prototype = {
* Update all the selector source links to reflect whether we're linking to
* original sources (e.g. Sass files).
*/
updateSourceLinks: function PropertyView_updateSourceLinks()
{
updateSourceLinks: function() {
if (!this._matchedSelectorViews) {
return;
}
@ -1162,8 +1139,7 @@ PropertyView.prototype = {
* @param {Event} aEvent Used to determine the class name of the targets click
* event.
*/
onMatchedToggle: function PropertyView_onMatchedToggle(aEvent)
{
onMatchedToggle: function(aEvent) {
if (aEvent.shiftKey) {
return;
}
@ -1175,8 +1151,7 @@ PropertyView.prototype = {
/**
* The action when a user clicks on the MDN help link for a property.
*/
mdnLinkClick: function PropertyView_mdnLinkClick(aEvent)
{
mdnLinkClick: function(aEvent) {
let inspector = this.tree.inspector;
if (inspector.target.tab) {
@ -1189,7 +1164,7 @@ PropertyView.prototype = {
/**
* Destroy this property view, removing event listeners
*/
destroy: function PropertyView_destroy() {
destroy: function() {
this.element.removeEventListener("dblclick", this.onMatchedToggle, false);
this.element.removeEventListener("keydown", this.onKeyDown, false);
this.element = null;
@ -1210,8 +1185,7 @@ PropertyView.prototype = {
* @param CssComputedView aTree, the owning CssComputedView
* @param aSelectorInfo
*/
function SelectorView(aTree, aSelectorInfo)
{
function SelectorView(aTree, aSelectorInfo) {
this.tree = aTree;
this.selectorInfo = aSelectorInfo;
this._cacheStatusNames();
@ -1245,8 +1219,7 @@ SelectorView.prototype = {
*
* @return {void}
*/
_cacheStatusNames: function SelectorView_cacheStatusNames()
{
_cacheStatusNames: function() {
if (SelectorView.STATUS_NAMES.length) {
return;
}
@ -1256,7 +1229,7 @@ SelectorView.prototype = {
if (i > CssLogic.STATUS.UNMATCHED) {
let value = CssComputedView.l10n("rule.status." + status);
// Replace normal spaces with non-breaking spaces
SelectorView.STATUS_NAMES[i] = value.replace(/ /g, '\u00A0');
SelectorView.STATUS_NAMES[i] = value.replace(/ /g, "\u00A0");
}
}
},
@ -1264,21 +1237,18 @@ SelectorView.prototype = {
/**
* A localized version of cssRule.status
*/
get statusText()
{
get statusText() {
return SelectorView.STATUS_NAMES[this.selectorInfo.status];
},
/**
* Get class name for selector depending on status
*/
get statusClass()
{
get statusClass() {
return SelectorView.CLASS_NAMES[this.selectorInfo.status - 1];
},
get href()
{
get href() {
if (this._href) {
return this._href;
}
@ -1287,19 +1257,15 @@ SelectorView.prototype = {
return this._href;
},
get sourceText()
{
get sourceText() {
return this.selectorInfo.sourceText;
},
get value()
{
get value() {
return this.selectorInfo.value;
},
get outputFragment()
{
get outputFragment() {
// Sadly, because this fragment is added to the template by DOM Templater
// we lose any events that are attached. This means that URLs will open in a
// new window. At some point we should fix this by stopping using the
@ -1320,8 +1286,7 @@ SelectorView.prototype = {
* Update the text of the source link to reflect whether we're showing
* original sources or not.
*/
updateSourceLink: function()
{
updateSourceLink: function() {
return this.updateSource().then((oldSource) => {
if (oldSource != this.source && this.tree.element) {
let selector = '[sourcelocation="' + oldSource + '"]';
@ -1337,8 +1302,7 @@ SelectorView.prototype = {
/**
* Update the 'source' store based on our original sources preference.
*/
updateSource: function()
{
updateSource: function() {
let rule = this.selectorInfo.rule;
this.sheet = rule.parentStyleSheet;
@ -1373,8 +1337,7 @@ SelectorView.prototype = {
/**
* Open the style editor if the RETURN key was pressed.
*/
maybeOpenStyleEditor: function(aEvent)
{
maybeOpenStyleEditor: function(aEvent) {
let keyEvent = Ci.nsIDOMKeyEvent;
if (aEvent.keyCode == keyEvent.DOM_VK_RETURN) {
this.openStyleEditor();
@ -1391,8 +1354,7 @@ SelectorView.prototype = {
*
* @param aEvent The click event
*/
openStyleEditor: function(aEvent)
{
openStyleEditor: function(aEvent) {
let inspector = this.tree.inspector;
let rule = this.selectorInfo.rule;
@ -1401,15 +1363,8 @@ SelectorView.prototype = {
//
// If the stylesheet is a content stylesheet we send it to the style
// editor else we display it in the view source window.
let sheet = rule.parentStyleSheet;
if (!sheet || sheet.isSystem) {
let contentDoc = null;
if (this.tree.viewedElement.isLocal_toBeDeprecated()) {
let rawNode = this.tree.viewedElement.rawNode();
if (rawNode) {
contentDoc = rawNode.ownerDocument;
}
}
let parentStyleSheet = rule.parentStyleSheet;
if (!parentStyleSheet || parentStyleSheet.isSystem) {
let toolbox = gDevTools.getToolbox(inspector.target);
toolbox.viewSource(rule.href, rule.line);
return;
@ -1447,7 +1402,7 @@ function createChild(aParent, aTag, aAttributes={}) {
if (aAttributes.hasOwnProperty(attr)) {
if (attr === "textContent") {
elt.textContent = aAttributes[attr];
} else if(attr === "child") {
} else if (attr === "child") {
elt.appendChild(aAttributes[attr]);
} else {
elt.setAttribute(attr, aAttributes[attr]);

View File

@ -5,7 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* globals overlays, Services, EventEmitter, StyleInspectorMenu,
clipboardHelper, _strings, domUtils, AutocompletePopup */
clipboardHelper, _strings, domUtils, AutocompletePopup, loader,
osString */
"use strict";
@ -209,7 +210,7 @@ ElementStyle.prototype = {
filter: this.showUserAgentStyles ? "ua" : undefined,
}).then(entries => {
if (this.destroyed) {
return;
return promise.resolve(undefined);
}
// Make sure the dummy element has been created before continuing...
@ -236,14 +237,12 @@ ElementStyle.prototype = {
// We're done with the previous list of rules.
delete this._refreshRules;
return null;
});
}).then(null, e => {
// populate is often called after a setTimeout,
// the connection may already be closed.
if (this.destroyed) {
return;
return promise.resolve(undefined);
}
return promiseWarn(e);
});
@ -636,7 +635,7 @@ Rule.prototype = {
disabled.delete(this.style);
}
let promise = aModifications.apply().then(() => {
let modificationsPromise = aModifications.apply().then(() => {
let cssProps = {};
for (let cssProp of parseDeclarations(this.style.cssText)) {
cssProps[cssProp.name] = cssProp;
@ -668,8 +667,8 @@ Rule.prototype = {
this.elementStyle._changed();
}).then(null, promiseWarn);
this._applyingModifications = promise;
return promise;
this._applyingModifications = modificationsPromise;
return modificationsPromise;
},
/**
@ -1111,7 +1110,8 @@ TextProperty.prototype = {
*/
stringifyProperty: function() {
// Get the displayed property value
let declaration = this.name + ": " + this.editor.committed.value + ";";
let declaration = this.name + ": " + this.editor.valueSpan.textContent +
";";
// Comment out property declarations that are not enabled
if (!this.enabled) {
@ -1741,7 +1741,7 @@ CssRuleView.prototype = {
refreshPanel: function() {
// Ignore refreshes during editing or when no element is selected.
if (this.isEditing || !this._elementStyle) {
return;
return promise.resolve(undefined);
}
// Repopulate the element style once the current modifications are done.
@ -1893,9 +1893,10 @@ CssRuleView.prototype = {
/**
* Creates an expandable container in the rule view
* @param {String} aLabel The label for the container header
* @param {Boolean} isPseudo Whether or not the container will hold
* pseudo element rules
* @param {String} aLabel
* The label for the container header
* @param {Boolean} isPseudo
* Whether or not the container will hold pseudo element rules
* @return {DOMNode} The container element
*/
createExpandableContainer: function(aLabel, isPseudo = false) {
@ -1915,44 +1916,59 @@ CssRuleView.prototype = {
container.classList.add("ruleview-expandable-container");
this.element.appendChild(container);
let toggleContainerVisibility = (isPseudo, showPseudo) => {
let isOpen = twisty.getAttribute("open");
if (isPseudo) {
this._showPseudoElements = !!showPseudo;
Services.prefs.setBoolPref("devtools.inspector.show_pseudo_elements",
this.showPseudoElements);
header.classList.toggle("show-expandable-container",
this.showPseudoElements);
isOpen = !this.showPseudoElements;
} else {
header.classList.toggle("show-expandable-container");
}
if (isOpen) {
twisty.removeAttribute("open");
} else {
twisty.setAttribute("open", "true");
}
};
header.addEventListener("dblclick", () => {
toggleContainerVisibility(isPseudo, !this.showPseudoElements);
this._toggleContainerVisibility(twisty, header, isPseudo,
!this.showPseudoElements);
}, false);
twisty.addEventListener("click", () => {
toggleContainerVisibility(isPseudo, !this.showPseudoElements);
this._toggleContainerVisibility(twisty, header, isPseudo,
!this.showPseudoElements);
}, false);
if (isPseudo) {
toggleContainerVisibility(isPseudo, this.showPseudoElements);
this._toggleContainerVisibility(twisty, header, isPseudo,
this.showPseudoElements);
}
return container;
},
/**
* Toggle the visibility of an expandable container
* @param {DOMNode} twisty
* clickable toggle DOM Node
* @param {DOMNode} header
* expandable container header DOM Node
* @param {Boolean} isPseudo
* whether or not the container will hold pseudo element rules
* @param {Boolean} showPseudo
* whether or not pseudo element rules should be displayed
*/
_toggleContainerVisibility: function(twisty, header, isPseudo, showPseudo) {
let isOpen = twisty.getAttribute("open");
if (isPseudo) {
this._showPseudoElements = !!showPseudo;
Services.prefs.setBoolPref("devtools.inspector.show_pseudo_elements",
this.showPseudoElements);
header.classList.toggle("show-expandable-container",
this.showPseudoElements);
isOpen = !this.showPseudoElements;
} else {
header.classList.toggle("show-expandable-container");
}
if (isOpen) {
twisty.removeAttribute("open");
} else {
twisty.setAttribute("open", "true");
}
},
_getRuleViewHeaderClassName: function(isPseudo) {
let baseClassName = "theme-gutter ruleview-header";
return isPseudo ? baseClassName + " ruleview-expandable-header" :

View File

@ -231,6 +231,7 @@ StyleInspectorMenu.prototype = {
this.menuitemCopy.hidden = !this._hasTextSelected();
this.menuitemCopyColor.hidden = !this._isColorPopup();
this.menuitemCopyImageDataUrl.hidden = !this._isImageUrl();
this.menuitemCopyUrl.hidden = !this._isImageUrl();
this.menuitemCopyRule.hidden = true;
this.menuitemCopyLocation.hidden = true;
@ -378,6 +379,10 @@ StyleInspectorMenu.prototype = {
* Retrieve the url for the selected image and copy it to the clipboard
*/
_onCopyUrl: function() {
if (!this._clickedNodeInfo) {
return;
}
clipboardHelper.copyString(this._clickedNodeInfo.value.url);
},

View File

@ -52,6 +52,20 @@ add_task(function*() {
copyRule: false
}
},
{
desc: "Test Copy Property Value with Priority",
node: ruleEditor.rule.textProps[3].editor.valueSpan,
menuItem: contextmenu.menuitemCopyPropertyValue,
expectedPattern: "#00F !important",
hidden: {
copyLocation: true,
copyPropertyDeclaration: false,
copyPropertyName: true,
copyPropertyValue: false,
copySelector: true,
copyRule: false
}
},
{
desc: "Test Copy Property Declaration",
node: ruleEditor.rule.textProps[2].editor.nameSpan,
@ -66,6 +80,20 @@ add_task(function*() {
copyRule: false
}
},
{
desc: "Test Copy Property Declaration with Priority",
node: ruleEditor.rule.textProps[3].editor.nameSpan,
menuItem: contextmenu.menuitemCopyPropertyDeclaration,
expectedPattern: "border-color: #00F !important;",
hidden: {
copyLocation: true,
copyPropertyDeclaration: false,
copyPropertyName: false,
copyPropertyValue: true,
copySelector: true,
copyRule: false
}
},
{
desc: "Test Copy Rule",
node: ruleEditor.rule.textProps[2].editor.nameSpan,
@ -74,6 +102,7 @@ add_task(function*() {
"\tcolor: #F00;[\\r\\n]+" +
"\tbackground-color: #00F;[\\r\\n]+" +
"\tfont-size: 12px;[\\r\\n]+" +
"\tborder-color: #00F !important;[\\r\\n]+" +
"}",
hidden: {
copyLocation: true,
@ -124,6 +153,7 @@ add_task(function*() {
"\t\/\\* color: #F00; \\*\/[\\r\\n]+" +
"\tbackground-color: #00F;[\\r\\n]+" +
"\tfont-size: 12px;[\\r\\n]+" +
"\tborder-color: #00F !important;[\\r\\n]+" +
"}",
hidden: {
copyLocation: true,

View File

@ -80,6 +80,7 @@ function* testCopyUrlToClipboard({view, inspector}, type, selector, expected) {
yield popup;
info("Context menu is displayed");
ok(!view._contextmenu.menuitemCopyUrl.hidden, "\"Copy URL\" menu entry is displayed");
ok(!view._contextmenu.menuitemCopyImageDataUrl.hidden, "\"Copy Image Data-URL\" menu entry is displayed");
if (type == "data-uri") {

View File

@ -6,4 +6,5 @@ html, body, #testid {
color: #F00;
background-color: #00F;
font-size: 12px;
border-color: #00F !important;
}

View File

@ -558,6 +558,7 @@
@RESPATH@/components/AlarmsManager.manifest
@RESPATH@/components/Push.js
@RESPATH@/components/Push.manifest
@RESPATH@/components/PushClient.js
@RESPATH@/components/PushNotificationService.js
@RESPATH@/components/SlowScriptDebug.manifest
@ -637,6 +638,7 @@
@RESPATH@/components/nsUrlClassifierHashCompleter.js
@RESPATH@/components/nsUrlClassifierListManager.js
@RESPATH@/components/nsUrlClassifierLib.js
@RESPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js
@RESPATH@/components/url-classifier.xpt
#endif

View File

@ -97,6 +97,9 @@ quit-button.tooltiptext.mac = Quit %1$S (%2$S)
# approval before you change it.
loop-call-button3.label = Hello
loop-call-button3.tooltiptext = Start a conversation
# LOCALIZATION NOTE(loop-call-button3-pb.tooltiptext): Shown when the button is
# placed inside a Private Browsing window. %S is the value of loop-call-button3.label.
loop-call-button3-pb.tooltiptext = %S is not available in Private Browsing
social-share-button.label = Share This Page
social-share-button.tooltiptext = Share this page

View File

@ -206,6 +206,8 @@ active_screenshare_button_title=Stop sharing
inactive_screenshare_button_title=Share your screen
share_tabs_button_title2=Share your Tabs
share_windows_button_title=Share other Windows
self_view_hidden_message=Self-view hidden but still being sent; resize window to show
## LOCALIZATION NOTE (call_with_contact_title): The title displayed
## when calling a contact. Don't translate the part between {{..}} because

View File

@ -115,7 +115,7 @@
.titlebar-button {
border: none;
margin: 0 !important;
padding: 12px 17px;
padding: 10px 17px;
}
#main-window[sizemode=maximized] .titlebar-button {
@ -147,6 +147,20 @@
list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-white);
}
#titlebar-min:-moz-lwtheme {
list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-themes);
}
#titlebar-max:-moz-lwtheme {
list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-themes);
}
#main-window[sizemode="maximized"] #titlebar-max:-moz-lwtheme {
list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-themes);
}
#titlebar-close:-moz-lwtheme {
list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-themes);
}
/* the 12px image renders a 10px icon, and the 10px upscaled gets rounded to 12.5, which
* rounds up to 13px, which makes the icon one pixel too big on 1.25dppx. Fix: */
@media (min-resolution: 1.20dppx) and (max-resolution: 1.45dppx) {
@ -222,20 +236,32 @@
background-color: Highlight;
}
#titlebar-min {
list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-highcontrast);
}
#titlebar-min:hover {
list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-highlight);
list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-highcontrast-hover);
}
#titlebar-max {
list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-highcontrast);
}
#titlebar-max:hover {
list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-highlight);
list-style-image: url(chrome://browser/skin/caption-buttons.svg#maximize-highcontrast-hover);
}
#main-window[sizemode="maximized"] #titlebar-max {
list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-highcontrast);
}
#main-window[sizemode="maximized"] #titlebar-max:hover {
list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-highlight);
list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-highcontrast-hover);
}
#titlebar-close {
list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-highcontrast);
}
#titlebar-close:hover {
list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-highlight);
list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-highcontrast-hover);
}
}
}

View File

@ -659,7 +659,7 @@ toolbar[brighttext] .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
-moz-padding-end: 5px;
}
#nav-bar .toolbarbutton-1[type=panel]:not(#back-button):not(#forward-button):not(#feed-button):not(#PanelUI-menu-button),
#nav-bar .toolbarbutton-1[type=panel],
#nav-bar .toolbarbutton-1[type=menu]:not(#back-button):not(#forward-button):not(#feed-button):not(#PanelUI-menu-button) {
padding-left: 5px;
padding-right: 5px;
@ -771,8 +771,8 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
width: 32px;
}
#nav-bar .toolbarbutton-1[type=panel]:not(#back-button):not(#forward-button):not(#feed-button):not(#PanelUI-menu-button) > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1[type=panel]:not(#back-button):not(#forward-button):not(#feed-button):not(#PanelUI-menu-button) > .toolbarbutton-badge-container,
#nav-bar .toolbarbutton-1[type=panel] > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1[type=panel] > .toolbarbutton-badge-container,
#nav-bar .toolbarbutton-1[type=menu]:not(#back-button):not(#forward-button):not(#feed-button):not(#PanelUI-menu-button) > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1[type=menu]:not(#back-button):not(#forward-button):not(#feed-button):not(#PanelUI-menu-button) > .toolbarbutton-badge-container,
#nav-bar .toolbarbutton-1[type=menu] > .toolbarbutton-text /* hack for add-ons that forcefully display the label */ {
@ -1203,6 +1203,11 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
-moz-padding-end: 2px;
}
/* overlap the urlbar's border */
#PopupAutoCompleteRichResult {
margin-top: -1px;
}
@media (-moz-os-version: windows-xp),
(-moz-os-version: windows-vista),
(-moz-os-version: windows-win7) {
@ -1246,6 +1251,11 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
.searchbar-textbox:not(:-moz-lwtheme)[focused] {
box-shadow: 0 0 0 1px Highlight inset;
}
/* overlap the urlbar's border and inset box-shadow */
#PopupAutoCompleteRichResult:not(:-moz-lwtheme) {
margin-top: -2px;
}
}
@media not all and (-moz-os-version: windows-xp) {

View File

@ -9,7 +9,7 @@
fill: none;
}
g:not(#close) {
g:not([id|="close"]) {
shape-rendering: crispEdges;
}
@ -21,17 +21,36 @@
display: initial;
}
[id$="-highlight"] > g {
g.highlight {
stroke-width: 1.9px;
}
g.themes {
stroke: #fff;
stroke-width: 1.9px;
}
.outer-stroke {
stroke: #000;
stroke-width: 3.6;
opacity: .75;
}
.restore-background-window {
stroke-width: .9;
}
[id$="-highcontrast-hover"] > g {
stroke: HighlightText;
}
[id$="-white"] > g {
stroke: #fff;
}
</style>
<g id="close">
<line x1="1" y1="1" x2="11" y2="11"/>
<line x1="11" y1="1" x2="1" y2="11"/>
<path d="M1,1 l 10,10 M1,11 l 10,-10"/>
</g>
<g id="maximize">
<rect x="1.5" y="1.5" width="9" height="9"/>
@ -43,13 +62,46 @@
<rect x="1.5" y="3.5" width="7" height="7"/>
<polyline points="3.5,3.5 3.5,1.5 10.5,1.5 10.5,8.5 8.5,8.5"/>
</g>
<use id="close-highlight" xlink:href="#close"/>
<use id="maximize-highlight" xlink:href="#maximize"/>
<use id="minimize-highlight" xlink:href="#minimize"/>
<use id="restore-highlight" xlink:href="#restore"/>
<use id="close-white" xlink:href="#close"/>
<use id="maximize-white" xlink:href="#maximize"/>
<use id="minimize-white" xlink:href="#minimize"/>
<use id="restore-white" xlink:href="#restore"/>
<g id="close-highcontrast" class="highlight">
<path d="M1,1 l 10,10 M1,11 l 10,-10"/>
</g>
<g id="maximize-highcontrast" class="highlight">
<rect x="2" y="2" width="8" height="8"/>
</g>
<g id="minimize-highcontrast" class="highlight">
<line x1="1" y1="6" x2="11" y2="6"/>
</g>
<g id="restore-highcontrast" class="highlight">
<rect x="2" y="4" width="6" height="6"/>
<polyline points="3.5,1.5 10.5,1.5 10.5,8.5" class="restore-background-window"/>
</g>
<use id="close-highcontrast-hover" xlink:href="#close-highcontrast"/>
<use id="maximize-highcontrast-hover" xlink:href="#maximize-highcontrast"/>
<use id="minimize-highcontrast-hover" xlink:href="#minimize-highcontrast"/>
<use id="restore-highcontrast-hover" xlink:href="#restore-highcontrast"/>
<g id="close-themes" class="themes">
<path d="M1,1 l 10,10 M1,11 l 10,-10" class="outer-stroke" />
<path d="M1.75,1.75 l 8.5,8.5 M1.75,10.25 l 8.5,-8.5"/>
</g>
<g id="maximize-themes" class="themes">
<rect x="2" y="2" width="8" height="8" class="outer-stroke"/>
<rect x="2" y="2" width="8" height="8"/>
</g>
<g id="minimize-themes" class="themes">
<line x1="0" y1="6" x2="12" y2="6" class="outer-stroke"/>
<line x1="1" y1="6" x2="11" y2="6"/>
</g>
<g id="restore-themes" class="themes">
<path d="M2,4 l 6,0 l 0,6 l -6,0z M2.5,1.5 l 8,0 l 0,8" class="outer-stroke"/>
<rect x="2" y="4" width="6" height="6"/>
<polyline points="3.5,1.5 10.5,1.5 10.5,8.5" class="restore-background-window"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -1,6 +1,7 @@
/* 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 "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
@ -56,7 +57,6 @@ private:
ScopeChecker(Scope scope_) :
scope(scope_) {}
virtual void run(const MatchFinder::MatchResult &Result);
void noteInferred(QualType T, DiagnosticsEngine &Diag);
private:
Scope scope;
};
@ -64,7 +64,6 @@ private:
class NonHeapClassChecker : public MatchFinder::MatchCallback {
public:
virtual void run(const MatchFinder::MatchResult &Result);
void noteInferred(QualType T, DiagnosticsEngine &Diag);
};
class ArithmeticArgChecker : public MatchFinder::MatchCallback {
@ -97,6 +96,16 @@ private:
virtual void run(const MatchFinder::MatchResult &Result);
};
class NeedsNoVTableTypeChecker : public MatchFinder::MatchCallback {
public:
virtual void run(const MatchFinder::MatchResult &Result);
};
class NonMemMovableChecker : public MatchFinder::MatchCallback {
public:
virtual void run(const MatchFinder::MatchResult &Result);
};
ScopeChecker stackClassChecker;
ScopeChecker globalClassChecker;
NonHeapClassChecker nonheapClassChecker;
@ -106,6 +115,8 @@ private:
NoAddRefReleaseOnReturnChecker noAddRefReleaseOnReturnChecker;
RefCountedInsideLambdaChecker refCountedInsideLambdaChecker;
ExplicitOperatorBoolChecker explicitOperatorBoolChecker;
NeedsNoVTableTypeChecker needsNoVTableTypeChecker;
NonMemMovableChecker nonMemMovableChecker;
MatchFinder astMatcher;
};
@ -217,6 +228,51 @@ bool isInterestingDeclForImplicitConversion(const Decl *decl) {
}
class CustomTypeAnnotation {
enum ReasonKind {
RK_None,
RK_Direct,
RK_ArrayElement,
RK_BaseClass,
RK_Field,
};
struct AnnotationReason {
QualType Type;
ReasonKind Kind;
const FieldDecl *Field;
bool valid() const { return Kind != RK_None; }
};
typedef DenseMap<void *, AnnotationReason> ReasonCache;
const char *Spelling;
const char *Pretty;
ReasonCache Cache;
public:
CustomTypeAnnotation(const char *Spelling, const char *Pretty)
: Spelling(Spelling), Pretty(Pretty) {};
// Checks if this custom annotation "effectively affects" the given type.
bool hasEffectiveAnnotation(QualType T) {
return directAnnotationReason(T).valid();
}
void dumpAnnotationReason(DiagnosticsEngine &Diag, QualType T, SourceLocation Loc);
private:
bool hasLiteralAnnotation(QualType T) const;
AnnotationReason directAnnotationReason(QualType T);
};
static CustomTypeAnnotation StackClass =
CustomTypeAnnotation("moz_stack_class", "stack");
static CustomTypeAnnotation GlobalClass =
CustomTypeAnnotation("moz_global_class", "global");
static CustomTypeAnnotation NonHeapClass =
CustomTypeAnnotation("moz_nonheap_class", "non-heap");
static CustomTypeAnnotation MustUse =
CustomTypeAnnotation("moz_must_use", "must-use");
class MozChecker : public ASTConsumer, public RecursiveASTVisitor<MozChecker> {
DiagnosticsEngine &Diag;
const CompilerInstance &CI;
@ -232,29 +288,29 @@ public:
TraverseDecl(ctx.getTranslationUnitDecl());
}
static bool hasCustomAnnotation(const Decl *d, const char *spelling) {
AnnotateAttr *attr = d->getAttr<AnnotateAttr>();
if (!attr)
return false;
static bool hasCustomAnnotation(const Decl *D, const char *Spelling) {
iterator_range<specific_attr_iterator<AnnotateAttr> > Attrs =
D->specific_attrs<AnnotateAttr>();
return attr->getAnnotation() == spelling;
for (AnnotateAttr *Attr : Attrs) {
if (Attr->getAnnotation() == Spelling) {
return true;
}
}
return false;
}
void HandleUnusedExprResult(const Stmt *stmt) {
const Expr* E = dyn_cast_or_null<Expr>(stmt);
if (E) {
// XXX It would be nice if we could use getAsTagDecl,
// but our version of clang is too old.
// (getAsTagDecl would also cover enums etc.)
QualType T = E->getType();
CXXRecordDecl *decl = T->getAsCXXRecordDecl();
if (decl) {
decl = decl->getDefinition();
if (decl && hasCustomAnnotation(decl, "moz_must_use")) {
unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "Unused MOZ_MUST_USE value of type %0");
Diag.Report(E->getLocStart(), errorID) << T;
}
if (MustUse.hasEffectiveAnnotation(T)) {
unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "Unused value of must-use type %0");
Diag.Report(E->getLocStart(), errorID) << T;
MustUse.dumpAnnotationReason(Diag, T, E->getLocStart());
}
}
}
@ -378,102 +434,6 @@ public:
}
};
/**
* Where classes may be allocated. Regular classes can be allocated anywhere,
* non-heap classes on the stack or as static variables, and stack classes only
* on the stack. Note that stack classes subsumes non-heap classes.
*/
enum ClassAllocationNature {
RegularClass = 0,
NonHeapClass = 1,
StackClass = 2,
GlobalClass = 3
};
/// A cached data of whether classes are stack classes, non-heap classes, or
/// neither.
DenseMap<const CXXRecordDecl *,
std::pair<const Decl *, ClassAllocationNature> > inferredAllocCauses;
ClassAllocationNature getClassAttrs(QualType T);
ClassAllocationNature getClassAttrs(CXXRecordDecl *D) {
// Normalize so that D points to the definition if it exists. If it doesn't,
// then we can't allocate it anyways.
if (!D->hasDefinition())
return RegularClass;
D = D->getDefinition();
// Base class: anyone with this annotation is obviously a stack class
if (MozChecker::hasCustomAnnotation(D, "moz_stack_class"))
return StackClass;
// Base class: anyone with this annotation is obviously a global class
if (MozChecker::hasCustomAnnotation(D, "moz_global_class"))
return GlobalClass;
// See if we cached the result.
DenseMap<const CXXRecordDecl *,
std::pair<const Decl *, ClassAllocationNature> >::iterator it =
inferredAllocCauses.find(D);
if (it != inferredAllocCauses.end()) {
return it->second.second;
}
// Continue looking, we might be a stack class yet. Even if we're a nonheap
// class, it might be possible that we've inferred to be a stack class.
ClassAllocationNature type = RegularClass;
if (MozChecker::hasCustomAnnotation(D, "moz_nonheap_class")) {
type = NonHeapClass;
}
inferredAllocCauses.insert(std::make_pair(D,
std::make_pair((const Decl *)0, type)));
// Look through all base cases to figure out if the parent is a stack class or
// a non-heap class. Since we might later infer to also be a stack class, keep
// going.
for (CXXRecordDecl::base_class_iterator base = D->bases_begin(),
e = D->bases_end(); base != e; ++base) {
ClassAllocationNature super = getClassAttrs(base->getType());
if (super == StackClass) {
inferredAllocCauses[D] = std::make_pair(
base->getType()->getAsCXXRecordDecl(), StackClass);
return StackClass;
} else if (super == GlobalClass) {
inferredAllocCauses[D] = std::make_pair(
base->getType()->getAsCXXRecordDecl(), GlobalClass);
return GlobalClass;
} else if (super == NonHeapClass) {
inferredAllocCauses[D] = std::make_pair(
base->getType()->getAsCXXRecordDecl(), NonHeapClass);
type = NonHeapClass;
}
}
// Maybe it has a member which is a stack class.
for (RecordDecl::field_iterator field = D->field_begin(), e = D->field_end();
field != e; ++field) {
ClassAllocationNature fieldType = getClassAttrs(field->getType());
if (fieldType == StackClass) {
inferredAllocCauses[D] = std::make_pair(*field, StackClass);
return StackClass;
} else if (fieldType == GlobalClass) {
inferredAllocCauses[D] = std::make_pair(*field, GlobalClass);
return GlobalClass;
} else if (fieldType == NonHeapClass) {
inferredAllocCauses[D] = std::make_pair(*field, NonHeapClass);
type = NonHeapClass;
}
}
return type;
}
ClassAllocationNature getClassAttrs(QualType T) {
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
T = arrTy->getElementType();
CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
return clazz ? getClassAttrs(clazz) : RegularClass;
}
/// A cached data of whether classes are refcounted or not.
typedef DenseMap<const CXXRecordDecl *,
std::pair<const Decl *, bool> > RefCountedMap;
@ -527,7 +487,88 @@ bool isClassRefCounted(QualType T) {
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
T = arrTy->getElementType();
CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
return clazz ? isClassRefCounted(clazz) : RegularClass;
return clazz ? isClassRefCounted(clazz) : false;
}
/// A cached data of whether classes are memmovable, and if not, what declaration
/// makes them non-movable
typedef DenseMap<const CXXRecordDecl *, const CXXRecordDecl *> InferredMovability;
InferredMovability inferredMovability;
bool isClassNonMemMovable(QualType T);
const CXXRecordDecl* isClassNonMemMovableWorker(QualType T);
const CXXRecordDecl* isClassNonMemMovableWorker(const CXXRecordDecl *D) {
// If we have a definition, then we want to standardize our reference to point
// to the definition node. If we don't have a definition, that means that either
// we only have a forward declaration of the type in our file, or we are being
// passed a template argument which is not used, and thus never instantiated by
// clang.
// As the argument isn't used, we can't memmove it (as we don't know it's size),
// which means not reporting an error is OK.
if (!D->hasDefinition()) {
return 0;
}
D = D->getDefinition();
// Are we explicitly marked as non-memmovable class?
if (MozChecker::hasCustomAnnotation(D, "moz_non_memmovable")) {
return D;
}
// Look through all base cases to figure out if the parent is a non-memmovable class.
for (CXXRecordDecl::base_class_const_iterator base = D->bases_begin();
base != D->bases_end(); ++base) {
const CXXRecordDecl *result = isClassNonMemMovableWorker(base->getType());
if (result) {
return result;
}
}
// Look through all members to figure out if a member is a non-memmovable class.
for (RecordDecl::field_iterator field = D->field_begin(), e = D->field_end();
field != e; ++field) {
const CXXRecordDecl *result = isClassNonMemMovableWorker(field->getType());
if (result) {
return result;
}
}
return 0;
}
const CXXRecordDecl* isClassNonMemMovableWorker(QualType T) {
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
T = arrTy->getElementType();
const CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
return clazz ? isClassNonMemMovableWorker(clazz) : 0;
}
bool isClassNonMemMovable(const CXXRecordDecl *D) {
InferredMovability::iterator it =
inferredMovability.find(D);
if (it != inferredMovability.end())
return !!it->second;
const CXXRecordDecl *result = isClassNonMemMovableWorker(D);
inferredMovability.insert(std::make_pair(D, result));
return !!result;
}
bool isClassNonMemMovable(QualType T) {
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
T = arrTy->getElementType();
const CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
return clazz ? isClassNonMemMovable(clazz) : false;
}
const CXXRecordDecl* findWhyClassIsNonMemMovable(QualType T) {
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
T = arrTy->getElementType();
CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
InferredMovability::iterator it =
inferredMovability.find(clazz);
assert(it != inferredMovability.end());
return it->second;
}
template<class T>
@ -540,6 +581,13 @@ bool IsInSystemHeader(const ASTContext &AC, const T &D) {
return SourceManager.isInSystemHeader(ExpansionLoc);
}
bool typeHasVTable(QualType T) {
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
T = arrTy->getElementType();
CXXRecordDecl* offender = T->getAsCXXRecordDecl();
return offender && offender->hasDefinition() && offender->isDynamicClass();
}
}
namespace clang {
@ -548,19 +596,19 @@ namespace ast_matchers {
/// This matcher will match any class with the stack class assertion or an
/// array of such classes.
AST_MATCHER(QualType, stackClassAggregate) {
return getClassAttrs(Node) == StackClass;
return StackClass.hasEffectiveAnnotation(Node);
}
/// This matcher will match any class with the global class assertion or an
/// array of such classes.
AST_MATCHER(QualType, globalClassAggregate) {
return getClassAttrs(Node) == GlobalClass;
return GlobalClass.hasEffectiveAnnotation(Node);
}
/// This matcher will match any class with the stack class assertion or an
/// array of such classes.
AST_MATCHER(QualType, nonheapClassAggregate) {
return getClassAttrs(Node) == NonHeapClass;
return NonHeapClass.hasEffectiveAnnotation(Node);
}
/// This matcher will match any function declaration that is declared as a heap
@ -694,11 +742,126 @@ AST_POLYMORPHIC_MATCHER_P(equalsBoundNode,
#endif
AST_MATCHER(QualType, hasVTable) {
return typeHasVTable(Node);
}
AST_MATCHER(CXXRecordDecl, hasNeedsNoVTableTypeAttr) {
return MozChecker::hasCustomAnnotation(&Node, "moz_needs_no_vtable_type");
}
/// This matcher will select classes which are non-memmovable
AST_MATCHER(QualType, isNonMemMovable) {
return isClassNonMemMovable(Node);
}
/// This matcher will select classes which require a memmovable template arg
AST_MATCHER(CXXRecordDecl, needsMemMovable) {
return MozChecker::hasCustomAnnotation(&Node, "moz_needs_memmovable_type");
}
}
}
namespace {
void CustomTypeAnnotation::dumpAnnotationReason(DiagnosticsEngine &Diag, QualType T, SourceLocation Loc) {
unsigned InheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note, "%1 is a %0 type because it inherits from a %0 type %2");
unsigned MemberID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note, "%1 is a %0 type because member %2 is a %0 type %3");
unsigned ArrayID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note, "%1 is a %0 type because it is an array of %0 type %2");
unsigned TemplID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note, "%1 is a %0 type because it has a template argument %0 type %2");
AnnotationReason Reason = directAnnotationReason(T);
for (;;) {
switch (Reason.Kind) {
case RK_ArrayElement:
Diag.Report(Loc, ArrayID)
<< Pretty << T << Reason.Type;
break;
case RK_BaseClass:
{
const CXXRecordDecl *Decl = T->getAsCXXRecordDecl();
assert(Decl && "This type should be a C++ class");
Diag.Report(Decl->getLocation(), InheritsID)
<< Pretty << T << Reason.Type;
break;
}
case RK_Field:
Diag.Report(Reason.Field->getLocation(), MemberID)
<< Pretty << T << Reason.Field << Reason.Type;
break;
default:
return;
}
T = Reason.Type;
Reason = directAnnotationReason(T);
}
}
bool CustomTypeAnnotation::hasLiteralAnnotation(QualType T) const {
if (const TagDecl *D = T->getAsTagDecl()) {
return MozChecker::hasCustomAnnotation(D, Spelling);
}
return false;
}
CustomTypeAnnotation::AnnotationReason CustomTypeAnnotation::directAnnotationReason(QualType T) {
if (hasLiteralAnnotation(T)) {
AnnotationReason Reason = { T, RK_Direct, nullptr };
return Reason;
}
// Check if we have a cached answer
void *Key = T.getAsOpaquePtr();
ReasonCache::iterator Cached = Cache.find(T.getAsOpaquePtr());
if (Cached != Cache.end()) {
return Cached->second;
}
// Check if we have a type which we can recurse into
if (const ArrayType *Array = T->getAsArrayTypeUnsafe()) {
if (hasEffectiveAnnotation(Array->getElementType())) {
AnnotationReason Reason = { Array->getElementType(), RK_ArrayElement, nullptr };
Cache[Key] = Reason;
return Reason;
}
}
// Recurse into base classes
if (const CXXRecordDecl *Decl = T->getAsCXXRecordDecl()) {
if (Decl->hasDefinition()) {
Decl = Decl->getDefinition();
for (const CXXBaseSpecifier &Base : Decl->bases()) {
if (hasEffectiveAnnotation(Base.getType())) {
AnnotationReason Reason = { Base.getType(), RK_BaseClass, nullptr };
Cache[Key] = Reason;
return Reason;
}
}
// Recurse into members
for (const FieldDecl *Field : Decl->fields()) {
if (hasEffectiveAnnotation(Field->getType())) {
AnnotationReason Reason = { Field->getType(), RK_Field, Field };
Cache[Key] = Reason;
return Reason;
}
}
}
}
AnnotationReason Reason = { QualType(), RK_None, nullptr };
Cache[Key] = Reason;
return Reason;
}
bool isPlacementNew(const CXXNewExpr *expr) {
// Regular new expressions aren't placement new
if (expr->getNumPlacementArgs() == 0)
@ -816,6 +979,18 @@ DiagnosticsMatcher::DiagnosticsMatcher()
astMatcher.addMatcher(methodDecl(anyOf(hasName("operator bool"),
hasName("operator _Bool"))).bind("node"),
&explicitOperatorBoolChecker);
astMatcher.addMatcher(classTemplateSpecializationDecl(
allOf(hasAnyTemplateArgument(refersToType(hasVTable())),
hasNeedsNoVTableTypeAttr())).bind("node"),
&needsNoVTableTypeChecker);
// Handle non-mem-movable template specializations
astMatcher.addMatcher(classTemplateSpecializationDecl(
allOf(needsMemMovable(),
hasAnyTemplateArgument(refersToType(isNonMemMovable())))
).bind("specialization"),
&nonMemMovableChecker);
}
void DiagnosticsMatcher::ScopeChecker::run(
@ -825,7 +1000,9 @@ void DiagnosticsMatcher::ScopeChecker::run(
DiagnosticIDs::Error, "variable of type %0 only valid on the stack");
unsigned globalID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "variable of type %0 only valid as global");
unsigned errorID = (scope == eGlobal) ? globalID : stackID;
SourceLocation Loc;
QualType T;
if (const VarDecl *d = Result.Nodes.getNodeAs<VarDecl>("node")) {
if (scope == eLocal) {
// Ignore the match if it's a local variable.
@ -840,56 +1017,29 @@ void DiagnosticsMatcher::ScopeChecker::run(
return;
}
Diag.Report(d->getLocation(), errorID) << d->getType();
noteInferred(d->getType(), Diag);
Loc = d->getLocation();
T = d->getType();
} else if (const CXXNewExpr *expr =
Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
// If it's placement new, then this match doesn't count.
if (scope == eLocal && isPlacementNew(expr))
return;
Diag.Report(expr->getStartLoc(), errorID) << expr->getAllocatedType();
noteInferred(expr->getAllocatedType(), Diag);
Loc = expr->getStartLoc();
T = expr->getAllocatedType();
} else if (const CallExpr *expr =
Result.Nodes.getNodeAs<CallExpr>("node")) {
QualType badType = GetCallReturnType(expr)->getPointeeType();
Diag.Report(expr->getLocStart(), errorID) << badType;
noteInferred(badType, Diag);
}
}
void DiagnosticsMatcher::ScopeChecker::noteInferred(QualType T,
DiagnosticsEngine &Diag) {
unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note,
"%0 is a %2 class because it inherits from a %2 class %1");
unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note,
"%0 is a %3 class because member %1 is a %3 class %2");
const char* attribute = (scope == eGlobal) ?
"moz_global_class" : "moz_stack_class";
const char* type = (scope == eGlobal) ?
"global" : "stack";
// Find the CXXRecordDecl that is the local/global class of interest
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
T = arrTy->getElementType();
CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
// Direct result, we're done.
if (MozChecker::hasCustomAnnotation(clazz, attribute))
return;
const Decl *cause = inferredAllocCauses[clazz].first;
if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(cause)) {
Diag.Report(clazz->getLocation(), inheritsID) <<
T << CRD->getDeclName() << type;
} else if (const FieldDecl *FD = dyn_cast<FieldDecl>(cause)) {
Diag.Report(FD->getLocation(), memberID) <<
T << FD << FD->getType() << type;
Loc = expr->getLocStart();
T = GetCallReturnType(expr)->getPointeeType();
}
// Recursively follow this back.
noteInferred(cast<ValueDecl>(cause)->getType(), Diag);
if (scope == eLocal) {
Diag.Report(Loc, stackID) << T;
StackClass.dumpAnnotationReason(Diag, T, Loc);
} else if (scope == eGlobal) {
Diag.Report(Loc, globalID) << T;
GlobalClass.dumpAnnotationReason(Diag, T, Loc);
}
}
void DiagnosticsMatcher::NonHeapClassChecker::run(
@ -897,46 +1047,22 @@ void DiagnosticsMatcher::NonHeapClassChecker::run(
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "variable of type %0 is not valid on the heap");
SourceLocation Loc;
QualType T;
if (const CXXNewExpr *expr = Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
// If it's placement new, then this match doesn't count.
if (isPlacementNew(expr))
return;
Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType();
noteInferred(expr->getAllocatedType(), Diag);
Loc = expr->getLocStart();
T = expr->getAllocatedType();
} else if (const CallExpr *expr = Result.Nodes.getNodeAs<CallExpr>("node")) {
QualType badType = GetCallReturnType(expr)->getPointeeType();
Diag.Report(expr->getLocStart(), stackID) << badType;
noteInferred(badType, Diag);
Loc = expr->getLocStart();
T = GetCallReturnType(expr)->getPointeeType();
}
}
void DiagnosticsMatcher::NonHeapClassChecker::noteInferred(QualType T,
DiagnosticsEngine &Diag) {
unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note,
"%0 is a non-heap class because it inherits from a non-heap class %1");
unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note,
"%0 is a non-heap class because member %1 is a non-heap class %2");
// Find the CXXRecordDecl that is the stack class of interest
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
T = arrTy->getElementType();
CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
// Direct result, we're done.
if (MozChecker::hasCustomAnnotation(clazz, "moz_nonheap_class"))
return;
const Decl *cause = inferredAllocCauses[clazz].first;
if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(cause)) {
Diag.Report(clazz->getLocation(), inheritsID) << T << CRD->getDeclName();
} else if (const FieldDecl *FD = dyn_cast<FieldDecl>(cause)) {
Diag.Report(FD->getLocation(), memberID) << T << FD << FD->getType();
}
// Recursively follow this back.
noteInferred(cast<ValueDecl>(cause)->getType(), Diag);
Diag.Report(Loc, stackID) << T;
NonHeapClass.dumpAnnotationReason(Diag, T, Loc);
}
void DiagnosticsMatcher::ArithmeticArgChecker::run(
@ -1047,6 +1173,72 @@ void DiagnosticsMatcher::ExplicitOperatorBoolChecker::run(
}
}
void DiagnosticsMatcher::NeedsNoVTableTypeChecker::run(
const MatchFinder::MatchResult &Result) {
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "%0 cannot be instantiated because %1 has a VTable");
unsigned noteID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note, "bad instantiation of %0 requested here");
const ClassTemplateSpecializationDecl *specialization =
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("node");
// Get the offending template argument
QualType offender;
const TemplateArgumentList &args =
specialization->getTemplateInstantiationArgs();
for (unsigned i = 0; i < args.size(); ++i) {
offender = args[i].getAsType();
if (typeHasVTable(offender)) {
break;
}
}
Diag.Report(specialization->getLocStart(), errorID) << specialization << offender;
Diag.Report(specialization->getPointOfInstantiation(), noteID) << specialization;
}
void DiagnosticsMatcher::NonMemMovableChecker::run(
const MatchFinder::MatchResult &Result) {
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "Cannot instantiate %0 with non-memmovable template argument %1");
unsigned note1ID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note, "instantiation of %0 requested here");
unsigned note2ID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Note, "%0 is non-memmovable because of the MOZ_NON_MEMMOVABLE annotation on %1");
unsigned note3ID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Note, "%0");
// Get the specialization
const ClassTemplateSpecializationDecl *specialization =
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("specialization");
SourceLocation requestLoc = specialization->getPointOfInstantiation();
const CXXRecordDecl *templ =
specialization->getSpecializedTemplate()->getTemplatedDecl();
// Report an error for every template argument which is non-memmovable
const TemplateArgumentList &args =
specialization->getTemplateInstantiationArgs();
for (unsigned i = 0; i < args.size(); ++i) {
QualType argType = args[i].getAsType();
if (isClassNonMemMovable(args[i].getAsType())) {
const CXXRecordDecl *reason = findWhyClassIsNonMemMovable(argType);
Diag.Report(specialization->getLocation(), errorID)
<< specialization << argType;
// XXX It would be really nice if we could get the instantiation stack information
// from Sema such that we could print a full template instantiation stack, however,
// it seems as though that information is thrown out by the time we get here so we
// can only report one level of template specialization (which in many cases won't
// be useful)
Diag.Report(requestLoc, note1ID)
<< specialization;
Diag.Report(reason->getLocation(), note2ID)
<< argType << reason;
}
}
}
class MozCheckAction : public PluginASTAction {
public:
ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI, StringRef fileName) override {

View File

@ -16,9 +16,9 @@ void gobble(void *) { }
void misuseGlobalClass(int len) {
Global notValid; // expected-error {{variable of type 'Global' only valid as global}}
Global alsoNotValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}}
Global alsoNotValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}} expected-note {{'Global [2]' is a global type because it is an array of global type 'Global'}}
static Global valid; // expected-error {{variable of type 'Global' only valid as global}}
static Global alsoValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}}
static Global alsoValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}} expected-note {{'Global [2]' is a global type because it is an array of global type 'Global'}}
gobble(&valid);
gobble(&notValid);
@ -35,7 +35,7 @@ void misuseGlobalClass(int len) {
Global valid;
struct RandomClass {
Global nonstaticMember; // expected-note {{'RandomClass' is a global class because member 'nonstaticMember' is a global class 'Global'}}
Global nonstaticMember; // expected-note {{'RandomClass' is a global type because member 'nonstaticMember' is a global type 'Global'}}
static Global staticMember;
};
struct MOZ_GLOBAL_CLASS RandomGlobalClass {
@ -43,7 +43,7 @@ struct MOZ_GLOBAL_CLASS RandomGlobalClass {
static Global staticMember;
};
struct BadInherit : Global {}; // expected-note {{'BadInherit' is a global class because it inherits from a global class 'Global'}}
struct BadInherit : Global {}; // expected-note {{'BadInherit' is a global type because it inherits from a global type 'Global'}}
struct MOZ_GLOBAL_CLASS GoodInherit : Global {};
void misuseGlobalClassEvenMore(int len) {

View File

@ -0,0 +1,17 @@
#define MOZ_MUST_USE __attribute__((annotate("moz_must_use")))
#define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class")))
class MOZ_MUST_USE MOZ_STACK_CLASS TestClass {};
TestClass foo; // expected-error {{variable of type 'TestClass' only valid on the stack}}
TestClass f()
{
TestClass bar;
return bar;
}
void g()
{
f(); // expected-error {{Unused value of must-use type 'TestClass'}}
}

View File

@ -20,42 +20,42 @@ void use(MayUse&&);
void use(bool);
void foo() {
producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMustUsePointer();
producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMayUse();
producesMayUsePointer();
producesMayUseRef();
{
producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMustUsePointer();
producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMayUse();
producesMayUsePointer();
producesMayUseRef();
}
if (true) {
producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMustUsePointer();
producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMayUse();
producesMayUsePointer();
producesMayUseRef();
} else {
producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMustUsePointer();
producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMayUse();
producesMayUsePointer();
producesMayUseRef();
}
if(true) producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
else producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
if(true) producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
else producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
if(true) producesMustUsePointer();
else producesMustUsePointer();
if(true) producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
else producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
if(true) producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
else producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
if(true) producesMayUse();
else producesMayUse();
if(true) producesMayUsePointer();
@ -63,18 +63,18 @@ void foo() {
if(true) producesMayUseRef();
else producesMayUseRef();
while (true) producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
while (true) producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
while (true) producesMustUsePointer();
while (true) producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
while (true) producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
while (true) producesMayUse();
while (true) producesMayUsePointer();
while (true) producesMayUseRef();
do producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
do producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
while (true);
do producesMustUsePointer();
while (true);
do producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
do producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
while (true);
do producesMayUse();
while (true);
@ -83,48 +83,48 @@ void foo() {
do producesMayUseRef();
while (true);
for (;;) producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
for (;;) producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
for (;;) producesMustUsePointer();
for (;;) producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
for (;;) producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
for (;;) producesMayUse();
for (;;) producesMayUsePointer();
for (;;) producesMayUseRef();
for (producesMustUse();;); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
for (producesMustUse();;); // expected-error {{Unused value of must-use type 'MustUse'}}
for (producesMustUsePointer();;);
for (producesMustUseRef();;); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
for (producesMustUseRef();;); // expected-error {{Unused value of must-use type 'MustUse'}}
for (producesMayUse();;);
for (producesMayUsePointer();;);
for (producesMayUseRef();;);
for (;;producesMustUse()); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
for (;;producesMustUse()); // expected-error {{Unused value of must-use type 'MustUse'}}
for (;;producesMustUsePointer());
for (;;producesMustUseRef()); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
for (;;producesMustUseRef()); // expected-error {{Unused value of must-use type 'MustUse'}}
for (;;producesMayUse());
for (;;producesMayUsePointer());
for (;;producesMayUseRef());
use((producesMustUse(), false)); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
use((producesMustUse(), false)); // expected-error {{Unused value of must-use type 'MustUse'}}
use((producesMustUsePointer(), false));
use((producesMustUseRef(), false)); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
use((producesMustUseRef(), false)); // expected-error {{Unused value of must-use type 'MustUse'}}
use((producesMayUse(), false));
use((producesMayUsePointer(), false));
use((producesMayUseRef(), false));
switch (1) {
case 1:
producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMustUsePointer();
producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMayUse();
producesMayUsePointer();
producesMayUseRef();
case 2:
producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
case 3:
producesMustUsePointer();
case 4:
producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
case 5:
producesMayUse();
case 6:
@ -132,9 +132,9 @@ void foo() {
case 7:
producesMayUseRef();
default:
producesMustUse(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMustUsePointer();
producesMustUseRef(); // expected-error {{Unused MOZ_MUST_USE value of type 'MustUse'}}
producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}}
producesMayUse();
producesMayUsePointer();
producesMayUseRef();

View File

@ -0,0 +1,94 @@
#define MOZ_NEEDS_NO_VTABLE_TYPE __attribute__((annotate("moz_needs_no_vtable_type")))
template <class T>
struct MOZ_NEEDS_NO_VTABLE_TYPE PickyConsumer { // expected-error {{'PickyConsumer<B>' cannot be instantiated because 'B' has a VTable}} expected-error {{'PickyConsumer<E>' cannot be instantiated because 'E' has a VTable}} expected-error {{'PickyConsumer<F>' cannot be instantiated because 'F' has a VTable}} expected-error {{'PickyConsumer<G>' cannot be instantiated because 'G' has a VTable}}
T *m;
};
template <class T>
struct MOZ_NEEDS_NO_VTABLE_TYPE PickyConsumer_A { // expected-error {{'PickyConsumer_A<B>' cannot be instantiated because 'B' has a VTable}} expected-error {{'PickyConsumer_A<E>' cannot be instantiated because 'E' has a VTable}} expected-error {{'PickyConsumer_A<F>' cannot be instantiated because 'F' has a VTable}} expected-error {{'PickyConsumer_A<G>' cannot be instantiated because 'G' has a VTable}}
T *m;
};
template <class T>
struct PickyConsumerWrapper {
PickyConsumer_A<T> m; // expected-note {{bad instantiation of 'PickyConsumer_A<B>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_A<E>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_A<F>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_A<G>' requested here}}
};
template <class T>
struct MOZ_NEEDS_NO_VTABLE_TYPE PickyConsumer_B { // expected-error {{'PickyConsumer_B<B>' cannot be instantiated because 'B' has a VTable}} expected-error {{'PickyConsumer_B<E>' cannot be instantiated because 'E' has a VTable}} expected-error {{'PickyConsumer_B<F>' cannot be instantiated because 'F' has a VTable}} expected-error {{'PickyConsumer_B<G>' cannot be instantiated because 'G' has a VTable}}
T *m;
};
template <class T>
struct PickyConsumerSubclass : PickyConsumer_B<T> {}; // expected-note {{bad instantiation of 'PickyConsumer_B<B>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_B<E>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_B<F>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_B<G>' requested here}}
template <class T>
struct NonPickyConsumer {
T *m;
};
struct A {};
struct B : virtual A {};
struct C : A {};
struct D {
void d();
};
struct E {
virtual void e();
};
struct F : E {
virtual void e() final;
};
struct G {
virtual void e() = 0;
};
void f() {
{
PickyConsumer<A> a1;
PickyConsumerWrapper<A> a2;
PickyConsumerSubclass<A> a3;
NonPickyConsumer<A> a4;
}
{
PickyConsumer<B> a1; // expected-note {{bad instantiation of 'PickyConsumer<B>' requested here}}
PickyConsumerWrapper<B> a2;
PickyConsumerSubclass<B> a3;
NonPickyConsumer<B> a4;
}
{
PickyConsumer<C> a1;
PickyConsumerWrapper<C> a2;
PickyConsumerSubclass<C> a3;
NonPickyConsumer<C> a4;
}
{
PickyConsumer<D> a1;
PickyConsumerWrapper<D> a2;
PickyConsumerSubclass<D> a3;
NonPickyConsumer<D> a4;
}
{
PickyConsumer<E> a1; // expected-note {{bad instantiation of 'PickyConsumer<E>' requested here}}
PickyConsumerWrapper<E> a2;
PickyConsumerSubclass<E> a3;
NonPickyConsumer<E> a4;
}
{
PickyConsumer<F> a1; // expected-note {{bad instantiation of 'PickyConsumer<F>' requested here}}
PickyConsumerWrapper<F> a2;
PickyConsumerSubclass<F> a3;
NonPickyConsumer<F> a4;
}
{
PickyConsumer<G> a1; // expected-note {{bad instantiation of 'PickyConsumer<G>' requested here}}
PickyConsumerWrapper<G> a2;
PickyConsumerSubclass<G> a3;
NonPickyConsumer<G> a4;
}
}

View File

@ -36,7 +36,7 @@ void misuseNonHeapClass(int len) {
NonHeap validStatic;
struct RandomClass {
NonHeap nonstaticMember; // expected-note {{'RandomClass' is a non-heap class because member 'nonstaticMember' is a non-heap class 'NonHeap'}}
NonHeap nonstaticMember; // expected-note {{'RandomClass' is a non-heap type because member 'nonstaticMember' is a non-heap type 'NonHeap'}}
static NonHeap staticMember;
};
struct MOZ_NONHEAP_CLASS RandomNonHeapClass {
@ -44,7 +44,7 @@ struct MOZ_NONHEAP_CLASS RandomNonHeapClass {
static NonHeap staticMember;
};
struct BadInherit : NonHeap {}; // expected-note {{'BadInherit' is a non-heap class because it inherits from a non-heap class 'NonHeap'}}
struct BadInherit : NonHeap {}; // expected-note {{'BadInherit' is a non-heap type because it inherits from a non-heap type 'NonHeap'}}
struct MOZ_NONHEAP_CLASS GoodInherit : NonHeap {};
void useStuffWrongly() {
@ -52,11 +52,11 @@ void useStuffWrongly() {
gobble(new RandomClass); // expected-error {{variable of type 'RandomClass' is not valid on the heap}}
}
// Stack class overrides non-heap classes.
// Stack class overrides non-heap typees.
struct MOZ_STACK_CLASS StackClass {};
struct MOZ_NONHEAP_CLASS InferredStackClass : GoodInherit {
NonHeap nonstaticMember;
StackClass stackClass; // expected-note {{'InferredStackClass' is a stack class because member 'stackClass' is a stack class 'StackClass'}}
StackClass stackClass; // expected-note {{'InferredStackClass' is a stack type because member 'stackClass' is a stack type 'StackClass'}}
};
InferredStackClass global; // expected-error {{variable of type 'InferredStackClass' only valid on the stack}}

View File

@ -0,0 +1,812 @@
#define MOZ_NON_MEMMOVABLE __attribute__((annotate("moz_non_memmovable")))
#define MOZ_NEEDS_MEMMOVABLE_TYPE __attribute__((annotate("moz_needs_memmovable_type")))
/*
These are a bunch of structs with variable levels of memmovability.
They will be used as template parameters to the various NeedyTemplates
*/
struct MOZ_NON_MEMMOVABLE NonMovable {}; // expected-note-re + {{'{{.*}}' is non-memmovable because of the MOZ_NON_MEMMOVABLE annotation on 'NonMovable'}}
struct Movable {};
// Subclasses
struct S_NonMovable : NonMovable {};
struct S_Movable : Movable {};
// Members
struct W_NonMovable {
NonMovable m;
};
struct W_Movable {
Movable m;
};
// Wrapped Subclasses
struct WS_NonMovable {
S_NonMovable m;
};
struct WS_Movable {
S_Movable m;
};
// Combinations of the above
struct SW_NonMovable : W_NonMovable {};
struct SW_Movable : W_Movable {};
struct SWS_NonMovable : WS_NonMovable {};
struct SWS_Movable : WS_Movable {};
// Basic templated wrapper
template <class T>
struct Template_Inline {
T m;
};
template <class T>
struct Template_Ref {
T* m;
};
template <class T>
struct Template_Unused {};
template <class T>
struct MOZ_NON_MEMMOVABLE Template_NonMovable {}; // expected-note-re + {{'{{.*}}' is non-memmovable because of the MOZ_NON_MEMMOVABLE annotation on 'Template_NonMovable<{{.*}}>'}}
/*
These tests take the following form:
DECLARATIONS => Declarations of the templates which are either marked with MOZ_NEEDS_MEMMOVABLE_TYPE
or which instantiate a MOZ_NEEDS_MEMMOVABLE_TYPE through some mechanism.
BAD N => Instantiations of the wrapper template with each of the non-memmovable types.
The prefix S_ means subclass, W_ means wrapped. Each of these rows should produce an error
on the NeedyTemplate in question, and a note at the instantiation location of that template.
Unfortunately, on every case more complicated than bad1, the instantiation location is
within another template. Thus, the notes are expected on the template in question which
actually instantiates the MOZ_NEEDS_MEMMOVABLE_TYPE template.
GOOD N => Instantiations of the wrapper template with each of the memmovable types.
This is meant as a sanity check to ensure that we don't reject valid instantiations of
templates.
Note 1: Each set uses it's own types to ensure that they don't re-use each-other's template specializations.
If they did, then some of the error messages would not be emitted (as error messages are emitted for template
specializations, rather than for variable declarations)
Note 2: Every instance of NeedyTemplate contains a member of type T. This is to ensure that T is actually
instantiated (if T is a template) by clang. If T isn't instantiated, then we can't actually tell if it is
NON_MEMMOVABLE. (This is OK in practice, as you cannot memmove a type which you don't know the size of).
Note 3: There are a set of tests for specializations of NeedyTemplate at the bottom. For each set of tests,
these tests contribute two expected errors to the templates.
*/
//
// 1 - Unwrapped MOZ_NEEDS_MEMMOVABLE_TYPE
//
template <class T>
struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate1 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate1<{{.*}}>' with non-memmovable template argument '{{.*}}'}}
void bad1() {
NeedyTemplate1<NonMovable> a1; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<S_NonMovable> a2; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<W_NonMovable> a3; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<WS_NonMovable> a4; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<SW_NonMovable> a5; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<SWS_NonMovable> a6; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_Inline<NonMovable> > b1; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_Inline<S_NonMovable> > b2; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_Inline<W_NonMovable> > b3; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_Inline<WS_NonMovable> > b4; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_Inline<SW_NonMovable> > b5; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_Inline<SWS_NonMovable> > b6; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_NonMovable<NonMovable> > c1; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_NonMovable<S_NonMovable> > c2; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_NonMovable<W_NonMovable> > c3; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_NonMovable<WS_NonMovable> > c4; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_NonMovable<SW_NonMovable> > c5; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_NonMovable<SWS_NonMovable> > c6; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_NonMovable<Movable> > c7; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_NonMovable<S_Movable> > c8; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_NonMovable<W_Movable> > c9; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_NonMovable<WS_Movable> > c10; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_NonMovable<SW_Movable> > c11; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
NeedyTemplate1<Template_NonMovable<SWS_Movable> > c12; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}}
}
void good1() {
NeedyTemplate1<Movable> a1;
NeedyTemplate1<S_Movable> a2;
NeedyTemplate1<W_Movable> a3;
NeedyTemplate1<WS_Movable> a4;
NeedyTemplate1<SW_Movable> a5;
NeedyTemplate1<SWS_Movable> a6;
NeedyTemplate1<Template_Inline<Movable> > b1;
NeedyTemplate1<Template_Inline<S_Movable> > b2;
NeedyTemplate1<Template_Inline<W_Movable> > b3;
NeedyTemplate1<Template_Inline<WS_Movable> > b4;
NeedyTemplate1<Template_Inline<SW_Movable> > b5;
NeedyTemplate1<Template_Inline<SWS_Movable> > b6;
NeedyTemplate1<Template_Unused<Movable> > c1;
NeedyTemplate1<Template_Unused<S_Movable> > c2;
NeedyTemplate1<Template_Unused<W_Movable> > c3;
NeedyTemplate1<Template_Unused<WS_Movable> > c4;
NeedyTemplate1<Template_Unused<SW_Movable> > c5;
NeedyTemplate1<Template_Unused<SWS_Movable> > c6;
NeedyTemplate1<Template_Unused<NonMovable> > c7;
NeedyTemplate1<Template_Unused<S_NonMovable> > c8;
NeedyTemplate1<Template_Unused<W_NonMovable> > c9;
NeedyTemplate1<Template_Unused<WS_NonMovable> > c10;
NeedyTemplate1<Template_Unused<SW_NonMovable> > c11;
NeedyTemplate1<Template_Unused<SWS_NonMovable> > c12;
NeedyTemplate1<Template_Ref<Movable> > d1;
NeedyTemplate1<Template_Ref<S_Movable> > d2;
NeedyTemplate1<Template_Ref<W_Movable> > d3;
NeedyTemplate1<Template_Ref<WS_Movable> > d4;
NeedyTemplate1<Template_Ref<SW_Movable> > d5;
NeedyTemplate1<Template_Ref<SWS_Movable> > d6;
NeedyTemplate1<Template_Ref<NonMovable> > d7;
NeedyTemplate1<Template_Ref<S_NonMovable> > d8;
NeedyTemplate1<Template_Ref<W_NonMovable> > d9;
NeedyTemplate1<Template_Ref<WS_NonMovable> > d10;
NeedyTemplate1<Template_Ref<SW_NonMovable> > d11;
NeedyTemplate1<Template_Ref<SWS_NonMovable> > d12;
}
//
// 2 - Subclassed MOZ_NEEDS_MEMMOVABLE_TYPE
//
template <class T>
struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate2 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate2<{{.*}}>' with non-memmovable template argument '{{.*}}'}}
template <class T>
struct S_NeedyTemplate2 : NeedyTemplate2<T> {}; // expected-note-re 26 {{instantiation of 'NeedyTemplate2<{{.*}}>' requested here}}
void bad2() {
S_NeedyTemplate2<NonMovable> a1;
S_NeedyTemplate2<S_NonMovable> a2;
S_NeedyTemplate2<W_NonMovable> a3;
S_NeedyTemplate2<WS_NonMovable> a4;
S_NeedyTemplate2<SW_NonMovable> a5;
S_NeedyTemplate2<SWS_NonMovable> a6;
S_NeedyTemplate2<Template_Inline<NonMovable> > b1;
S_NeedyTemplate2<Template_Inline<S_NonMovable> > b2;
S_NeedyTemplate2<Template_Inline<W_NonMovable> > b3;
S_NeedyTemplate2<Template_Inline<WS_NonMovable> > b4;
S_NeedyTemplate2<Template_Inline<SW_NonMovable> > b5;
S_NeedyTemplate2<Template_Inline<SWS_NonMovable> > b6;
S_NeedyTemplate2<Template_NonMovable<NonMovable> > c1;
S_NeedyTemplate2<Template_NonMovable<S_NonMovable> > c2;
S_NeedyTemplate2<Template_NonMovable<W_NonMovable> > c3;
S_NeedyTemplate2<Template_NonMovable<WS_NonMovable> > c4;
S_NeedyTemplate2<Template_NonMovable<SW_NonMovable> > c5;
S_NeedyTemplate2<Template_NonMovable<SWS_NonMovable> > c6;
S_NeedyTemplate2<Template_NonMovable<Movable> > c7;
S_NeedyTemplate2<Template_NonMovable<S_Movable> > c8;
S_NeedyTemplate2<Template_NonMovable<W_Movable> > c9;
S_NeedyTemplate2<Template_NonMovable<WS_Movable> > c10;
S_NeedyTemplate2<Template_NonMovable<SW_Movable> > c11;
S_NeedyTemplate2<Template_NonMovable<SWS_Movable> > c12;
}
void good2() {
S_NeedyTemplate2<Movable> a1;
S_NeedyTemplate2<S_Movable> a2;
S_NeedyTemplate2<W_Movable> a3;
S_NeedyTemplate2<WS_Movable> a4;
S_NeedyTemplate2<SW_Movable> a5;
S_NeedyTemplate2<SWS_Movable> a6;
S_NeedyTemplate2<Template_Inline<Movable> > b1;
S_NeedyTemplate2<Template_Inline<S_Movable> > b2;
S_NeedyTemplate2<Template_Inline<W_Movable> > b3;
S_NeedyTemplate2<Template_Inline<WS_Movable> > b4;
S_NeedyTemplate2<Template_Inline<SW_Movable> > b5;
S_NeedyTemplate2<Template_Inline<SWS_Movable> > b6;
S_NeedyTemplate2<Template_Unused<Movable> > c1;
S_NeedyTemplate2<Template_Unused<S_Movable> > c2;
S_NeedyTemplate2<Template_Unused<W_Movable> > c3;
S_NeedyTemplate2<Template_Unused<WS_Movable> > c4;
S_NeedyTemplate2<Template_Unused<SW_Movable> > c5;
S_NeedyTemplate2<Template_Unused<SWS_Movable> > c6;
S_NeedyTemplate2<Template_Unused<NonMovable> > c7;
S_NeedyTemplate2<Template_Unused<S_NonMovable> > c8;
S_NeedyTemplate2<Template_Unused<W_NonMovable> > c9;
S_NeedyTemplate2<Template_Unused<WS_NonMovable> > c10;
S_NeedyTemplate2<Template_Unused<SW_NonMovable> > c11;
S_NeedyTemplate2<Template_Unused<SWS_NonMovable> > c12;
S_NeedyTemplate2<Template_Ref<Movable> > d1;
S_NeedyTemplate2<Template_Ref<S_Movable> > d2;
S_NeedyTemplate2<Template_Ref<W_Movable> > d3;
S_NeedyTemplate2<Template_Ref<WS_Movable> > d4;
S_NeedyTemplate2<Template_Ref<SW_Movable> > d5;
S_NeedyTemplate2<Template_Ref<SWS_Movable> > d6;
S_NeedyTemplate2<Template_Ref<NonMovable> > d7;
S_NeedyTemplate2<Template_Ref<S_NonMovable> > d8;
S_NeedyTemplate2<Template_Ref<W_NonMovable> > d9;
S_NeedyTemplate2<Template_Ref<WS_NonMovable> > d10;
S_NeedyTemplate2<Template_Ref<SW_NonMovable> > d11;
S_NeedyTemplate2<Template_Ref<SWS_NonMovable> > d12;
}
//
// 3 - Wrapped MOZ_NEEDS_MEMMOVABLE_TYPE
//
template <class T>
struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate3 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate3<{{.*}}>' with non-memmovable template argument '{{.*}}'}}
template <class T>
struct W_NeedyTemplate3 {
NeedyTemplate3<T> m; // expected-note-re 26 {{instantiation of 'NeedyTemplate3<{{.*}}>' requested here}}
};
void bad3() {
W_NeedyTemplate3<NonMovable> a1;
W_NeedyTemplate3<S_NonMovable> a2;
W_NeedyTemplate3<W_NonMovable> a3;
W_NeedyTemplate3<WS_NonMovable> a4;
W_NeedyTemplate3<SW_NonMovable> a5;
W_NeedyTemplate3<SWS_NonMovable> a6;
W_NeedyTemplate3<Template_Inline<NonMovable> > b1;
W_NeedyTemplate3<Template_Inline<S_NonMovable> > b2;
W_NeedyTemplate3<Template_Inline<W_NonMovable> > b3;
W_NeedyTemplate3<Template_Inline<WS_NonMovable> > b4;
W_NeedyTemplate3<Template_Inline<SW_NonMovable> > b5;
W_NeedyTemplate3<Template_Inline<SWS_NonMovable> > b6;
W_NeedyTemplate3<Template_NonMovable<NonMovable> > c1;
W_NeedyTemplate3<Template_NonMovable<S_NonMovable> > c2;
W_NeedyTemplate3<Template_NonMovable<W_NonMovable> > c3;
W_NeedyTemplate3<Template_NonMovable<WS_NonMovable> > c4;
W_NeedyTemplate3<Template_NonMovable<SW_NonMovable> > c5;
W_NeedyTemplate3<Template_NonMovable<SWS_NonMovable> > c6;
W_NeedyTemplate3<Template_NonMovable<Movable> > c7;
W_NeedyTemplate3<Template_NonMovable<S_Movable> > c8;
W_NeedyTemplate3<Template_NonMovable<W_Movable> > c9;
W_NeedyTemplate3<Template_NonMovable<WS_Movable> > c10;
W_NeedyTemplate3<Template_NonMovable<SW_Movable> > c11;
W_NeedyTemplate3<Template_NonMovable<SWS_Movable> > c12;
}
void good3() {
W_NeedyTemplate3<Movable> a1;
W_NeedyTemplate3<S_Movable> a2;
W_NeedyTemplate3<W_Movable> a3;
W_NeedyTemplate3<WS_Movable> a4;
W_NeedyTemplate3<SW_Movable> a5;
W_NeedyTemplate3<SWS_Movable> a6;
W_NeedyTemplate3<Template_Inline<Movable> > b1;
W_NeedyTemplate3<Template_Inline<S_Movable> > b2;
W_NeedyTemplate3<Template_Inline<W_Movable> > b3;
W_NeedyTemplate3<Template_Inline<WS_Movable> > b4;
W_NeedyTemplate3<Template_Inline<SW_Movable> > b5;
W_NeedyTemplate3<Template_Inline<SWS_Movable> > b6;
W_NeedyTemplate3<Template_Unused<Movable> > c1;
W_NeedyTemplate3<Template_Unused<S_Movable> > c2;
W_NeedyTemplate3<Template_Unused<W_Movable> > c3;
W_NeedyTemplate3<Template_Unused<WS_Movable> > c4;
W_NeedyTemplate3<Template_Unused<SW_Movable> > c5;
W_NeedyTemplate3<Template_Unused<SWS_Movable> > c6;
W_NeedyTemplate3<Template_Unused<NonMovable> > c7;
W_NeedyTemplate3<Template_Unused<S_NonMovable> > c8;
W_NeedyTemplate3<Template_Unused<W_NonMovable> > c9;
W_NeedyTemplate3<Template_Unused<WS_NonMovable> > c10;
W_NeedyTemplate3<Template_Unused<SW_NonMovable> > c11;
W_NeedyTemplate3<Template_Unused<SWS_NonMovable> > c12;
W_NeedyTemplate3<Template_Ref<Movable> > d1;
W_NeedyTemplate3<Template_Ref<S_Movable> > d2;
W_NeedyTemplate3<Template_Ref<W_Movable> > d3;
W_NeedyTemplate3<Template_Ref<WS_Movable> > d4;
W_NeedyTemplate3<Template_Ref<SW_Movable> > d5;
W_NeedyTemplate3<Template_Ref<SWS_Movable> > d6;
W_NeedyTemplate3<Template_Ref<NonMovable> > d7;
W_NeedyTemplate3<Template_Ref<S_NonMovable> > d8;
W_NeedyTemplate3<Template_Ref<W_NonMovable> > d9;
W_NeedyTemplate3<Template_Ref<WS_NonMovable> > d10;
W_NeedyTemplate3<Template_Ref<SW_NonMovable> > d11;
W_NeedyTemplate3<Template_Ref<SWS_NonMovable> > d12;
}
//
// 4 - Wrapped Subclassed MOZ_NEEDS_MEMMOVABLE_TYPE
//
template <class T>
struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate4 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate4<{{.*}}>' with non-memmovable template argument '{{.*}}'}}
template <class T>
struct S_NeedyTemplate4 : NeedyTemplate4<T> {}; // expected-note-re 26 {{instantiation of 'NeedyTemplate4<{{.*}}>' requested here}}
template <class T>
struct WS_NeedyTemplate4 {
S_NeedyTemplate4<T> m;
};
void bad4() {
WS_NeedyTemplate4<NonMovable> a1;
WS_NeedyTemplate4<S_NonMovable> a2;
WS_NeedyTemplate4<W_NonMovable> a3;
WS_NeedyTemplate4<WS_NonMovable> a4;
WS_NeedyTemplate4<SW_NonMovable> a5;
WS_NeedyTemplate4<SWS_NonMovable> a6;
WS_NeedyTemplate4<Template_Inline<NonMovable> > b1;
WS_NeedyTemplate4<Template_Inline<S_NonMovable> > b2;
WS_NeedyTemplate4<Template_Inline<W_NonMovable> > b3;
WS_NeedyTemplate4<Template_Inline<WS_NonMovable> > b4;
WS_NeedyTemplate4<Template_Inline<SW_NonMovable> > b5;
WS_NeedyTemplate4<Template_Inline<SWS_NonMovable> > b6;
WS_NeedyTemplate4<Template_NonMovable<NonMovable> > c1;
WS_NeedyTemplate4<Template_NonMovable<S_NonMovable> > c2;
WS_NeedyTemplate4<Template_NonMovable<W_NonMovable> > c3;
WS_NeedyTemplate4<Template_NonMovable<WS_NonMovable> > c4;
WS_NeedyTemplate4<Template_NonMovable<SW_NonMovable> > c5;
WS_NeedyTemplate4<Template_NonMovable<SWS_NonMovable> > c6;
WS_NeedyTemplate4<Template_NonMovable<Movable> > c7;
WS_NeedyTemplate4<Template_NonMovable<S_Movable> > c8;
WS_NeedyTemplate4<Template_NonMovable<W_Movable> > c9;
WS_NeedyTemplate4<Template_NonMovable<WS_Movable> > c10;
WS_NeedyTemplate4<Template_NonMovable<SW_Movable> > c11;
WS_NeedyTemplate4<Template_NonMovable<SWS_Movable> > c12;
}
void good4() {
WS_NeedyTemplate4<Movable> a1;
WS_NeedyTemplate4<S_Movable> a2;
WS_NeedyTemplate4<W_Movable> a3;
WS_NeedyTemplate4<WS_Movable> a4;
WS_NeedyTemplate4<SW_Movable> a5;
WS_NeedyTemplate4<SWS_Movable> a6;
WS_NeedyTemplate4<Template_Inline<Movable> > b1;
WS_NeedyTemplate4<Template_Inline<S_Movable> > b2;
WS_NeedyTemplate4<Template_Inline<W_Movable> > b3;
WS_NeedyTemplate4<Template_Inline<WS_Movable> > b4;
WS_NeedyTemplate4<Template_Inline<SW_Movable> > b5;
WS_NeedyTemplate4<Template_Inline<SWS_Movable> > b6;
WS_NeedyTemplate4<Template_Unused<Movable> > c1;
WS_NeedyTemplate4<Template_Unused<S_Movable> > c2;
WS_NeedyTemplate4<Template_Unused<W_Movable> > c3;
WS_NeedyTemplate4<Template_Unused<WS_Movable> > c4;
WS_NeedyTemplate4<Template_Unused<SW_Movable> > c5;
WS_NeedyTemplate4<Template_Unused<SWS_Movable> > c6;
WS_NeedyTemplate4<Template_Unused<NonMovable> > c7;
WS_NeedyTemplate4<Template_Unused<S_NonMovable> > c8;
WS_NeedyTemplate4<Template_Unused<W_NonMovable> > c9;
WS_NeedyTemplate4<Template_Unused<WS_NonMovable> > c10;
WS_NeedyTemplate4<Template_Unused<SW_NonMovable> > c11;
WS_NeedyTemplate4<Template_Unused<SWS_NonMovable> > c12;
WS_NeedyTemplate4<Template_Ref<Movable> > d1;
WS_NeedyTemplate4<Template_Ref<S_Movable> > d2;
WS_NeedyTemplate4<Template_Ref<W_Movable> > d3;
WS_NeedyTemplate4<Template_Ref<WS_Movable> > d4;
WS_NeedyTemplate4<Template_Ref<SW_Movable> > d5;
WS_NeedyTemplate4<Template_Ref<SWS_Movable> > d6;
WS_NeedyTemplate4<Template_Ref<NonMovable> > d7;
WS_NeedyTemplate4<Template_Ref<S_NonMovable> > d8;
WS_NeedyTemplate4<Template_Ref<W_NonMovable> > d9;
WS_NeedyTemplate4<Template_Ref<WS_NonMovable> > d10;
WS_NeedyTemplate4<Template_Ref<SW_NonMovable> > d11;
WS_NeedyTemplate4<Template_Ref<SWS_NonMovable> > d12;
}
//
// 5 - Subclassed Wrapped MOZ_NEEDS_MEMMOVABLE_TYPE
//
template <class T>
struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate5 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate5<{{.*}}>' with non-memmovable template argument '{{.*}}'}}
template <class T>
struct W_NeedyTemplate5 {
NeedyTemplate5<T> m; // expected-note-re 26 {{instantiation of 'NeedyTemplate5<{{.*}}>' requested here}}
};
template <class T>
struct SW_NeedyTemplate5 : W_NeedyTemplate5<T> {};
void bad5() {
SW_NeedyTemplate5<NonMovable> a1;
SW_NeedyTemplate5<S_NonMovable> a2;
SW_NeedyTemplate5<W_NonMovable> a3;
SW_NeedyTemplate5<WS_NonMovable> a4;
SW_NeedyTemplate5<SW_NonMovable> a5;
SW_NeedyTemplate5<SWS_NonMovable> a6;
SW_NeedyTemplate5<Template_Inline<NonMovable> > b1;
SW_NeedyTemplate5<Template_Inline<S_NonMovable> > b2;
SW_NeedyTemplate5<Template_Inline<W_NonMovable> > b3;
SW_NeedyTemplate5<Template_Inline<WS_NonMovable> > b4;
SW_NeedyTemplate5<Template_Inline<SW_NonMovable> > b5;
SW_NeedyTemplate5<Template_Inline<SWS_NonMovable> > b6;
SW_NeedyTemplate5<Template_NonMovable<NonMovable> > c1;
SW_NeedyTemplate5<Template_NonMovable<S_NonMovable> > c2;
SW_NeedyTemplate5<Template_NonMovable<W_NonMovable> > c3;
SW_NeedyTemplate5<Template_NonMovable<WS_NonMovable> > c4;
SW_NeedyTemplate5<Template_NonMovable<SW_NonMovable> > c5;
SW_NeedyTemplate5<Template_NonMovable<SWS_NonMovable> > c6;
SW_NeedyTemplate5<Template_NonMovable<Movable> > c7;
SW_NeedyTemplate5<Template_NonMovable<S_Movable> > c8;
SW_NeedyTemplate5<Template_NonMovable<W_Movable> > c9;
SW_NeedyTemplate5<Template_NonMovable<WS_Movable> > c10;
SW_NeedyTemplate5<Template_NonMovable<SW_Movable> > c11;
SW_NeedyTemplate5<Template_NonMovable<SWS_Movable> > c12;
}
void good5() {
SW_NeedyTemplate5<Movable> a1;
SW_NeedyTemplate5<S_Movable> a2;
SW_NeedyTemplate5<W_Movable> a3;
SW_NeedyTemplate5<WS_Movable> a4;
SW_NeedyTemplate5<SW_Movable> a5;
SW_NeedyTemplate5<SWS_Movable> a6;
SW_NeedyTemplate5<Template_Inline<Movable> > b1;
SW_NeedyTemplate5<Template_Inline<S_Movable> > b2;
SW_NeedyTemplate5<Template_Inline<W_Movable> > b3;
SW_NeedyTemplate5<Template_Inline<WS_Movable> > b4;
SW_NeedyTemplate5<Template_Inline<SW_Movable> > b5;
SW_NeedyTemplate5<Template_Inline<SWS_Movable> > b6;
SW_NeedyTemplate5<Template_Unused<Movable> > c1;
SW_NeedyTemplate5<Template_Unused<S_Movable> > c2;
SW_NeedyTemplate5<Template_Unused<W_Movable> > c3;
SW_NeedyTemplate5<Template_Unused<WS_Movable> > c4;
SW_NeedyTemplate5<Template_Unused<SW_Movable> > c5;
SW_NeedyTemplate5<Template_Unused<SWS_Movable> > c6;
SW_NeedyTemplate5<Template_Unused<NonMovable> > c7;
SW_NeedyTemplate5<Template_Unused<S_NonMovable> > c8;
SW_NeedyTemplate5<Template_Unused<W_NonMovable> > c9;
SW_NeedyTemplate5<Template_Unused<WS_NonMovable> > c10;
SW_NeedyTemplate5<Template_Unused<SW_NonMovable> > c11;
SW_NeedyTemplate5<Template_Unused<SWS_NonMovable> > c12;
SW_NeedyTemplate5<Template_Ref<Movable> > d1;
SW_NeedyTemplate5<Template_Ref<S_Movable> > d2;
SW_NeedyTemplate5<Template_Ref<W_Movable> > d3;
SW_NeedyTemplate5<Template_Ref<WS_Movable> > d4;
SW_NeedyTemplate5<Template_Ref<SW_Movable> > d5;
SW_NeedyTemplate5<Template_Ref<SWS_Movable> > d6;
SW_NeedyTemplate5<Template_Ref<NonMovable> > d7;
SW_NeedyTemplate5<Template_Ref<S_NonMovable> > d8;
SW_NeedyTemplate5<Template_Ref<W_NonMovable> > d9;
SW_NeedyTemplate5<Template_Ref<WS_NonMovable> > d10;
SW_NeedyTemplate5<Template_Ref<SW_NonMovable> > d11;
SW_NeedyTemplate5<Template_Ref<SWS_NonMovable> > d12;
}
//
// 6 - MOZ_NEEDS_MEMMOVABLE_TYPE instantiated with default template argument
//
// Note: This has an extra error, because it also includes a test with the default template argument.
//
template <class T>
struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate6 {T m;}; // expected-error-re 27 {{Cannot instantiate 'NeedyTemplate6<{{.*}}>' with non-memmovable template argument '{{.*}}'}}
template <class T>
struct W_NeedyTemplate6 {
NeedyTemplate6<T> m; // expected-note-re 27 {{instantiation of 'NeedyTemplate6<{{.*}}>' requested here}}
};
template <class T>
struct SW_NeedyTemplate6 : W_NeedyTemplate6<T> {};
// We create a different NonMovable type here, as NeedyTemplate6 will already be instantiated with NonMovable
struct MOZ_NON_MEMMOVABLE NonMovable2 {}; // expected-note {{'NonMovable2' is non-memmovable because of the MOZ_NON_MEMMOVABLE annotation on 'NonMovable2'}}
template <class T = NonMovable2>
struct Defaulted_SW_NeedyTemplate6 {
SW_NeedyTemplate6<T> m;
};
void bad6() {
Defaulted_SW_NeedyTemplate6<NonMovable> a1;
Defaulted_SW_NeedyTemplate6<S_NonMovable> a2;
Defaulted_SW_NeedyTemplate6<W_NonMovable> a3;
Defaulted_SW_NeedyTemplate6<WS_NonMovable> a4;
Defaulted_SW_NeedyTemplate6<SW_NonMovable> a5;
Defaulted_SW_NeedyTemplate6<SWS_NonMovable> a6;
Defaulted_SW_NeedyTemplate6<Template_Inline<NonMovable> > b1;
Defaulted_SW_NeedyTemplate6<Template_Inline<S_NonMovable> > b2;
Defaulted_SW_NeedyTemplate6<Template_Inline<W_NonMovable> > b3;
Defaulted_SW_NeedyTemplate6<Template_Inline<WS_NonMovable> > b4;
Defaulted_SW_NeedyTemplate6<Template_Inline<SW_NonMovable> > b5;
Defaulted_SW_NeedyTemplate6<Template_Inline<SWS_NonMovable> > b6;
Defaulted_SW_NeedyTemplate6<Template_NonMovable<NonMovable> > c1;
Defaulted_SW_NeedyTemplate6<Template_NonMovable<S_NonMovable> > c2;
Defaulted_SW_NeedyTemplate6<Template_NonMovable<W_NonMovable> > c3;
Defaulted_SW_NeedyTemplate6<Template_NonMovable<WS_NonMovable> > c4;
Defaulted_SW_NeedyTemplate6<Template_NonMovable<SW_NonMovable> > c5;
Defaulted_SW_NeedyTemplate6<Template_NonMovable<SWS_NonMovable> > c6;
Defaulted_SW_NeedyTemplate6<Template_NonMovable<Movable> > c7;
Defaulted_SW_NeedyTemplate6<Template_NonMovable<S_Movable> > c8;
Defaulted_SW_NeedyTemplate6<Template_NonMovable<W_Movable> > c9;
Defaulted_SW_NeedyTemplate6<Template_NonMovable<WS_Movable> > c10;
Defaulted_SW_NeedyTemplate6<Template_NonMovable<SW_Movable> > c11;
Defaulted_SW_NeedyTemplate6<Template_NonMovable<SWS_Movable> > c12;
Defaulted_SW_NeedyTemplate6<> c13;
}
void good6() {
Defaulted_SW_NeedyTemplate6<Movable> a1;
Defaulted_SW_NeedyTemplate6<S_Movable> a2;
Defaulted_SW_NeedyTemplate6<W_Movable> a3;
Defaulted_SW_NeedyTemplate6<WS_Movable> a4;
Defaulted_SW_NeedyTemplate6<SW_Movable> a5;
Defaulted_SW_NeedyTemplate6<SWS_Movable> a6;
Defaulted_SW_NeedyTemplate6<Template_Inline<Movable> > b1;
Defaulted_SW_NeedyTemplate6<Template_Inline<S_Movable> > b2;
Defaulted_SW_NeedyTemplate6<Template_Inline<W_Movable> > b3;
Defaulted_SW_NeedyTemplate6<Template_Inline<WS_Movable> > b4;
Defaulted_SW_NeedyTemplate6<Template_Inline<SW_Movable> > b5;
Defaulted_SW_NeedyTemplate6<Template_Inline<SWS_Movable> > b6;
Defaulted_SW_NeedyTemplate6<Template_Unused<Movable> > c1;
Defaulted_SW_NeedyTemplate6<Template_Unused<S_Movable> > c2;
Defaulted_SW_NeedyTemplate6<Template_Unused<W_Movable> > c3;
Defaulted_SW_NeedyTemplate6<Template_Unused<WS_Movable> > c4;
Defaulted_SW_NeedyTemplate6<Template_Unused<SW_Movable> > c5;
Defaulted_SW_NeedyTemplate6<Template_Unused<SWS_Movable> > c6;
Defaulted_SW_NeedyTemplate6<Template_Unused<NonMovable> > c7;
Defaulted_SW_NeedyTemplate6<Template_Unused<S_NonMovable> > c8;
Defaulted_SW_NeedyTemplate6<Template_Unused<W_NonMovable> > c9;
Defaulted_SW_NeedyTemplate6<Template_Unused<WS_NonMovable> > c10;
Defaulted_SW_NeedyTemplate6<Template_Unused<SW_NonMovable> > c11;
Defaulted_SW_NeedyTemplate6<Template_Unused<SWS_NonMovable> > c12;
Defaulted_SW_NeedyTemplate6<Template_Ref<Movable> > d1;
Defaulted_SW_NeedyTemplate6<Template_Ref<S_Movable> > d2;
Defaulted_SW_NeedyTemplate6<Template_Ref<W_Movable> > d3;
Defaulted_SW_NeedyTemplate6<Template_Ref<WS_Movable> > d4;
Defaulted_SW_NeedyTemplate6<Template_Ref<SW_Movable> > d5;
Defaulted_SW_NeedyTemplate6<Template_Ref<SWS_Movable> > d6;
Defaulted_SW_NeedyTemplate6<Template_Ref<NonMovable> > d7;
Defaulted_SW_NeedyTemplate6<Template_Ref<S_NonMovable> > d8;
Defaulted_SW_NeedyTemplate6<Template_Ref<W_NonMovable> > d9;
Defaulted_SW_NeedyTemplate6<Template_Ref<WS_NonMovable> > d10;
Defaulted_SW_NeedyTemplate6<Template_Ref<SW_NonMovable> > d11;
Defaulted_SW_NeedyTemplate6<Template_Ref<SWS_NonMovable> > d12;
}
//
// 7 - MOZ_NEEDS_MEMMOVABLE_TYPE instantiated as default template argument
//
template <class T>
struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate7 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate7<{{.*}}>' with non-memmovable template argument '{{.*}}'}}
template <class T, class Q = NeedyTemplate7<T> >
struct Defaulted_Templated_NeedyTemplate7 {Q m;}; // expected-note-re 26 {{instantiation of 'NeedyTemplate7<{{.*}}>' requested here}}
void bad7() {
Defaulted_Templated_NeedyTemplate7<NonMovable> a1;
Defaulted_Templated_NeedyTemplate7<S_NonMovable> a2;
Defaulted_Templated_NeedyTemplate7<W_NonMovable> a3;
Defaulted_Templated_NeedyTemplate7<WS_NonMovable> a4;
Defaulted_Templated_NeedyTemplate7<SW_NonMovable> a5;
Defaulted_Templated_NeedyTemplate7<SWS_NonMovable> a6;
Defaulted_Templated_NeedyTemplate7<Template_Inline<NonMovable> > b1;
Defaulted_Templated_NeedyTemplate7<Template_Inline<S_NonMovable> > b2;
Defaulted_Templated_NeedyTemplate7<Template_Inline<W_NonMovable> > b3;
Defaulted_Templated_NeedyTemplate7<Template_Inline<WS_NonMovable> > b4;
Defaulted_Templated_NeedyTemplate7<Template_Inline<SW_NonMovable> > b5;
Defaulted_Templated_NeedyTemplate7<Template_Inline<SWS_NonMovable> > b6;
Defaulted_Templated_NeedyTemplate7<Template_NonMovable<NonMovable> > c1;
Defaulted_Templated_NeedyTemplate7<Template_NonMovable<S_NonMovable> > c2;
Defaulted_Templated_NeedyTemplate7<Template_NonMovable<W_NonMovable> > c3;
Defaulted_Templated_NeedyTemplate7<Template_NonMovable<WS_NonMovable> > c4;
Defaulted_Templated_NeedyTemplate7<Template_NonMovable<SW_NonMovable> > c5;
Defaulted_Templated_NeedyTemplate7<Template_NonMovable<SWS_NonMovable> > c6;
Defaulted_Templated_NeedyTemplate7<Template_NonMovable<Movable> > c7;
Defaulted_Templated_NeedyTemplate7<Template_NonMovable<S_Movable> > c8;
Defaulted_Templated_NeedyTemplate7<Template_NonMovable<W_Movable> > c9;
Defaulted_Templated_NeedyTemplate7<Template_NonMovable<WS_Movable> > c10;
Defaulted_Templated_NeedyTemplate7<Template_NonMovable<SW_Movable> > c11;
Defaulted_Templated_NeedyTemplate7<Template_NonMovable<SWS_Movable> > c12;
}
void good7() {
Defaulted_Templated_NeedyTemplate7<Movable> a1;
Defaulted_Templated_NeedyTemplate7<S_Movable> a2;
Defaulted_Templated_NeedyTemplate7<W_Movable> a3;
Defaulted_Templated_NeedyTemplate7<WS_Movable> a4;
Defaulted_Templated_NeedyTemplate7<SW_Movable> a5;
Defaulted_Templated_NeedyTemplate7<SWS_Movable> a6;
Defaulted_Templated_NeedyTemplate7<Template_Inline<Movable> > b1;
Defaulted_Templated_NeedyTemplate7<Template_Inline<S_Movable> > b2;
Defaulted_Templated_NeedyTemplate7<Template_Inline<W_Movable> > b3;
Defaulted_Templated_NeedyTemplate7<Template_Inline<WS_Movable> > b4;
Defaulted_Templated_NeedyTemplate7<Template_Inline<SW_Movable> > b5;
Defaulted_Templated_NeedyTemplate7<Template_Inline<SWS_Movable> > b6;
Defaulted_Templated_NeedyTemplate7<Template_Unused<Movable> > c1;
Defaulted_Templated_NeedyTemplate7<Template_Unused<S_Movable> > c2;
Defaulted_Templated_NeedyTemplate7<Template_Unused<W_Movable> > c3;
Defaulted_Templated_NeedyTemplate7<Template_Unused<WS_Movable> > c4;
Defaulted_Templated_NeedyTemplate7<Template_Unused<SW_Movable> > c5;
Defaulted_Templated_NeedyTemplate7<Template_Unused<SWS_Movable> > c6;
Defaulted_Templated_NeedyTemplate7<Template_Unused<NonMovable> > c7;
Defaulted_Templated_NeedyTemplate7<Template_Unused<S_NonMovable> > c8;
Defaulted_Templated_NeedyTemplate7<Template_Unused<W_NonMovable> > c9;
Defaulted_Templated_NeedyTemplate7<Template_Unused<WS_NonMovable> > c10;
Defaulted_Templated_NeedyTemplate7<Template_Unused<SW_NonMovable> > c11;
Defaulted_Templated_NeedyTemplate7<Template_Unused<SWS_NonMovable> > c12;
Defaulted_Templated_NeedyTemplate7<Template_Ref<Movable> > d1;
Defaulted_Templated_NeedyTemplate7<Template_Ref<S_Movable> > d2;
Defaulted_Templated_NeedyTemplate7<Template_Ref<W_Movable> > d3;
Defaulted_Templated_NeedyTemplate7<Template_Ref<WS_Movable> > d4;
Defaulted_Templated_NeedyTemplate7<Template_Ref<SW_Movable> > d5;
Defaulted_Templated_NeedyTemplate7<Template_Ref<SWS_Movable> > d6;
Defaulted_Templated_NeedyTemplate7<Template_Ref<NonMovable> > d7;
Defaulted_Templated_NeedyTemplate7<Template_Ref<S_NonMovable> > d8;
Defaulted_Templated_NeedyTemplate7<Template_Ref<W_NonMovable> > d9;
Defaulted_Templated_NeedyTemplate7<Template_Ref<WS_NonMovable> > d10;
Defaulted_Templated_NeedyTemplate7<Template_Ref<SW_NonMovable> > d11;
Defaulted_Templated_NeedyTemplate7<Template_Ref<SWS_NonMovable> > d12;
}
//
// 8 - Wrapped MOZ_NEEDS_MEMMOVABLE_TYPE instantiated as default template argument
//
template <class T>
struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate8 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate8<{{.*}}>' with non-memmovable template argument '{{.*}}'}}
template <class T, class Q = NeedyTemplate8<T> >
struct Defaulted_Templated_NeedyTemplate8 {Q m;}; // expected-note-re 26 {{instantiation of 'NeedyTemplate8<{{.*}}>' requested here}}
template <class T>
struct W_Defaulted_Templated_NeedyTemplate8 {
Defaulted_Templated_NeedyTemplate8<T> m;
};
void bad8() {
W_Defaulted_Templated_NeedyTemplate8<NonMovable> a1;
W_Defaulted_Templated_NeedyTemplate8<S_NonMovable> a2;
W_Defaulted_Templated_NeedyTemplate8<W_NonMovable> a3;
W_Defaulted_Templated_NeedyTemplate8<WS_NonMovable> a4;
W_Defaulted_Templated_NeedyTemplate8<SW_NonMovable> a5;
W_Defaulted_Templated_NeedyTemplate8<SWS_NonMovable> a6;
W_Defaulted_Templated_NeedyTemplate8<Template_Inline<NonMovable> > b1;
W_Defaulted_Templated_NeedyTemplate8<Template_Inline<S_NonMovable> > b2;
W_Defaulted_Templated_NeedyTemplate8<Template_Inline<W_NonMovable> > b3;
W_Defaulted_Templated_NeedyTemplate8<Template_Inline<WS_NonMovable> > b4;
W_Defaulted_Templated_NeedyTemplate8<Template_Inline<SW_NonMovable> > b5;
W_Defaulted_Templated_NeedyTemplate8<Template_Inline<SWS_NonMovable> > b6;
W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<NonMovable> > c1;
W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<S_NonMovable> > c2;
W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<W_NonMovable> > c3;
W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<WS_NonMovable> > c4;
W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<SW_NonMovable> > c5;
W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<SWS_NonMovable> > c6;
W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<Movable> > c7;
W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<S_Movable> > c8;
W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<W_Movable> > c9;
W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<WS_Movable> > c10;
W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<SW_Movable> > c11;
W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<SWS_Movable> > c12;
}
void good8() {
W_Defaulted_Templated_NeedyTemplate8<Movable> a1;
W_Defaulted_Templated_NeedyTemplate8<S_Movable> a2;
W_Defaulted_Templated_NeedyTemplate8<W_Movable> a3;
W_Defaulted_Templated_NeedyTemplate8<WS_Movable> a4;
W_Defaulted_Templated_NeedyTemplate8<SW_Movable> a5;
W_Defaulted_Templated_NeedyTemplate8<SWS_Movable> a6;
W_Defaulted_Templated_NeedyTemplate8<Template_Inline<Movable> > b1;
W_Defaulted_Templated_NeedyTemplate8<Template_Inline<S_Movable> > b2;
W_Defaulted_Templated_NeedyTemplate8<Template_Inline<W_Movable> > b3;
W_Defaulted_Templated_NeedyTemplate8<Template_Inline<WS_Movable> > b4;
W_Defaulted_Templated_NeedyTemplate8<Template_Inline<SW_Movable> > b5;
W_Defaulted_Templated_NeedyTemplate8<Template_Inline<SWS_Movable> > b6;
W_Defaulted_Templated_NeedyTemplate8<Template_Unused<Movable> > c1;
W_Defaulted_Templated_NeedyTemplate8<Template_Unused<S_Movable> > c2;
W_Defaulted_Templated_NeedyTemplate8<Template_Unused<W_Movable> > c3;
W_Defaulted_Templated_NeedyTemplate8<Template_Unused<WS_Movable> > c4;
W_Defaulted_Templated_NeedyTemplate8<Template_Unused<SW_Movable> > c5;
W_Defaulted_Templated_NeedyTemplate8<Template_Unused<SWS_Movable> > c6;
W_Defaulted_Templated_NeedyTemplate8<Template_Unused<NonMovable> > c7;
W_Defaulted_Templated_NeedyTemplate8<Template_Unused<S_NonMovable> > c8;
W_Defaulted_Templated_NeedyTemplate8<Template_Unused<W_NonMovable> > c9;
W_Defaulted_Templated_NeedyTemplate8<Template_Unused<WS_NonMovable> > c10;
W_Defaulted_Templated_NeedyTemplate8<Template_Unused<SW_NonMovable> > c11;
W_Defaulted_Templated_NeedyTemplate8<Template_Unused<SWS_NonMovable> > c12;
W_Defaulted_Templated_NeedyTemplate8<Template_Ref<Movable> > d1;
W_Defaulted_Templated_NeedyTemplate8<Template_Ref<S_Movable> > d2;
W_Defaulted_Templated_NeedyTemplate8<Template_Ref<W_Movable> > d3;
W_Defaulted_Templated_NeedyTemplate8<Template_Ref<WS_Movable> > d4;
W_Defaulted_Templated_NeedyTemplate8<Template_Ref<SW_Movable> > d5;
W_Defaulted_Templated_NeedyTemplate8<Template_Ref<SWS_Movable> > d6;
W_Defaulted_Templated_NeedyTemplate8<Template_Ref<NonMovable> > d7;
W_Defaulted_Templated_NeedyTemplate8<Template_Ref<S_NonMovable> > d8;
W_Defaulted_Templated_NeedyTemplate8<Template_Ref<W_NonMovable> > d9;
W_Defaulted_Templated_NeedyTemplate8<Template_Ref<WS_NonMovable> > d10;
W_Defaulted_Templated_NeedyTemplate8<Template_Ref<SW_NonMovable> > d11;
W_Defaulted_Templated_NeedyTemplate8<Template_Ref<SWS_NonMovable> > d12;
}
/*
SpecializedNonMovable is a non-movable class which has an explicit specialization of NeedyTemplate
for it. Instantiations of NeedyTemplateN<SpecializedNonMovable> should be legal as the explicit
specialization isn't annotated with MOZ_NEEDS_MEMMOVABLE_TYPE.
However, as it is MOZ_NON_MEMMOVABLE, derived classes and members shouldn't be able to be used to
instantiate NeedyTemplate.
*/
struct MOZ_NON_MEMMOVABLE SpecializedNonMovable {}; // expected-note 8 {{'S_SpecializedNonMovable' is non-memmovable because of the MOZ_NON_MEMMOVABLE annotation on 'SpecializedNonMovable'}} expected-note 8 {{'Template_Inline<SpecializedNonMovable>' is non-memmovable because of the MOZ_NON_MEMMOVABLE annotation on 'SpecializedNonMovable'}}
struct S_SpecializedNonMovable : SpecializedNonMovable {};
// Specialize all of the NeedyTemplates with SpecializedNonMovable.
template <>
struct NeedyTemplate1<SpecializedNonMovable> {};
template <>
struct NeedyTemplate2<SpecializedNonMovable> {};
template <>
struct NeedyTemplate3<SpecializedNonMovable> {};
template <>
struct NeedyTemplate4<SpecializedNonMovable> {};
template <>
struct NeedyTemplate5<SpecializedNonMovable> {};
template <>
struct NeedyTemplate6<SpecializedNonMovable> {};
template <>
struct NeedyTemplate7<SpecializedNonMovable> {};
template <>
struct NeedyTemplate8<SpecializedNonMovable> {};
void specialization() {
/*
SpecializedNonMovable has a specialization for every variant of NeedyTemplate,
so these templates are valid, even though SpecializedNonMovable isn't
memmovable
*/
NeedyTemplate1<SpecializedNonMovable> a1;
S_NeedyTemplate2<SpecializedNonMovable> a2;
W_NeedyTemplate3<SpecializedNonMovable> a3;
WS_NeedyTemplate4<SpecializedNonMovable> a4;
SW_NeedyTemplate5<SpecializedNonMovable> a5;
Defaulted_SW_NeedyTemplate6<SpecializedNonMovable> a6;
Defaulted_Templated_NeedyTemplate7<SpecializedNonMovable> a7;
W_Defaulted_Templated_NeedyTemplate8<SpecializedNonMovable> a8;
/*
These entries contain an element which is SpecializedNonMovable, and are non-movable
as there is no valid specialization, and their member is non-memmovable
*/
NeedyTemplate1<Template_Inline<SpecializedNonMovable> > b1; // expected-note {{instantiation of 'NeedyTemplate1<Template_Inline<SpecializedNonMovable> >' requested here}}
S_NeedyTemplate2<Template_Inline<SpecializedNonMovable> > b2;
W_NeedyTemplate3<Template_Inline<SpecializedNonMovable> > b3;
WS_NeedyTemplate4<Template_Inline<SpecializedNonMovable> > b4;
SW_NeedyTemplate5<Template_Inline<SpecializedNonMovable> > b5;
Defaulted_SW_NeedyTemplate6<Template_Inline<SpecializedNonMovable> > b6;
Defaulted_Templated_NeedyTemplate7<Template_Inline<SpecializedNonMovable> > b7;
W_Defaulted_Templated_NeedyTemplate8<Template_Inline<SpecializedNonMovable> > b8;
/*
The subclass of SpecializedNonMovable, is also non-memmovable,
as there is no valid specialization.
*/
NeedyTemplate1<S_SpecializedNonMovable> c1; // expected-note {{instantiation of 'NeedyTemplate1<S_SpecializedNonMovable>' requested here}}
S_NeedyTemplate2<S_SpecializedNonMovable> c2;
W_NeedyTemplate3<S_SpecializedNonMovable> c3;
WS_NeedyTemplate4<S_SpecializedNonMovable> c4;
SW_NeedyTemplate5<S_SpecializedNonMovable> c5;
Defaulted_SW_NeedyTemplate6<S_SpecializedNonMovable> c6;
Defaulted_Templated_NeedyTemplate7<S_SpecializedNonMovable> c7;
W_Defaulted_Templated_NeedyTemplate8<S_SpecializedNonMovable> c8;
}

View File

@ -18,7 +18,7 @@ void misuseStackClass(int len) {
Stack valid;
Stack alsoValid[2];
static Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}}
static Stack alsoNotValid[2]; // expected-error {{variable of type 'Stack [2]' only valid on the stack}}
static Stack alsoNotValid[2]; // expected-error {{variable of type 'Stack [2]' only valid on the stack}} expected-note {{'Stack [2]' is a stack type because it is an array of stack type 'Stack'}}
gobble(&valid);
gobble(&notValid);
@ -35,7 +35,7 @@ void misuseStackClass(int len) {
Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}}
struct RandomClass {
Stack nonstaticMember; // expected-note {{'RandomClass' is a stack class because member 'nonstaticMember' is a stack class 'Stack'}}
Stack nonstaticMember; // expected-note {{'RandomClass' is a stack type because member 'nonstaticMember' is a stack type 'Stack'}}
static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
};
struct MOZ_STACK_CLASS RandomStackClass {
@ -43,7 +43,7 @@ struct MOZ_STACK_CLASS RandomStackClass {
static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
};
struct BadInherit : Stack {}; // expected-note {{'BadInherit' is a stack class because it inherits from a stack class 'Stack'}}
struct BadInherit : Stack {}; // expected-note {{'BadInherit' is a stack type because it inherits from a stack type 'Stack'}}
struct MOZ_STACK_CLASS GoodInherit : Stack {};
BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid on the stack}}

View File

@ -9,13 +9,16 @@ SOURCES += [
'TestCustomHeap.cpp',
'TestExplicitOperatorBool.cpp',
'TestGlobalClass.cpp',
'TestMultipleAnnotations.cpp',
'TestMustOverride.cpp',
'TestMustUse.cpp',
'TestNANTestingExpr.cpp',
'TestNANTestingExprC.c',
'TestNeedsNoVTableType.cpp',
'TestNoAddRefReleaseOnReturn.cpp',
'TestNoArithmeticExprInArgument.cpp',
'TestNonHeapClass.cpp',
'TestNonMemMovable.cpp',
'TestNoRefcountedInsideLambdas.cpp',
'TestStackClass.cpp',
'TestTrivialCtorDtor.cpp',

View File

@ -109,6 +109,7 @@ class MachCommands(MachCommandBase):
'--show-possibly-lost=no',
'--track-origins=yes',
'--trace-children=yes',
'-v', # Enable verbosity to get the list of used suppressions
]
for s in suppressions:

View File

@ -1710,20 +1710,22 @@ nsDocShell::LoadStream(nsIInputStream* aStream, nsIURI* aURI,
}
uint32_t loadType = LOAD_NORMAL;
nsCOMPtr<nsIPrincipal> requestingPrincipal;
if (aLoadInfo) {
nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
(void)aLoadInfo->GetLoadType(&lt);
// Get the appropriate LoadType from nsIDocShellLoadInfo type
loadType = ConvertDocShellLoadInfoToLoadType(lt);
nsCOMPtr<nsISupports> owner;
aLoadInfo->GetOwner(getter_AddRefs(owner));
requestingPrincipal = do_QueryInterface(owner);
}
NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE);
mLoadType = loadType;
nsCOMPtr<nsISupports> owner;
aLoadInfo->GetOwner(getter_AddRefs(owner));
nsCOMPtr<nsIPrincipal> requestingPrincipal = do_QueryInterface(owner);
if (!requestingPrincipal) {
requestingPrincipal = nsContentUtils::GetSystemPrincipal();
}
@ -2953,129 +2955,16 @@ nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue)
nsresult
nsDocShell::PopProfileTimelineMarkers(
JSContext* aCx,
JS::MutableHandle<JS::Value> aProfileTimelineMarkers)
JS::MutableHandle<JS::Value> aOut)
{
// Looping over all markers gathered so far at the docShell level, whenever a
// START marker is found, look for the corresponding END marker and build a
// {name,start,end} JS object.
// Paint markers are different because paint is handled at root docShell level
// in the information that a paint was done is then stored at each sub
// docShell level but we can only be sure that a paint did happen in a
// docShell if an Layer marker type was recorded too.
nsTArray<dom::ProfileTimelineMarker> store;
SequenceRooter<dom::ProfileTimelineMarker> rooter(aCx, &store);
nsTArray<mozilla::dom::ProfileTimelineMarker> profileTimelineMarkers;
SequenceRooter<mozilla::dom::ProfileTimelineMarker> rooter(
aCx, &profileTimelineMarkers);
if (!IsObserved()) {
if (!ToJSValue(aCx, profileTimelineMarkers, aProfileTimelineMarkers)) {
JS_ClearPendingException(aCx);
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
if (IsObserved()) {
mObserved->PopMarkers(aCx, store);
}
nsTArray<UniquePtr<TimelineMarker>>& markersStore = mObserved.get()->mTimelineMarkers;
// If we see an unpaired START, we keep it around for the next call
// to PopProfileTimelineMarkers. We store the kept START objects in
// this array.
nsTArray<UniquePtr<TimelineMarker>> keptMarkers;
for (uint32_t i = 0; i < markersStore.Length(); ++i) {
UniquePtr<TimelineMarker>& startPayload = markersStore[i];
const char* startMarkerName = startPayload->GetName();
bool hasSeenPaintedLayer = false;
bool isPaint = strcmp(startMarkerName, "Paint") == 0;
// If we are processing a Paint marker, we append information from
// all the embedded Layer markers to this array.
dom::Sequence<dom::ProfileTimelineLayerRect> layerRectangles;
// If this is a TRACING_TIMESTAMP marker, there's no corresponding "end"
// marker, as it's a single unit of time, not a duration, create the final
// marker here.
if (startPayload->GetMetaData() == TRACING_TIMESTAMP) {
mozilla::dom::ProfileTimelineMarker* marker =
profileTimelineMarkers.AppendElement();
marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
marker->mStart = startPayload->GetTime();
marker->mEnd = startPayload->GetTime();
marker->mStack = startPayload->GetStack();
startPayload->AddDetails(aCx, *marker);
continue;
}
if (startPayload->GetMetaData() == TRACING_INTERVAL_START) {
bool hasSeenEnd = false;
// DOM events can be nested, so we must take care when searching
// for the matching end. It doesn't hurt to apply this logic to
// all event types.
uint32_t markerDepth = 0;
// The assumption is that the devtools timeline flushes markers frequently
// enough for the amount of markers to always be small enough that the
// nested for loop isn't going to be a performance problem.
for (uint32_t j = i + 1; j < markersStore.Length(); ++j) {
UniquePtr<TimelineMarker>& endPayload = markersStore[j];
const char* endMarkerName = endPayload->GetName();
// Look for Layer markers to stream out paint markers.
if (isPaint && strcmp(endMarkerName, "Layer") == 0) {
hasSeenPaintedLayer = true;
endPayload->AddLayerRectangles(layerRectangles);
}
if (!startPayload->Equals(*endPayload)) {
continue;
}
// Pair start and end markers.
if (endPayload->GetMetaData() == TRACING_INTERVAL_START) {
++markerDepth;
} else if (endPayload->GetMetaData() == TRACING_INTERVAL_END) {
if (markerDepth > 0) {
--markerDepth;
} else {
// But ignore paint start/end if no layer has been painted.
if (!isPaint || (isPaint && hasSeenPaintedLayer)) {
mozilla::dom::ProfileTimelineMarker* marker =
profileTimelineMarkers.AppendElement();
marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
marker->mStart = startPayload->GetTime();
marker->mEnd = endPayload->GetTime();
marker->mStack = startPayload->GetStack();
if (isPaint) {
marker->mRectangles.Construct(layerRectangles);
}
startPayload->AddDetails(aCx, *marker);
endPayload->AddDetails(aCx, *marker);
}
// We want the start to be dropped either way.
hasSeenEnd = true;
break;
}
}
}
// If we did not see the corresponding END, keep the START.
if (!hasSeenEnd) {
keptMarkers.AppendElement(Move(markersStore[i]));
markersStore.RemoveElementAt(i);
--i;
}
}
}
markersStore.SwapElements(keptMarkers);
if (!ToJSValue(aCx, profileTimelineMarkers, aProfileTimelineMarkers)) {
if (!ToJSValue(aCx, store, aOut)) {
JS_ClearPendingException(aCx);
return NS_ERROR_UNEXPECTED;
}

View File

@ -34,4 +34,101 @@ ObservedDocShell::ClearMarkers()
mTimelineMarkers.Clear();
}
void
ObservedDocShell::PopMarkers(JSContext* aCx,
nsTArray<dom::ProfileTimelineMarker>& aStore)
{
// If we see an unpaired START, we keep it around for the next call
// to ObservedDocShell::PopMarkers. We store the kept START objects here.
nsTArray<UniquePtr<TimelineMarker>> keptStartMarkers;
for (uint32_t i = 0; i < mTimelineMarkers.Length(); ++i) {
UniquePtr<TimelineMarker>& startPayload = mTimelineMarkers[i];
// If this is a TRACING_TIMESTAMP marker, there's no corresponding END
// as it's a single unit of time, not a duration.
if (startPayload->GetMetaData() == TRACING_TIMESTAMP) {
dom::ProfileTimelineMarker* marker = aStore.AppendElement();
marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
marker->mStart = startPayload->GetTime();
marker->mEnd = startPayload->GetTime();
marker->mStack = startPayload->GetStack();
startPayload->AddDetails(aCx, *marker);
continue;
}
// Whenever a START marker is found, look for the corresponding END
// and build a {name,start,end} JS object.
if (startPayload->GetMetaData() == TRACING_INTERVAL_START) {
bool hasSeenEnd = false;
// "Paint" markers are different because painting is handled at root
// docshell level. The information that a paint was done is stored at
// sub-docshell level, but we can only be sure that a paint did actually
// happen in if a "Layer" marker was recorded too.
bool startIsPaintType = strcmp(startPayload->GetName(), "Paint") == 0;
bool hasSeenLayerType = false;
// If we are processing a "Paint" marker, we append information from
// all the embedded "Layer" markers to this array.
dom::Sequence<dom::ProfileTimelineLayerRect> layerRectangles;
// DOM events can be nested, so we must take care when searching
// for the matching end. It doesn't hurt to apply this logic to
// all event types.
uint32_t markerDepth = 0;
// The assumption is that the devtools timeline flushes markers frequently
// enough for the amount of markers to always be small enough that the
// nested for loop isn't going to be a performance problem.
for (uint32_t j = i + 1; j < mTimelineMarkers.Length(); ++j) {
UniquePtr<TimelineMarker>& endPayload = mTimelineMarkers[j];
bool endIsLayerType = strcmp(endPayload->GetName(), "Layer") == 0;
// Look for "Layer" markers to stream out "Paint" markers.
if (startIsPaintType && endIsLayerType) {
hasSeenLayerType = true;
endPayload->AddLayerRectangles(layerRectangles);
}
if (!startPayload->Equals(*endPayload)) {
continue;
}
if (endPayload->GetMetaData() == TRACING_INTERVAL_START) {
++markerDepth;
continue;
}
if (endPayload->GetMetaData() == TRACING_INTERVAL_END) {
if (markerDepth > 0) {
--markerDepth;
continue;
}
if (!startIsPaintType || (startIsPaintType && hasSeenLayerType)) {
dom::ProfileTimelineMarker* marker = aStore.AppendElement();
marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
marker->mStart = startPayload->GetTime();
marker->mEnd = endPayload->GetTime();
marker->mStack = startPayload->GetStack();
if (hasSeenLayerType) {
marker->mRectangles.Construct(layerRectangles);
}
startPayload->AddDetails(aCx, *marker);
endPayload->AddDetails(aCx, *marker);
}
hasSeenEnd = true;
break;
}
}
// If we did not see the corresponding END, keep the START.
if (!hasSeenEnd) {
keptStartMarkers.AppendElement(Move(mTimelineMarkers[i]));
mTimelineMarkers.RemoveElementAt(i);
--i;
}
}
}
mTimelineMarkers.SwapElements(keptStartMarkers);
}
} // namespace mozilla

View File

@ -15,6 +15,9 @@ class nsDocShell;
class TimelineMarker;
namespace mozilla {
namespace dom {
struct ProfileTimelineMarker;
}
// # ObservedDocShell
//
@ -24,18 +27,16 @@ class ObservedDocShell : public LinkedListElement<ObservedDocShell>
{
private:
nsRefPtr<nsDocShell> mDocShell;
public:
// FIXME: make this private once all marker-specific logic has been
// moved out of nsDocShell.
nsTArray<UniquePtr<TimelineMarker>> mTimelineMarkers;
public:
explicit ObservedDocShell(nsDocShell* aDocShell);
nsDocShell* operator*() const { return mDocShell.get(); }
void AddMarker(const char* aName, TracingMetadata aMetaData);
void AddMarker(UniquePtr<TimelineMarker>&& aMarker);
void ClearMarkers();
void PopMarkers(JSContext* aCx, nsTArray<dom::ProfileTimelineMarker>& aStore);
};
} // namespace mozilla

View File

@ -2728,8 +2728,52 @@ Navigator::RequestMediaKeySystemAccess(const nsAString& aKeySystem,
const Optional<Sequence<MediaKeySystemOptions>>& aOptions,
ErrorResult& aRv)
{
nsAutoCString logMsg;
logMsg.AppendPrintf("Navigator::RequestMediaKeySystemAccess(keySystem='%s' options=[",
NS_ConvertUTF16toUTF8(aKeySystem).get());
if (aOptions.WasPassed()) {
const Sequence<MediaKeySystemOptions>& options = aOptions.Value();
for (size_t i = 0; i < options.Length(); i++) {
const MediaKeySystemOptions& op = options[i];
if (i > 0) {
logMsg.AppendLiteral(",");
}
logMsg.AppendLiteral("{");
logMsg.AppendPrintf("stateful='%s'",
MediaKeysRequirementValues::strings[(size_t)op.mStateful].value);
logMsg.AppendPrintf(", uniqueIdentifier='%s'",
MediaKeysRequirementValues::strings[(size_t)op.mUniqueidentifier].value);
if (!op.mAudioCapability.IsEmpty()) {
logMsg.AppendPrintf(", audioCapability='%s'",
NS_ConvertUTF16toUTF8(op.mAudioCapability).get());
}
if (!op.mAudioType.IsEmpty()) {
logMsg.AppendPrintf(", audioType='%s'",
NS_ConvertUTF16toUTF8(op.mAudioType).get());
}
if (!op.mInitDataType.IsEmpty()) {
logMsg.AppendPrintf(", initDataType='%s'",
NS_ConvertUTF16toUTF8(op.mInitDataType).get());
}
if (!op.mVideoCapability.IsEmpty()) {
logMsg.AppendPrintf(", videoCapability='%s'",
NS_ConvertUTF16toUTF8(op.mVideoCapability).get());
}
if (!op.mVideoType.IsEmpty()) {
logMsg.AppendPrintf(", videoType='%s'",
NS_ConvertUTF16toUTF8(op.mVideoType).get());
}
logMsg.AppendLiteral("}");
}
}
logMsg.AppendPrintf("])");
EME_LOG(logMsg.get());
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
nsRefPtr<DetailedPromise> promise = DetailedPromise::Create(go, aRv);
nsRefPtr<DetailedPromise> promise = DetailedPromise::Create(go, aRv,
NS_LITERAL_CSTRING("navigator.requestMediaKeySystemAccess"));
if (aRv.Failed()) {
return nullptr;
}

View File

@ -9,11 +9,13 @@
#include "mozilla/dom/MimeTypeArrayBinding.h"
#include "mozilla/dom/MimeTypeBinding.h"
#include "nsIDOMNavigator.h"
#include "nsPIDOMWindow.h"
#include "nsPluginArray.h"
#include "nsIMIMEService.h"
#include "nsIMIMEInfo.h"
#include "Navigator.h"
#include "nsServiceManagerUtils.h"
#include "nsPluginTags.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -216,19 +218,22 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsMimeType, Release)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsMimeType, mWindow, mPluginElement)
nsMimeType::nsMimeType(nsPIDOMWindow* aWindow, nsPluginElement* aPluginElement,
uint32_t aPluginTagMimeIndex, const nsAString& aType)
nsMimeType::nsMimeType(nsPIDOMWindow* aWindow,
nsPluginElement* aPluginElement,
const nsAString& aType,
const nsAString& aDescription,
const nsAString& aExtension)
: mWindow(aWindow),
mPluginElement(aPluginElement),
mPluginTagMimeIndex(aPluginTagMimeIndex),
mType(aType)
mType(aType),
mDescription(aDescription),
mExtension(aExtension)
{
}
nsMimeType::nsMimeType(nsPIDOMWindow* aWindow, const nsAString& aType)
: mWindow(aWindow),
mPluginElement(nullptr),
mPluginTagMimeIndex(0),
mType(aType)
{
}
@ -251,14 +256,9 @@ nsMimeType::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
}
void
nsMimeType::GetDescription(nsString& retval) const
nsMimeType::GetDescription(nsString& aRetval) const
{
retval.Truncate();
if (mPluginElement) {
CopyUTF8toUTF16(mPluginElement->PluginTag()->
mMimeDescriptions[mPluginTagMimeIndex], retval);
}
aRetval = mDescription;
}
nsPluginElement*
@ -269,14 +269,9 @@ nsMimeType::GetEnabledPlugin() const
}
void
nsMimeType::GetSuffixes(nsString& retval) const
nsMimeType::GetSuffixes(nsString& aRetval) const
{
retval.Truncate();
if (mPluginElement) {
CopyUTF8toUTF16(mPluginElement->PluginTag()->
mExtensions[mPluginTagMimeIndex], retval);
}
aRetval = mExtension;
}
void

View File

@ -58,8 +58,11 @@ public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsMimeType)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(nsMimeType)
nsMimeType(nsPIDOMWindow* aWindow, nsPluginElement* aPluginElement,
uint32_t aPluginTagMimeIndex, const nsAString& aMimeType);
nsMimeType(nsPIDOMWindow* aWindow,
nsPluginElement* aPluginElement,
const nsAString& aType,
const nsAString& aDescription,
const nsAString& aExtension);
nsMimeType(nsPIDOMWindow* aWindow, const nsAString& aMimeType);
nsPIDOMWindow* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@ -85,8 +88,9 @@ protected:
// mimetype array. We rely on the cycle collector to break this
// cycle.
nsRefPtr<nsPluginElement> mPluginElement;
uint32_t mPluginTagMimeIndex;
nsString mType;
nsString mDescription;
nsString mExtension;
};
#endif /* nsMimeTypeArray_h___ */

View File

@ -130,7 +130,7 @@ nsPluginArray::Refresh(bool aReloadDocuments)
// that plugins did not change and was not reloaded
if (pluginHost->ReloadPlugins() ==
NS_ERROR_PLUGINS_PLUGINSNOTCHANGED) {
nsTArray<nsRefPtr<nsPluginTag> > newPluginTags;
nsTArray<nsCOMPtr<nsIInternalPluginTag> > newPluginTags;
pluginHost->GetPlugins(newPluginTags);
// Check if the number of plugins we know about are different from
@ -279,7 +279,7 @@ operator<(const nsRefPtr<nsPluginElement>& lhs,
const nsRefPtr<nsPluginElement>& rhs)
{
// Sort plugins alphabetically by name.
return lhs->PluginTag()->mName < rhs->PluginTag()->mName;
return lhs->PluginTag()->Name() < rhs->PluginTag()->Name();
}
void
@ -296,14 +296,13 @@ nsPluginArray::EnsurePlugins()
return;
}
nsTArray<nsRefPtr<nsPluginTag> > pluginTags;
nsTArray<nsCOMPtr<nsIInternalPluginTag> > pluginTags;
pluginHost->GetPlugins(pluginTags);
// need to wrap each of these with a nsPluginElement, which is
// scriptable.
for (uint32_t i = 0; i < pluginTags.Length(); ++i) {
nsPluginTag* pluginTag = pluginTags[i];
mPlugins.AppendElement(new nsPluginElement(mWindow, pluginTag));
mPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i]));
}
// Alphabetize the enumeration order of non-hidden plugins to reduce
@ -323,7 +322,7 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsPluginElement, mWindow, mMimeTypes)
nsPluginElement::nsPluginElement(nsPIDOMWindow* aWindow,
nsPluginTag* aPluginTag)
nsIInternalPluginTag* aPluginTag)
: mWindow(aWindow),
mPluginTag(aPluginTag)
{
@ -349,25 +348,25 @@ nsPluginElement::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
void
nsPluginElement::GetDescription(nsString& retval) const
{
CopyUTF8toUTF16(mPluginTag->mDescription, retval);
CopyUTF8toUTF16(mPluginTag->Description(), retval);
}
void
nsPluginElement::GetFilename(nsString& retval) const
{
CopyUTF8toUTF16(mPluginTag->mFileName, retval);
CopyUTF8toUTF16(mPluginTag->FileName(), retval);
}
void
nsPluginElement::GetVersion(nsString& retval) const
{
CopyUTF8toUTF16(mPluginTag->mVersion, retval);
CopyUTF8toUTF16(mPluginTag->Version(), retval);
}
void
nsPluginElement::GetName(nsString& retval) const
{
CopyUTF8toUTF16(mPluginTag->mName, retval);
CopyUTF8toUTF16(mPluginTag->Name(), retval);
}
nsMimeType*
@ -452,8 +451,18 @@ nsPluginElement::EnsurePluginMimeTypes()
return;
}
for (uint32_t i = 0; i < mPluginTag->mMimeTypes.Length(); ++i) {
NS_ConvertUTF8toUTF16 type(mPluginTag->mMimeTypes[i]);
mMimeTypes.AppendElement(new nsMimeType(mWindow, this, i, type));
if (mPluginTag->MimeTypes().Length() != mPluginTag->MimeDescriptions().Length() ||
mPluginTag->MimeTypes().Length() != mPluginTag->Extensions().Length()) {
MOZ_ASSERT(false, "mime type arrays expected to be the same length");
return;
}
for (uint32_t i = 0; i < mPluginTag->MimeTypes().Length(); ++i) {
NS_ConvertUTF8toUTF16 type(mPluginTag->MimeTypes()[i]);
NS_ConvertUTF8toUTF16 description(mPluginTag->MimeDescriptions()[i]);
NS_ConvertUTF8toUTF16 extension(mPluginTag->Extensions()[i]);
mMimeTypes.AppendElement(new nsMimeType(mWindow, this, type, description,
extension));
}
}

View File

@ -11,11 +11,11 @@
#include "nsWeakReference.h"
#include "nsIObserver.h"
#include "nsWrapperCache.h"
#include "nsPluginTags.h"
#include "nsPIDOMWindow.h"
class nsPluginElement;
class nsMimeType;
class nsIInternalPluginTag;
class nsPluginArray final : public nsIObserver,
public nsSupportsWeakReference,
@ -70,12 +70,12 @@ public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsPluginElement)
nsPluginElement(nsPIDOMWindow* aWindow, nsPluginTag* aPluginTag);
nsPluginElement(nsPIDOMWindow* aWindow, nsIInternalPluginTag* aPluginTag);
nsPIDOMWindow* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
nsPluginTag* PluginTag() const
nsIInternalPluginTag* PluginTag() const
{
return mPluginTag;
}
@ -102,7 +102,7 @@ protected:
void EnsurePluginMimeTypes();
nsCOMPtr<nsPIDOMWindow> mWindow;
nsRefPtr<nsPluginTag> mPluginTag;
nsCOMPtr<nsIInternalPluginTag> mPluginTag;
nsTArray<nsRefPtr<nsMimeType> > mMimeTypes;
};

View File

@ -960,6 +960,25 @@ DOMInterfaces = {
'workers': True
},
'PushManager': [{
'workers': False,
'headerFile': 'mozilla/dom/PushManager.h',
'nativeType': 'mozilla::dom::PushManager',
}, {
'workers': True,
'headerFile': 'mozilla/dom/PushManager.h',
'nativeType': 'mozilla::dom::WorkerPushManager',
}],
'PushSubscription': [{
'workers': False,
'headerFile': 'mozilla/dom/PushManager.h',
}, {
'workers': True,
'headerFile': 'mozilla/dom/PushManager.h',
'nativeType': 'mozilla::dom::WorkerPushSubscription',
}],
'Range': {
'nativeType': 'nsRange',
'binaryNames': {

View File

@ -12,7 +12,6 @@
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/Preferences.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
@ -299,50 +298,8 @@ private:
}
};
class PrefEnabledRunnable final : public WorkerMainThreadRunnable
{
public:
explicit PrefEnabledRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerMainThreadRunnable(aWorkerPrivate)
, mEnabled(false)
{ }
bool MainThreadRun() override
{
AssertIsOnMainThread();
mEnabled = Preferences::GetBool("dom.broadcastChannel.enabled", false);
return true;
}
bool IsEnabled() const
{
return mEnabled;
}
private:
bool mEnabled;
};
} // namespace
/* static */ bool
BroadcastChannel::IsEnabled(JSContext* aCx, JSObject* aGlobal)
{
if (NS_IsMainThread()) {
return Preferences::GetBool("dom.broadcastChannel.enabled", false);
}
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
nsRefPtr<PrefEnabledRunnable> runnable =
new PrefEnabledRunnable(workerPrivate);
runnable->Dispatch(workerPrivate->GetJSContext());
return runnable->IsEnabled();
}
BroadcastChannel::BroadcastChannel(nsPIDOMWindow* aWindow,
const PrincipalInfo& aPrincipalInfo,
const nsACString& aOrigin,

View File

@ -47,8 +47,6 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BroadcastChannel,
DOMEventTargetHelper)
static bool IsEnabled(JSContext* aCx, JSObject* aGlobal);
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;

View File

@ -1,11 +0,0 @@
onmessage = function() {
var exists = true;
try {
var bc = new BroadcastChannel('foobar');
} catch(e) {
exists = false;
}
postMessage({ exists: exists });
}

View File

@ -1,7 +1,6 @@
[DEFAULT]
support-files =
iframe_broadcastchannel.html
broadcastchannel_pref_worker.js
broadcastchannel_sharedWorker.js
broadcastchannel_worker.js
broadcastchannel_worker_alive.js
@ -17,7 +16,6 @@ support-files =
[test_broadcastchannel_basic.html]
[test_broadcastchannel_close.html]
[test_broadcastchannel_self.html]
[test_broadcastchannel_pref.html]
[test_broadcastchannel_sharedWorker.html]
[test_broadcastchannel_worker.html]
[test_broadcastchannel_worker_alive.html]

View File

@ -130,7 +130,7 @@ function runTest() {
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.broadcastChannel.enabled", true]]}, runTest);
runTest();
</script>
</body>

View File

@ -55,7 +55,7 @@ function runTest() {
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.broadcastChannel.enabled", true]]}, runTest);
runTest();
</script>
</body>

View File

@ -54,7 +54,7 @@ function runTest() {
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.broadcastChannel.enabled", true]]}, runTest);
runTest();
</script>
</body>

View File

@ -84,8 +84,7 @@ var steps = [
},
function() {
SpecialPowers.pushPrefEnv({"set": [["dom.broadcastChannel.enabled", true],
["network.disable.ipc.security", true],
SpecialPowers.pushPrefEnv({"set": [["network.disable.ipc.security", true],
["browser.pagethumbnails.capturing_disabled", true],
["dom.mozBrowserFramesEnabled", true],
["dom.ipc.browser_frames.oop_by_default", false],

View File

@ -84,8 +84,7 @@ var steps = [
},
function() {
SpecialPowers.pushPrefEnv({"set": [["dom.broadcastChannel.enabled", true],
["network.disable.ipc.security", true],
SpecialPowers.pushPrefEnv({"set": [["network.disable.ipc.security", true],
["browser.pagethumbnails.capturing_disabled", true],
["dom.mozBrowserFramesEnabled", true],
["dom.ipc.browser_frames.oop_by_default", false],

View File

@ -1,72 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for BroadcastChannel</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<div id="content"></div>
<script type="application/javascript">
function testNoPref() {
SpecialPowers.pushPrefEnv({"set": [["dom.broadcastChannel.enabled", false]]}, function() {
ok(!("BroadcastChannel" in window), "BroadcastChannel should not exist");
runTests();
});
}
function testPref() {
SpecialPowers.pushPrefEnv({"set": [["dom.broadcastChannel.enabled", true]]}, function() {
ok("BroadcastChannel" in window, "BroadcastChannel should exist");
runTests();
});
}
function testNoPrefWorker() {
SpecialPowers.pushPrefEnv({"set": [["dom.broadcastChannel.enabled", false]]}, function() {
var worker = new Worker("broadcastchannel_pref_worker.js");
worker.onmessage = function(event) {
ok(!event.data.exists, "BroadcastChannel should not exist in workers");
runTests();
}
worker.postMessage('go!');
});
}
function testPrefWorker() {
SpecialPowers.pushPrefEnv({"set": [["dom.broadcastChannel.enabled", true]]}, function() {
var worker = new Worker("broadcastchannel_pref_worker.js");
worker.onmessage = function(event) {
ok(event.data.exists, "BroadcastChannel should exist in workers");
runTests();
}
worker.postMessage('go!');
});
}
var tests = [
testNoPref,
testPref,
testNoPrefWorker,
testPrefWorker
];
function runTests() {
if (tests.length == 0) {
SimpleTest.finish();
return;
}
var test = tests.shift();
test();
}
SimpleTest.waitForExplicitFinish();
runTests();
</script>
</body>
</html>

View File

@ -109,7 +109,7 @@ function runTest() {
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.broadcastChannel.enabled", true]]}, runTest);
runTest();
</script>
</body>

View File

@ -30,7 +30,7 @@ function runTest() {
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.broadcastChannel.enabled", true]]}, runTest);
runTest();
</script>
</body>

View File

@ -44,8 +44,7 @@ function runTests() {
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.broadcastChannel.enabled", true],
["dom.workers.sharedWorkers.enabled", true]]}, runTests);
SpecialPowers.pushPrefEnv({"set": [["dom.workers.sharedWorkers.enabled", true]]}, runTests);
</script>
</pre>

View File

@ -54,7 +54,7 @@ function runTests() {
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.broadcastChannel.enabled", true]]}, runTests);
runTests();
</script>
</pre>

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