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

This commit is contained in:
Ryan VanderMeulen 2015-07-27 10:43:09 -04:00
Родитель bee8091366 4f09769d88
Коммит 7941dfa396
807 изменённых файлов: 13061 добавлений и 13252 удалений

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -148,8 +148,13 @@ let wrapper = {
if (accountData.customizeSync) { if (accountData.customizeSync) {
Services.prefs.setBoolPref(PREF_SYNC_SHOW_CUSTOMIZATION, true); 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 // We need to confirm a relink - see shouldAllowRelink for more
let newAccountEmail = accountData.email; let newAccountEmail = accountData.email;

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -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();
});

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -570,13 +570,15 @@ loop.conversationViews = (function(mozL10n) {
var OngoingConversationView = React.createClass({displayName: "OngoingConversationView", var OngoingConversationView = React.createClass({displayName: "OngoingConversationView",
mixins: [ mixins: [
loop.store.StoreMixin("conversationStore"),
sharedMixins.MediaSetupMixin sharedMixins.MediaSetupMixin
], ],
propTypes: { propTypes: {
// local // local
audio: React.PropTypes.object, 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, dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
// The poster URLs are for UI-showcase testing and development. // The poster URLs are for UI-showcase testing and development.
localPosterUrl: React.PropTypes.string, localPosterUrl: React.PropTypes.string,
@ -597,7 +599,17 @@ loop.conversationViews = (function(mozL10n) {
}, },
getInitialState: function() { 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() { 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() { shouldRenderRemoteVideo: function() {
if (this.props.mediaConnected) { if (this.props.mediaConnected) {
// If remote video is not enabled, we're muted, so we'll show an avatar // If remote video is not enabled, we're muted, so we'll show an avatar
@ -646,33 +682,25 @@ loop.conversationViews = (function(mozL10n) {
}, },
render: function() { render: function() {
var localStreamClasses = React.addons.classSet({
local: true,
"local-stream": true,
"local-stream-audio": !this.props.video.enabled
});
return ( return (
React.createElement("div", {className: "video-layout-wrapper"}, React.createElement("div", {className: "desktop-call-wrapper"},
React.createElement("div", {className: "conversation"}, React.createElement(sharedViews.MediaLayoutView, {
React.createElement("div", {className: "media nested"}, dispatcher: this.props.dispatcher,
React.createElement("div", {className: "video_wrapper remote_wrapper"}, displayScreenShare: false,
React.createElement("div", {className: "video_inner remote focus-stream"}, isLocalLoading: this._isLocalLoading(),
React.createElement(sharedViews.MediaView, {displayAvatar: !this.shouldRenderRemoteVideo(), isRemoteLoading: this._isRemoteLoading(),
isLoading: false, isScreenShareLoading: false,
mediaType: "remote", localPosterUrl: this.props.localPosterUrl,
posterUrl: this.props.remotePosterUrl, localSrcVideoObject: this.state.localSrcVideoObject,
srcVideoObject: this.state.remoteSrcVideoObject}) localVideoMuted: !this.props.video.enabled,
) matchMedia: this.state.matchMedia || window.matchMedia.bind(window),
), remotePosterUrl: this.props.remotePosterUrl,
React.createElement("div", {className: localStreamClasses}, remoteSrcVideoObject: this.state.remoteSrcVideoObject,
React.createElement(sharedViews.MediaView, {displayAvatar: !this.props.video.enabled, renderRemoteVideo: this.shouldRenderRemoteVideo(),
isLoading: false, screenSharePosterUrl: null,
mediaType: "local", screenShareVideoObject: this.state.screenShareVideoObject,
posterUrl: this.props.localPosterUrl, showContextRoomName: false,
srcVideoObject: this.state.localSrcVideoObject}) useDesktopPaths: true}),
)
),
React.createElement(loop.shared.views.ConversationToolbar, { React.createElement(loop.shared.views.ConversationToolbar, {
audio: this.props.audio, audio: this.props.audio,
dispatcher: this.props.dispatcher, dispatcher: this.props.dispatcher,
@ -681,7 +709,6 @@ loop.conversationViews = (function(mozL10n) {
publishStream: this.publishStream, publishStream: this.publishStream,
video: this.props.video}) video: this.props.video})
) )
)
); );
} }
}); });
@ -778,6 +805,7 @@ loop.conversationViews = (function(mozL10n) {
case CALL_STATES.ONGOING: { case CALL_STATES.ONGOING: {
return (React.createElement(OngoingConversationView, { return (React.createElement(OngoingConversationView, {
audio: {enabled: !this.state.audioMuted}, audio: {enabled: !this.state.audioMuted},
conversationStore: this.getStore(),
dispatcher: this.props.dispatcher, dispatcher: this.props.dispatcher,
mediaConnected: this.state.mediaConnected, mediaConnected: this.state.mediaConnected,
remoteSrcVideoObject: this.state.remoteSrcVideoObject, remoteSrcVideoObject: this.state.remoteSrcVideoObject,

Просмотреть файл

@ -570,13 +570,15 @@ loop.conversationViews = (function(mozL10n) {
var OngoingConversationView = React.createClass({ var OngoingConversationView = React.createClass({
mixins: [ mixins: [
loop.store.StoreMixin("conversationStore"),
sharedMixins.MediaSetupMixin sharedMixins.MediaSetupMixin
], ],
propTypes: { propTypes: {
// local // local
audio: React.PropTypes.object, 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, dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
// The poster URLs are for UI-showcase testing and development. // The poster URLs are for UI-showcase testing and development.
localPosterUrl: React.PropTypes.string, localPosterUrl: React.PropTypes.string,
@ -597,7 +599,17 @@ loop.conversationViews = (function(mozL10n) {
}, },
getInitialState: function() { 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() { 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() { shouldRenderRemoteVideo: function() {
if (this.props.mediaConnected) { if (this.props.mediaConnected) {
// If remote video is not enabled, we're muted, so we'll show an avatar // If remote video is not enabled, we're muted, so we'll show an avatar
@ -646,33 +682,25 @@ loop.conversationViews = (function(mozL10n) {
}, },
render: function() { render: function() {
var localStreamClasses = React.addons.classSet({
local: true,
"local-stream": true,
"local-stream-audio": !this.props.video.enabled
});
return ( return (
<div className="video-layout-wrapper"> <div className="desktop-call-wrapper">
<div className="conversation"> <sharedViews.MediaLayoutView
<div className="media nested"> dispatcher={this.props.dispatcher}
<div className="video_wrapper remote_wrapper"> displayScreenShare={false}
<div className="video_inner remote focus-stream"> isLocalLoading={this._isLocalLoading()}
<sharedViews.MediaView displayAvatar={!this.shouldRenderRemoteVideo()} isRemoteLoading={this._isRemoteLoading()}
isLoading={false} isScreenShareLoading={false}
mediaType="remote" localPosterUrl={this.props.localPosterUrl}
posterUrl={this.props.remotePosterUrl} localSrcVideoObject={this.state.localSrcVideoObject}
srcVideoObject={this.state.remoteSrcVideoObject} /> localVideoMuted={!this.props.video.enabled}
</div> matchMedia={this.state.matchMedia || window.matchMedia.bind(window)}
</div> remotePosterUrl={this.props.remotePosterUrl}
<div className={localStreamClasses}> remoteSrcVideoObject={this.state.remoteSrcVideoObject}
<sharedViews.MediaView displayAvatar={!this.props.video.enabled} renderRemoteVideo={this.shouldRenderRemoteVideo()}
isLoading={false} screenSharePosterUrl={null}
mediaType="local" screenShareVideoObject={this.state.screenShareVideoObject}
posterUrl={this.props.localPosterUrl} showContextRoomName={false}
srcVideoObject={this.state.localSrcVideoObject} /> useDesktopPaths={true} />
</div>
</div>
<loop.shared.views.ConversationToolbar <loop.shared.views.ConversationToolbar
audio={this.props.audio} audio={this.props.audio}
dispatcher={this.props.dispatcher} dispatcher={this.props.dispatcher}
@ -681,7 +709,6 @@ loop.conversationViews = (function(mozL10n) {
publishStream={this.publishStream} publishStream={this.publishStream}
video={this.props.video} /> video={this.props.video} />
</div> </div>
</div>
); );
} }
}); });
@ -778,6 +805,7 @@ loop.conversationViews = (function(mozL10n) {
case CALL_STATES.ONGOING: { case CALL_STATES.ONGOING: {
return (<OngoingConversationView return (<OngoingConversationView
audio={{enabled: !this.state.audioMuted}} audio={{enabled: !this.state.audioMuted}}
conversationStore={this.getStore()}
dispatcher={this.props.dispatcher} dispatcher={this.props.dispatcher}
mediaConnected={this.state.mediaConnected} mediaConnected={this.state.mediaConnected}
remoteSrcVideoObject={this.state.remoteSrcVideoObject} remoteSrcVideoObject={this.state.remoteSrcVideoObject}

Просмотреть файл

@ -665,7 +665,7 @@ loop.roomViews = (function(mozL10n) {
* @returns {boolean} * @returns {boolean}
* @private * @private
*/ */
_shouldRenderLocalLoading: function () { _isLocalLoading: function () {
return this.state.roomState === ROOM_STATES.MEDIA_WAIT && return this.state.roomState === ROOM_STATES.MEDIA_WAIT &&
!this.state.localSrcVideoObject; !this.state.localSrcVideoObject;
}, },
@ -677,7 +677,7 @@ loop.roomViews = (function(mozL10n) {
* @returns {boolean} * @returns {boolean}
* @private * @private
*/ */
_shouldRenderRemoteLoading: function() { _isRemoteLoading: function() {
return !!(this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS && return !!(this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
!this.state.remoteSrcVideoObject && !this.state.remoteSrcVideoObject &&
!this.state.mediaConnected); !this.state.mediaConnected);
@ -741,12 +741,25 @@ loop.roomViews = (function(mozL10n) {
return null; return null;
} }
default: { default: {
return ( return (
React.createElement("div", {className: "room-conversation-wrapper"}, React.createElement("div", {className: "room-conversation-wrapper desktop-room-wrapper"},
React.createElement("div", {className: "video-layout-wrapper"}, React.createElement(sharedViews.MediaLayoutView, {
React.createElement("div", {className: "conversation room-conversation"}, dispatcher: this.props.dispatcher,
React.createElement("div", {className: "media nested"}, 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, { React.createElement(DesktopRoomInvitationView, {
dispatcher: this.props.dispatcher, dispatcher: this.props.dispatcher,
error: this.state.error, error: this.state.error,
@ -758,22 +771,6 @@ loop.roomViews = (function(mozL10n) {
show: shouldRenderInvitationOverlay, show: shouldRenderInvitationOverlay,
showEditContext: shouldRenderInvitationOverlay && shouldRenderEditContextView, showEditContext: shouldRenderInvitationOverlay && shouldRenderEditContextView,
socialShareProviders: this.state.socialShareProviders}), 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, { React.createElement(DesktopRoomEditContextView, {
dispatcher: this.props.dispatcher, dispatcher: this.props.dispatcher,
error: this.state.error, error: this.state.error,
@ -793,12 +790,6 @@ loop.roomViews = (function(mozL10n) {
screenShare: screenShareData, screenShare: screenShareData,
video: {enabled: !this.state.videoMuted, visible: true}}) video: {enabled: !this.state.videoMuted, visible: true}})
) )
),
React.createElement(sharedViews.chat.TextChatView, {
dispatcher: this.props.dispatcher,
showRoomName: false,
useDesktopPaths: true})
)
); );
} }
} }

Просмотреть файл

@ -665,7 +665,7 @@ loop.roomViews = (function(mozL10n) {
* @returns {boolean} * @returns {boolean}
* @private * @private
*/ */
_shouldRenderLocalLoading: function () { _isLocalLoading: function () {
return this.state.roomState === ROOM_STATES.MEDIA_WAIT && return this.state.roomState === ROOM_STATES.MEDIA_WAIT &&
!this.state.localSrcVideoObject; !this.state.localSrcVideoObject;
}, },
@ -677,7 +677,7 @@ loop.roomViews = (function(mozL10n) {
* @returns {boolean} * @returns {boolean}
* @private * @private
*/ */
_shouldRenderRemoteLoading: function() { _isRemoteLoading: function() {
return !!(this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS && return !!(this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
!this.state.remoteSrcVideoObject && !this.state.remoteSrcVideoObject &&
!this.state.mediaConnected); !this.state.mediaConnected);
@ -741,12 +741,25 @@ loop.roomViews = (function(mozL10n) {
return null; return null;
} }
default: { default: {
return ( return (
<div className="room-conversation-wrapper"> <div className="room-conversation-wrapper desktop-room-wrapper">
<div className="video-layout-wrapper"> <sharedViews.MediaLayoutView
<div className="conversation room-conversation"> dispatcher={this.props.dispatcher}
<div className="media nested"> 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 <DesktopRoomInvitationView
dispatcher={this.props.dispatcher} dispatcher={this.props.dispatcher}
error={this.state.error} error={this.state.error}
@ -758,22 +771,6 @@ loop.roomViews = (function(mozL10n) {
show={shouldRenderInvitationOverlay} show={shouldRenderInvitationOverlay}
showEditContext={shouldRenderInvitationOverlay && shouldRenderEditContextView} showEditContext={shouldRenderInvitationOverlay && shouldRenderEditContextView}
socialShareProviders={this.state.socialShareProviders} /> 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 <DesktopRoomEditContextView
dispatcher={this.props.dispatcher} dispatcher={this.props.dispatcher}
error={this.state.error} error={this.state.error}
@ -782,7 +779,7 @@ loop.roomViews = (function(mozL10n) {
roomData={roomData} roomData={roomData}
savingContext={this.state.savingContext} savingContext={this.state.savingContext}
show={!shouldRenderInvitationOverlay && shouldRenderEditContextView} /> show={!shouldRenderInvitationOverlay && shouldRenderEditContextView} />
</div> </sharedViews.MediaLayoutView>
<sharedViews.ConversationToolbar <sharedViews.ConversationToolbar
audio={{enabled: !this.state.audioMuted, visible: true}} audio={{enabled: !this.state.audioMuted, visible: true}}
dispatcher={this.props.dispatcher} dispatcher={this.props.dispatcher}
@ -793,12 +790,6 @@ loop.roomViews = (function(mozL10n) {
screenShare={screenShareData} screenShare={screenShareData}
video={{enabled: !this.state.videoMuted, visible: true}} /> video={{enabled: !this.state.videoMuted, visible: true}} />
</div> </div>
</div>
<sharedViews.chat.TextChatView
dispatcher={this.props.dispatcher}
showRoomName={false}
useDesktopPaths={true} />
</div>
); );
} }
} }

Просмотреть файл

@ -504,28 +504,6 @@
text-align: center; 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 { .conversation .media.nested .focus-stream {
display: inline-block; display: inline-block;
position: absolute; /* workaround for lack of object-fit; see bug 1020445 */ position: absolute; /* workaround for lack of object-fit; see bug 1020445 */
@ -592,15 +570,11 @@
z-index: 1; z-index: 1;
} }
.remote .avatar { .remote > .avatar {
/* make visually distinct from local avatar */ /* make visually distinct from local avatar */
opacity: 0.25; opacity: 0.25;
} }
.fx-embedded .media.nested {
min-height: 200px;
}
.fx-embedded-call-identifier { .fx-embedded-call-identifier {
display: inline; display: inline;
width: 100%; width: 100%;
@ -675,7 +649,9 @@
* */ * */
html, .fx-embedded, #main, html, .fx-embedded, #main,
.video-layout-wrapper, .video-layout-wrapper,
.conversation { .conversation,
.desktop-call-wrapper,
.desktop-room-wrapper {
height: 100%; height: 100%;
} }
@ -935,7 +911,6 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
border-top: 2px solid #444; border-top: 2px solid #444;
border-bottom: 2px solid #444; border-bottom: 2px solid #444;
padding: .5rem; padding: .5rem;
max-height: 400px;
position: absolute; position: absolute;
left: 0; left: 0;
bottom: 0; bottom: 0;
@ -951,7 +926,7 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
/* Make the context view float atop the video elements. */ /* Make the context view float atop the video elements. */
z-index: 2; z-index: 3;
} }
.room-invitation-overlay .room-context { .room-invitation-overlay .room-context {
@ -1087,12 +1062,12 @@ html[dir="rtl"] .room-context-btn-close {
.standalone-room-wrapper > .media-layout { .standalone-room-wrapper > .media-layout {
/* 50px is the header, 64px for toolbar, 3em is the footer. */ /* 50px is the header, 64px for toolbar, 3em is the footer. */
height: calc(100% - 50px - 64px - 3em); height: calc(100% - 50px - 64px - 3em);
margin: 0 10px;
} }
.media-layout > .media-wrapper { .media-layout > .media-wrapper {
display: flex; display: flex;
flex-flow: column wrap; flex-flow: column wrap;
margin: 0 10px;
height: 100%; height: 100%;
} }
@ -1139,6 +1114,14 @@ html[dir="rtl"] .room-context-btn-close {
height: calc(100% - 300px); 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 /* Temporarily slaved from .media-wrapper until we use it in more places
to avoid affecting the conversation window on desktop. */ to avoid affecting the conversation window on desktop. */
.media-wrapper > .text-chat-view > .text-chat-entries { .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 /* Temporarily slaved from .media-wrapper until we use it in more places
to avoid affecting the conversation window on desktop. */ 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. */ /* 40px is the height of .text-chat-box. */
height: calc(100% - 40px); height: calc(100% - 40px);
width: 100%; width: 100%;
@ -1215,20 +1198,26 @@ html[dir="rtl"] .room-context-btn-close {
height: 100%; height: 100%;
} }
.media-wrapper > .local { .media-wrapper > .focus-stream > .local {
/* Position over the remote video */ /* Position over the remote video */
position: absolute; position: absolute;
/* Make sure its on top */ /* Make sure its on top */
z-index: 1001; z-index: 2;
margin: 3px; margin: 3px;
right: 0; right: 0;
/* 29px is (30% of 50px high header) + (height toolbar (38px) + /* 29px is (30% of 50px high header) + (height toolbar (38px) +
height footer (25px) - height header (50px)) */ height footer (25px) - height header (50px)) */
bottom: calc(30% + 29px); bottom: 0;
width: 120px; width: 120px;
height: 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 { html[dir="rtl"] .media-wrapper > .local {
right: auto; right: auto;
left: 0; left: 0;
@ -1247,6 +1236,15 @@ html[dir="rtl"] .room-context-btn-close {
height: 30%; 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 { .media-wrapper.receiving-screen-share > .screen {
order: 1; 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 { .standalone > #main > .room-conversation-wrapper > .media-layout > .conversation-toolbar {
border: none; border: none;
} }
@ -1415,37 +1454,12 @@ html[dir="rtl"] .standalone .room-conversation-wrapper .room-inner-info-area {
height: auto; height: auto;
} }
/* Text chat in rooms styles */ /* Text chat in styles */
.fx-embedded .room-conversation-wrapper {
display: flex;
flex-flow: column nowrap;
}
.fx-embedded .video-layout-wrapper {
flex: 1 1 auto;
}
.text-chat-view { .text-chat-view {
background: white; 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 { .text-chat-box {
flex: 0 0 auto; flex: 0 0 auto;
max-height: 40px; 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 { .self-view-hidden-message {
/* Not displayed by default; display is turned on elsewhere when the /* Not displayed by default; display is turned on elsewhere when the
* self-view is actually hidden. * self-view is actually hidden.

Просмотреть файл

@ -580,21 +580,6 @@ loop.store.ActiveRoomStore = (function() {
* @param {sharedActions.ConnectionFailure} actionData * @param {sharedActions.ConnectionFailure} actionData
*/ */
connectionFailure: function(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 ? var exitState = this._storeState.roomState === ROOM_STATES.FAILED ?
this._storeState.failureExitState : this._storeState.roomState; this._storeState.failureExitState : this._storeState.roomState;

Просмотреть файл

@ -146,21 +146,6 @@ loop.store = loop.store || {};
* @param {sharedActions.ConnectionFailure} actionData The action data. * @param {sharedActions.ConnectionFailure} actionData The action data.
*/ */
connectionFailure: function(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;
}
this._endSession(); this._endSession();
this.setStoreState({ this.setStoreState({
callState: CALL_STATES.TERMINATED, callState: CALL_STATES.TERMINATED,

Просмотреть файл

@ -61,15 +61,26 @@ loop.OTSdkDriver = (function() {
/** /**
* XXX This is a workaround for desktop machines that do not have a * XXX This is a workaround for desktop machines that do not have a
* camera installed. As we don't yet have device enumeration, when * camera installed. The SDK doesn't currently do use the new device
* we do, this can be removed (bug 1138851), and the sdk should handle it. * 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 // 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 // the result. So here we define our own one which wraps around the
// we can change it later. // real device enumeration api.
window.MediaStreamTrack.getSources = function(callback) { 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)); 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 expect the local video to be muted automatically by the SDK. Hence
// we don't mute it manually here. // we don't mute it manually here.
this._mockPublisherEl = document.createElement("div"); 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, this.publisher = this.sdk.initPublisher(this._mockPublisherEl,
_.extend(this._getDataChannelSettings, this._getCopyPublisherConfig)); _.extend(this._getDataChannelSettings, this._getCopyPublisherConfig));
@ -135,17 +138,6 @@ loop.OTSdkDriver = (function() {
this._onAccessDialogOpened.bind(this)); 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 * Handles the setMute action. Informs the published stream to mute
* or unmute audio as appropriate. * or unmute audio as appropriate.

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -946,6 +946,7 @@ loop.shared.views = (function(_, mozL10n) {
var MediaLayoutView = React.createClass({displayName: "MediaLayoutView", var MediaLayoutView = React.createClass({displayName: "MediaLayoutView",
propTypes: { propTypes: {
children: React.PropTypes.node,
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
displayScreenShare: React.PropTypes.bool.isRequired, displayScreenShare: React.PropTypes.bool.isRequired,
isLocalLoading: React.PropTypes.bool.isRequired, isLocalLoading: React.PropTypes.bool.isRequired,
@ -955,6 +956,9 @@ loop.shared.views = (function(_, mozL10n) {
localPosterUrl: React.PropTypes.string, localPosterUrl: React.PropTypes.string,
localSrcVideoObject: React.PropTypes.object, localSrcVideoObject: React.PropTypes.object,
localVideoMuted: React.PropTypes.bool.isRequired, 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, remotePosterUrl: React.PropTypes.string,
remoteSrcVideoObject: React.PropTypes.object, remoteSrcVideoObject: React.PropTypes.object,
renderRemoteVideo: React.PropTypes.bool.isRequired, renderRemoteVideo: React.PropTypes.bool.isRequired,
@ -964,6 +968,60 @@ loop.shared.views = (function(_, mozL10n) {
useDesktopPaths: React.PropTypes.bool.isRequired 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() { render: function() {
var remoteStreamClasses = React.addons.classSet({ var remoteStreamClasses = React.addons.classSet({
"remote": true, "remote": true,
@ -979,7 +1037,9 @@ loop.shared.views = (function(_, mozL10n) {
"media-wrapper": true, "media-wrapper": true,
"receiving-screen-share": this.props.displayScreenShare, "receiving-screen-share": this.props.displayScreenShare,
"showing-local-streams": this.props.localSrcVideoObject || "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 ( return (
@ -993,7 +1053,10 @@ loop.shared.views = (function(_, mozL10n) {
isLoading: this.props.isRemoteLoading, isLoading: this.props.isRemoteLoading,
mediaType: "remote", mediaType: "remote",
posterUrl: this.props.remotePosterUrl, 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("div", {className: screenShareStreamClasses},
React.createElement(MediaView, {displayAvatar: false, React.createElement(MediaView, {displayAvatar: false,
@ -1006,13 +1069,8 @@ loop.shared.views = (function(_, mozL10n) {
dispatcher: this.props.dispatcher, dispatcher: this.props.dispatcher,
showRoomName: this.props.showContextRoomName, showRoomName: this.props.showContextRoomName,
useDesktopPaths: false}), useDesktopPaths: false}),
React.createElement("div", {className: "local"}, this.state.localMediaAboslutelyPositioned ?
React.createElement(MediaView, {displayAvatar: this.props.localVideoMuted, null : this.renderLocalVideo()
isLoading: this.props.isLocalLoading,
mediaType: "local",
posterUrl: this.props.localPosterUrl,
srcVideoObject: this.props.localSrcVideoObject})
)
) )
) )
); );

Просмотреть файл

@ -946,6 +946,7 @@ loop.shared.views = (function(_, mozL10n) {
var MediaLayoutView = React.createClass({ var MediaLayoutView = React.createClass({
propTypes: { propTypes: {
children: React.PropTypes.node,
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired, dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
displayScreenShare: React.PropTypes.bool.isRequired, displayScreenShare: React.PropTypes.bool.isRequired,
isLocalLoading: React.PropTypes.bool.isRequired, isLocalLoading: React.PropTypes.bool.isRequired,
@ -955,6 +956,9 @@ loop.shared.views = (function(_, mozL10n) {
localPosterUrl: React.PropTypes.string, localPosterUrl: React.PropTypes.string,
localSrcVideoObject: React.PropTypes.object, localSrcVideoObject: React.PropTypes.object,
localVideoMuted: React.PropTypes.bool.isRequired, 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, remotePosterUrl: React.PropTypes.string,
remoteSrcVideoObject: React.PropTypes.object, remoteSrcVideoObject: React.PropTypes.object,
renderRemoteVideo: React.PropTypes.bool.isRequired, renderRemoteVideo: React.PropTypes.bool.isRequired,
@ -964,6 +968,60 @@ loop.shared.views = (function(_, mozL10n) {
useDesktopPaths: React.PropTypes.bool.isRequired 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() { render: function() {
var remoteStreamClasses = React.addons.classSet({ var remoteStreamClasses = React.addons.classSet({
"remote": true, "remote": true,
@ -979,7 +1037,9 @@ loop.shared.views = (function(_, mozL10n) {
"media-wrapper": true, "media-wrapper": true,
"receiving-screen-share": this.props.displayScreenShare, "receiving-screen-share": this.props.displayScreenShare,
"showing-local-streams": this.props.localSrcVideoObject || "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 ( return (
@ -994,6 +1054,9 @@ loop.shared.views = (function(_, mozL10n) {
mediaType="remote" mediaType="remote"
posterUrl={this.props.remotePosterUrl} posterUrl={this.props.remotePosterUrl}
srcVideoObject={this.props.remoteSrcVideoObject} /> srcVideoObject={this.props.remoteSrcVideoObject} />
{ this.state.localMediaAboslutelyPositioned ?
this.renderLocalVideo() : null }
{ this.props.children }
</div> </div>
<div className={screenShareStreamClasses}> <div className={screenShareStreamClasses}>
<MediaView displayAvatar={false} <MediaView displayAvatar={false}
@ -1006,13 +1069,8 @@ loop.shared.views = (function(_, mozL10n) {
dispatcher={this.props.dispatcher} dispatcher={this.props.dispatcher}
showRoomName={this.props.showContextRoomName} showRoomName={this.props.showContextRoomName}
useDesktopPaths={false} /> useDesktopPaths={false} />
<div className="local"> { this.state.localMediaAboslutelyPositioned ?
<MediaView displayAvatar={this.props.localVideoMuted} null : this.renderLocalVideo() }
isLoading={this.props.isLocalLoading}
mediaType="local"
posterUrl={this.props.localPosterUrl}
srcVideoObject={this.props.localSrcVideoObject} />
</div>
</div> </div>
</div> </div>
); );

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -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() { describe("Edit Context", function() {
it("should show the form when the edit button is clicked", function() { it("should show the form when the edit button is clicked", function() {
view = mountTestComponent(); view = mountTestComponent();

Просмотреть файл

@ -99,7 +99,7 @@ class Test1BrowserCall(MarionetteTestCase):
self.switch_to_chatbox() self.switch_to_chatbox()
# expect a video container on desktop side # 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.assertEqual(media_container.tag_name, "div", "expect a video container")
self.check_video(".local-video") self.check_video(".local-video")

Просмотреть файл

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

Просмотреть файл

@ -167,3 +167,19 @@ add_task(function* test_screen_share() {
MozLoopService.setScreenShareState("1", false); MozLoopService.setScreenShareState("1", false);
Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state"); 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();
});

Просмотреть файл

@ -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() { it("should store the failure reason", function() {
store.connectionFailure(connectionFailureAction); store.connectionFailure(connectionFailureAction);

Просмотреть файл

@ -147,26 +147,6 @@ describe("loop.store.ConversationStore", function () {
store.setStoreState({windowId: "42"}); 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() { it("should disconnect the session", function() {
store.connectionFailure( store.connectionFailure(
new sharedActions.ConnectionFailure({reason: "fake"})); new sharedActions.ConnectionFailure({reason: "fake"}));

Просмотреть файл

@ -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() { describe("#setMute", function() {
beforeEach(function() { beforeEach(function() {
sdk.initPublisher.returns(publisher); sdk.initPublisher.returns(publisher);

Просмотреть файл

@ -56,27 +56,6 @@ describe("loop.shared.views.TextChatView", function () {
store.setStoreState({ textChatEnabled: true }); 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() { it("should render message entries when message were sent/ received", function() {
view = mountTestComponent({ view = mountTestComponent({
messageList: [{ messageList: [{
@ -297,6 +276,41 @@ describe("loop.shared.views.TextChatView", function () {
fakeServer.restore(); 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() { it("should show timestamps from msgs sent more than 1 min apart", function() {
view = mountTestComponent(); view = mountTestComponent();
@ -326,12 +340,6 @@ describe("loop.shared.views.TextChatView", function () {
.to.eql(2); .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() { it("should render message entries when message were sent/ received", function() {
view = mountTestComponent(); view = mountTestComponent();

Просмотреть файл

@ -1057,6 +1057,7 @@ describe("loop.shared.views", function() {
isRemoteLoading: false, isRemoteLoading: false,
isScreenShareLoading: false, isScreenShareLoading: false,
localVideoMuted: false, localVideoMuted: false,
matchMedia: window.matchMedia,
renderRemoteVideo: false, renderRemoteVideo: false,
showContextRoomName: false, showContextRoomName: false,
useDesktopPaths: false useDesktopPaths: false
@ -1144,5 +1145,35 @@ describe("loop.shared.views", function() {
expect(view.getDOMNode().querySelector(".media-wrapper") expect(view.getDOMNode().querySelector(".media-wrapper")
.classList.contains("showing-local-streams")).eql(true); .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);
});
}); });
}); });

Просмотреть файл

@ -75,13 +75,25 @@
var dispatcher = new loop.Dispatcher(); 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) { sendTextChatMessage: function(message) {
dispatcher.dispatch(new loop.shared.actions.ReceivedTextChatMessage({ dispatcher.dispatch(new loop.shared.actions.ReceivedTextChatMessage({
message: message.message message: message.message
})); }));
} }
}, Backbone.Events); };
var mockSDK = new MockSDK();
/** /**
* Every view that uses an activeRoomStore needs its own; if they shared * Every view that uses an activeRoomStore needs its own; if they shared
@ -116,7 +128,6 @@
}); });
store.forcedUpdate = function forcedUpdate(contentWindow) { store.forcedUpdate = function forcedUpdate(contentWindow) {
// Since this is called by setTimeout, we don't want to lose any // 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... // exceptions if there's a problem and we need to debug, so...
try { try {
@ -136,6 +147,17 @@
camera: {height: 480, orientation: 0, width: 640} camera: {height: 480, orientation: 0, width: 640}
}, },
remoteVideoEnabled: options.remoteVideoEnabled, 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), matchMedia: contentWindow.matchMedia.bind(contentWindow),
roomState: options.roomState, roomState: options.roomState,
videoMuted: !!options.videoMuted videoMuted: !!options.videoMuted
@ -185,6 +207,10 @@
roomState: ROOM_STATES.HAS_PARTICIPANTS roomState: ROOM_STATES.HAS_PARTICIPANTS
}); });
var updatingMobileActiveRoomStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS
});
var localFaceMuteRoomStore = makeActiveRoomStore({ var localFaceMuteRoomStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS, roomState: ROOM_STATES.HAS_PARTICIPANTS,
videoMuted: true videoMuted: true
@ -201,12 +227,19 @@
receivingScreenShare: true receivingScreenShare: true
}); });
var updatingSharingRoomMobileStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS,
receivingScreenShare: true
});
var loadingRemoteLoadingScreenStore = makeActiveRoomStore({ var loadingRemoteLoadingScreenStore = makeActiveRoomStore({
mediaConnected: false, mediaConnected: false,
receivingScreenShare: true,
roomState: ROOM_STATES.HAS_PARTICIPANTS, roomState: ROOM_STATES.HAS_PARTICIPANTS,
remoteSrcVideoObject: false remoteSrcVideoObject: false
}); });
var loadingScreenSharingRoomStore = makeActiveRoomStore({ var loadingScreenSharingRoomStore = makeActiveRoomStore({
receivingScreenShare: true,
roomState: ROOM_STATES.HAS_PARTICIPANTS roomState: ROOM_STATES.HAS_PARTICIPANTS
}); });
@ -234,7 +267,10 @@
}); });
var invitationRoomStore = new loop.store.RoomStore(dispatcher, { 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, { 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({ var desktopLocalFaceMuteActiveRoomStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS, roomState: ROOM_STATES.HAS_PARTICIPANTS,
videoMuted: true videoMuted: true
@ -272,14 +322,58 @@
activeRoomStore: desktopRemoteFaceMuteActiveRoomStore activeRoomStore: desktopRemoteFaceMuteActiveRoomStore
}); });
var conversationStore = new loop.store.ConversationStore(dispatcher, { 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: {}, client: {},
mozLoop: navigator.mozLoop, mozLoop: navigator.mozLoop,
sdkDriver: mockSDK sdkDriver: mockSDK
}); });
var textChatStore = new loop.store.TextChatStore(dispatcher, {
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. // Update the text chat store with the room info.
textChatStore.updateRoomInfo(new sharedActions.UpdateRoomInfo({ textChatStore.updateRoomInfo(new sharedActions.UpdateRoomInfo({
@ -341,7 +435,7 @@
loop.store.StoreMixin.register({ loop.store.StoreMixin.register({
activeRoomStore: activeRoomStore, activeRoomStore: activeRoomStore,
conversationStore: conversationStore, conversationStore: conversationStores[0],
textChatStore: textChatStore textChatStore: textChatStore
}); });
@ -360,14 +454,6 @@
requestCallUrlInfo: noop 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({ var mockWebSocket = new loop.CallConnectionWebSocket({
url: "fake", url: "fake",
callId: "fakeId", callId: "fakeId",
@ -763,7 +849,7 @@
React.createElement("div", {className: "fx-embedded"}, React.createElement("div", {className: "fx-embedded"},
React.createElement(CallFailedView, {dispatcher: dispatcher, React.createElement(CallFailedView, {dispatcher: dispatcher,
outgoing: false, outgoing: false,
store: conversationStore}) store: conversationStores[0]})
) )
), ),
React.createElement(Example, {dashed: true, React.createElement(Example, {dashed: true,
@ -772,7 +858,7 @@
React.createElement("div", {className: "fx-embedded"}, React.createElement("div", {className: "fx-embedded"},
React.createElement(CallFailedView, {dispatcher: dispatcher, React.createElement(CallFailedView, {dispatcher: dispatcher,
outgoing: true, outgoing: true,
store: conversationStore}) store: conversationStores[1]})
) )
), ),
React.createElement(Example, {dashed: true, React.createElement(Example, {dashed: true,
@ -781,18 +867,22 @@
React.createElement("div", {className: "fx-embedded"}, React.createElement("div", {className: "fx-embedded"},
React.createElement(CallFailedView, {dispatcher: dispatcher, emailLinkError: true, React.createElement(CallFailedView, {dispatcher: dispatcher, emailLinkError: true,
outgoing: true, outgoing: true,
store: conversationStore}) store: conversationStores[0]})
) )
) )
), ),
React.createElement(Section, {name: "OngoingConversationView"}, React.createElement(Section, {name: "OngoingConversationView"},
React.createElement(FramedExample, {height: 254, React.createElement(FramedExample, {
dashed: true,
height: 394,
onContentsRendered: conversationStores[0].forcedUpdate,
summary: "Desktop ongoing conversation window", summary: "Desktop ongoing conversation window",
width: 298}, width: 298},
React.createElement("div", {className: "fx-embedded"}, React.createElement("div", {className: "fx-embedded"},
React.createElement(OngoingConversationView, { React.createElement(OngoingConversationView, {
audio: {enabled: true}, audio: {enabled: true},
conversationStore: conversationStores[0],
dispatcher: dispatcher, dispatcher: dispatcher,
localPosterUrl: "sample-img/video-screen-local.png", localPosterUrl: "sample-img/video-screen-local.png",
mediaConnected: true, mediaConnected: true,
@ -802,12 +892,34 @@
) )
), ),
React.createElement(FramedExample, {height: 600, React.createElement(FramedExample, {
summary: "Desktop ongoing conversation window large", 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: 600,
onContentsRendered: conversationStores[2].forcedUpdate,
summary: "Desktop ongoing conversation window (large)",
width: 800}, width: 800},
React.createElement("div", {className: "fx-embedded"}, React.createElement("div", {className: "fx-embedded"},
React.createElement(OngoingConversationView, { React.createElement(OngoingConversationView, {
audio: {enabled: true}, audio: {enabled: true},
conversationStore: conversationStores[2],
dispatcher: dispatcher, dispatcher: dispatcher,
localPosterUrl: "sample-img/video-screen-local.png", localPosterUrl: "sample-img/video-screen-local.png",
mediaConnected: true, mediaConnected: true,
@ -817,13 +929,18 @@
) )
), ),
React.createElement(FramedExample, {height: 254, React.createElement(FramedExample, {
dashed: true,
height: 394,
onContentsRendered: conversationStores[3].forcedUpdate,
summary: "Desktop ongoing conversation window - local face mute", summary: "Desktop ongoing conversation window - local face mute",
width: 298}, width: 298},
React.createElement("div", {className: "fx-embedded"}, React.createElement("div", {className: "fx-embedded"},
React.createElement(OngoingConversationView, { React.createElement(OngoingConversationView, {
audio: {enabled: true}, audio: {enabled: true},
conversationStore: conversationStores[3],
dispatcher: dispatcher, dispatcher: dispatcher,
localPosterUrl: "sample-img/video-screen-local.png",
mediaConnected: true, mediaConnected: true,
remotePosterUrl: "sample-img/video-screen-remote.png", remotePosterUrl: "sample-img/video-screen-remote.png",
remoteVideoEnabled: true, 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", summary: "Desktop ongoing conversation window - remote face mute",
width: 298}, width: 298},
React.createElement("div", {className: "fx-embedded"}, React.createElement("div", {className: "fx-embedded"},
React.createElement(OngoingConversationView, { React.createElement(OngoingConversationView, {
audio: {enabled: true}, audio: {enabled: true},
conversationStore: conversationStores[4],
dispatcher: dispatcher, dispatcher: dispatcher,
localPosterUrl: "sample-img/video-screen-local.png", localPosterUrl: "sample-img/video-screen-local.png",
mediaConnected: true, mediaConnected: true,
remotePosterUrl: "sample-img/video-screen-remote.png",
remoteVideoEnabled: false, remoteVideoEnabled: false,
video: {enabled: true}}) video: {enabled: true}})
) )
@ -894,7 +1015,8 @@
React.createElement(Section, {name: "DesktopRoomConversationView"}, React.createElement(Section, {name: "DesktopRoomConversationView"},
React.createElement(FramedExample, { 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)", summary: "Desktop room conversation (invitation, text-chat inclusion/scrollbars don't happen in real client)",
width: 298}, width: 298},
React.createElement("div", {className: "fx-embedded"}, React.createElement("div", {className: "fx-embedded"},
@ -911,6 +1033,7 @@
React.createElement(FramedExample, { React.createElement(FramedExample, {
dashed: true, dashed: true,
height: 394, height: 394,
onContentsRendered: desktopRoomStoreLoading.activeRoomStore.forcedUpdate,
summary: "Desktop room conversation (loading)", summary: "Desktop room conversation (loading)",
width: 298}, width: 298},
/* Hide scrollbars here. Rotating loading div overflows and causes /* Hide scrollbars here. Rotating loading div overflows and causes
@ -927,8 +1050,12 @@
) )
), ),
React.createElement(FramedExample, {height: 254, React.createElement(FramedExample, {
summary: "Desktop room conversation"}, dashed: true,
height: 394,
onContentsRendered: roomStore.activeRoomStore.forcedUpdate,
summary: "Desktop room conversation",
width: 298},
React.createElement("div", {className: "fx-embedded"}, React.createElement("div", {className: "fx-embedded"},
React.createElement(DesktopRoomConversationView, { React.createElement(DesktopRoomConversationView, {
dispatcher: dispatcher, dispatcher: dispatcher,
@ -941,8 +1068,46 @@
) )
), ),
React.createElement(FramedExample, {dashed: true, 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, height: 394,
onContentsRendered: desktopLocalFaceMuteRoomStore.activeRoomStore.forcedUpdate,
summary: "Desktop room conversation local face-mute", summary: "Desktop room conversation local face-mute",
width: 298}, width: 298},
React.createElement("div", {className: "fx-embedded"}, React.createElement("div", {className: "fx-embedded"},
@ -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", summary: "Desktop room conversation remote face-mute",
width: 298}, width: 298},
React.createElement("div", {className: "fx-embedded"}, React.createElement("div", {className: "fx-embedded"},
@ -964,6 +1131,7 @@
localPosterUrl: "sample-img/video-screen-local.png", localPosterUrl: "sample-img/video-screen-local.png",
mozLoop: navigator.mozLoop, mozLoop: navigator.mozLoop,
onCallTerminated: function(){}, onCallTerminated: function(){},
remotePosterUrl: "sample-img/video-screen-remote.png",
roomStore: desktopRemoteFaceMuteRoomStore}) roomStore: desktopRemoteFaceMuteRoomStore})
) )
) )
@ -1081,8 +1249,7 @@
isFirefox: true, isFirefox: true,
localPosterUrl: "sample-img/video-screen-local.png", localPosterUrl: "sample-img/video-screen-local.png",
remotePosterUrl: "sample-img/video-screen-remote.png", remotePosterUrl: "sample-img/video-screen-remote.png",
roomState: ROOM_STATES.HAS_PARTICIPANTS, roomState: ROOM_STATES.HAS_PARTICIPANTS})
screenSharePosterUrl: "sample-img/video-screen-baz.png"})
) )
), ),
@ -1102,8 +1269,7 @@
isFirefox: true, isFirefox: true,
localPosterUrl: "sample-img/video-screen-local.png", localPosterUrl: "sample-img/video-screen-local.png",
remotePosterUrl: "sample-img/video-screen-remote.png", remotePosterUrl: "sample-img/video-screen-remote.png",
roomState: ROOM_STATES.HAS_PARTICIPANTS, roomState: ROOM_STATES.HAS_PARTICIPANTS})
screenSharePosterUrl: "sample-img/video-screen-baz.png"})
) )
), ),
@ -1171,12 +1337,12 @@
cssClass: "standalone", cssClass: "standalone",
dashed: true, dashed: true,
height: 480, height: 480,
onContentsRendered: updatingActiveRoomStore.forcedUpdate, onContentsRendered: updatingMobileActiveRoomStore.forcedUpdate,
summary: "Standalone room conversation (has-participants, 600x480)", summary: "Standalone room conversation (has-participants, 600x480)",
width: 600}, width: 600},
React.createElement("div", {className: "standalone"}, React.createElement("div", {className: "standalone"},
React.createElement(StandaloneRoomView, { React.createElement(StandaloneRoomView, {
activeRoomStore: updatingActiveRoomStore, activeRoomStore: updatingMobileActiveRoomStore,
dispatcher: dispatcher, dispatcher: dispatcher,
isFirefox: true, isFirefox: true,
localPosterUrl: "sample-img/video-screen-local.png", localPosterUrl: "sample-img/video-screen-local.png",
@ -1189,12 +1355,12 @@
cssClass: "standalone", cssClass: "standalone",
dashed: true, dashed: true,
height: 480, height: 480,
onContentsRendered: updatingSharingRoomStore.forcedUpdate, onContentsRendered: updatingSharingRoomMobileStore.forcedUpdate,
summary: "Standalone room convo (has-participants, receivingScreenShare, 600x480)", summary: "Standalone room convo (has-participants, receivingScreenShare, 600x480)",
width: 600}, width: 600},
React.createElement("div", {className: "standalone", cssClass: "standalone"}, React.createElement("div", {className: "standalone", cssClass: "standalone"},
React.createElement(StandaloneRoomView, { React.createElement(StandaloneRoomView, {
activeRoomStore: updatingSharingRoomStore, activeRoomStore: updatingSharingRoomMobileStore,
dispatcher: dispatcher, dispatcher: dispatcher,
isFirefox: true, isFirefox: true,
localPosterUrl: "sample-img/video-screen-local.png", localPosterUrl: "sample-img/video-screen-local.png",
@ -1282,7 +1448,7 @@
// This simulates the mocha layout for errors which means we can run // This simulates the mocha layout for errors which means we can run
// this alongside our other unit tests but use the same harness. // this alongside our other unit tests but use the same harness.
var expectedWarningsCount = 23; var expectedWarningsCount = 18;
var warningsMismatch = caughtWarnings.length !== expectedWarningsCount; var warningsMismatch = caughtWarnings.length !== expectedWarningsCount;
if (uncaughtError || warningsMismatch) { if (uncaughtError || warningsMismatch) {
$("#results").append("<div class='failures'><em>" + $("#results").append("<div class='failures'><em>" +

Просмотреть файл

@ -75,13 +75,25 @@
var dispatcher = new loop.Dispatcher(); 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) { sendTextChatMessage: function(message) {
dispatcher.dispatch(new loop.shared.actions.ReceivedTextChatMessage({ dispatcher.dispatch(new loop.shared.actions.ReceivedTextChatMessage({
message: message.message message: message.message
})); }));
} }
}, Backbone.Events); };
var mockSDK = new MockSDK();
/** /**
* Every view that uses an activeRoomStore needs its own; if they shared * Every view that uses an activeRoomStore needs its own; if they shared
@ -116,7 +128,6 @@
}); });
store.forcedUpdate = function forcedUpdate(contentWindow) { store.forcedUpdate = function forcedUpdate(contentWindow) {
// Since this is called by setTimeout, we don't want to lose any // 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... // exceptions if there's a problem and we need to debug, so...
try { try {
@ -136,6 +147,17 @@
camera: {height: 480, orientation: 0, width: 640} camera: {height: 480, orientation: 0, width: 640}
}, },
remoteVideoEnabled: options.remoteVideoEnabled, 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), matchMedia: contentWindow.matchMedia.bind(contentWindow),
roomState: options.roomState, roomState: options.roomState,
videoMuted: !!options.videoMuted videoMuted: !!options.videoMuted
@ -185,6 +207,10 @@
roomState: ROOM_STATES.HAS_PARTICIPANTS roomState: ROOM_STATES.HAS_PARTICIPANTS
}); });
var updatingMobileActiveRoomStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS
});
var localFaceMuteRoomStore = makeActiveRoomStore({ var localFaceMuteRoomStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS, roomState: ROOM_STATES.HAS_PARTICIPANTS,
videoMuted: true videoMuted: true
@ -201,12 +227,19 @@
receivingScreenShare: true receivingScreenShare: true
}); });
var updatingSharingRoomMobileStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS,
receivingScreenShare: true
});
var loadingRemoteLoadingScreenStore = makeActiveRoomStore({ var loadingRemoteLoadingScreenStore = makeActiveRoomStore({
mediaConnected: false, mediaConnected: false,
receivingScreenShare: true,
roomState: ROOM_STATES.HAS_PARTICIPANTS, roomState: ROOM_STATES.HAS_PARTICIPANTS,
remoteSrcVideoObject: false remoteSrcVideoObject: false
}); });
var loadingScreenSharingRoomStore = makeActiveRoomStore({ var loadingScreenSharingRoomStore = makeActiveRoomStore({
receivingScreenShare: true,
roomState: ROOM_STATES.HAS_PARTICIPANTS roomState: ROOM_STATES.HAS_PARTICIPANTS
}); });
@ -234,7 +267,10 @@
}); });
var invitationRoomStore = new loop.store.RoomStore(dispatcher, { 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, { 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({ var desktopLocalFaceMuteActiveRoomStore = makeActiveRoomStore({
roomState: ROOM_STATES.HAS_PARTICIPANTS, roomState: ROOM_STATES.HAS_PARTICIPANTS,
videoMuted: true videoMuted: true
@ -272,14 +322,58 @@
activeRoomStore: desktopRemoteFaceMuteActiveRoomStore activeRoomStore: desktopRemoteFaceMuteActiveRoomStore
}); });
var conversationStore = new loop.store.ConversationStore(dispatcher, { 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: {}, client: {},
mozLoop: navigator.mozLoop, mozLoop: navigator.mozLoop,
sdkDriver: mockSDK sdkDriver: mockSDK
}); });
var textChatStore = new loop.store.TextChatStore(dispatcher, {
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. // Update the text chat store with the room info.
textChatStore.updateRoomInfo(new sharedActions.UpdateRoomInfo({ textChatStore.updateRoomInfo(new sharedActions.UpdateRoomInfo({
@ -341,7 +435,7 @@
loop.store.StoreMixin.register({ loop.store.StoreMixin.register({
activeRoomStore: activeRoomStore, activeRoomStore: activeRoomStore,
conversationStore: conversationStore, conversationStore: conversationStores[0],
textChatStore: textChatStore textChatStore: textChatStore
}); });
@ -360,14 +454,6 @@
requestCallUrlInfo: noop 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({ var mockWebSocket = new loop.CallConnectionWebSocket({
url: "fake", url: "fake",
callId: "fakeId", callId: "fakeId",
@ -763,7 +849,7 @@
<div className="fx-embedded"> <div className="fx-embedded">
<CallFailedView dispatcher={dispatcher} <CallFailedView dispatcher={dispatcher}
outgoing={false} outgoing={false}
store={conversationStore} /> store={conversationStores[0]} />
</div> </div>
</Example> </Example>
<Example dashed={true} <Example dashed={true}
@ -772,7 +858,7 @@
<div className="fx-embedded"> <div className="fx-embedded">
<CallFailedView dispatcher={dispatcher} <CallFailedView dispatcher={dispatcher}
outgoing={true} outgoing={true}
store={conversationStore} /> store={conversationStores[1]} />
</div> </div>
</Example> </Example>
<Example dashed={true} <Example dashed={true}
@ -781,18 +867,22 @@
<div className="fx-embedded"> <div className="fx-embedded">
<CallFailedView dispatcher={dispatcher} emailLinkError={true} <CallFailedView dispatcher={dispatcher} emailLinkError={true}
outgoing={true} outgoing={true}
store={conversationStore} /> store={conversationStores[0]} />
</div> </div>
</Example> </Example>
</Section> </Section>
<Section name="OngoingConversationView"> <Section name="OngoingConversationView">
<FramedExample height={254} <FramedExample
dashed={true}
height={394}
onContentsRendered={conversationStores[0].forcedUpdate}
summary="Desktop ongoing conversation window" summary="Desktop ongoing conversation window"
width={298}> width={298}>
<div className="fx-embedded"> <div className="fx-embedded">
<OngoingConversationView <OngoingConversationView
audio={{enabled: true}} audio={{enabled: true}}
conversationStore={conversationStores[0]}
dispatcher={dispatcher} dispatcher={dispatcher}
localPosterUrl="sample-img/video-screen-local.png" localPosterUrl="sample-img/video-screen-local.png"
mediaConnected={true} mediaConnected={true}
@ -802,12 +892,34 @@
</div> </div>
</FramedExample> </FramedExample>
<FramedExample height={600} <FramedExample
summary="Desktop ongoing conversation window large" 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={600}
onContentsRendered={conversationStores[2].forcedUpdate}
summary="Desktop ongoing conversation window (large)"
width={800}> width={800}>
<div className="fx-embedded"> <div className="fx-embedded">
<OngoingConversationView <OngoingConversationView
audio={{enabled: true}} audio={{enabled: true}}
conversationStore={conversationStores[2]}
dispatcher={dispatcher} dispatcher={dispatcher}
localPosterUrl="sample-img/video-screen-local.png" localPosterUrl="sample-img/video-screen-local.png"
mediaConnected={true} mediaConnected={true}
@ -817,13 +929,18 @@
</div> </div>
</FramedExample> </FramedExample>
<FramedExample height={254} <FramedExample
dashed={true}
height={394}
onContentsRendered={conversationStores[3].forcedUpdate}
summary="Desktop ongoing conversation window - local face mute" summary="Desktop ongoing conversation window - local face mute"
width={298} > width={298} >
<div className="fx-embedded"> <div className="fx-embedded">
<OngoingConversationView <OngoingConversationView
audio={{enabled: true}} audio={{enabled: true}}
conversationStore={conversationStores[3]}
dispatcher={dispatcher} dispatcher={dispatcher}
localPosterUrl="sample-img/video-screen-local.png"
mediaConnected={true} mediaConnected={true}
remotePosterUrl="sample-img/video-screen-remote.png" remotePosterUrl="sample-img/video-screen-remote.png"
remoteVideoEnabled={true} remoteVideoEnabled={true}
@ -831,15 +948,19 @@
</div> </div>
</FramedExample> </FramedExample>
<FramedExample height={254} <FramedExample
dashed={true} height={394}
onContentsRendered={conversationStores[4].forcedUpdate}
summary="Desktop ongoing conversation window - remote face mute" summary="Desktop ongoing conversation window - remote face mute"
width={298} > width={298} >
<div className="fx-embedded"> <div className="fx-embedded">
<OngoingConversationView <OngoingConversationView
audio={{enabled: true}} audio={{enabled: true}}
conversationStore={conversationStores[4]}
dispatcher={dispatcher} dispatcher={dispatcher}
localPosterUrl="sample-img/video-screen-local.png" localPosterUrl="sample-img/video-screen-local.png"
mediaConnected={true} mediaConnected={true}
remotePosterUrl="sample-img/video-screen-remote.png"
remoteVideoEnabled={false} remoteVideoEnabled={false}
video={{enabled: true}} /> video={{enabled: true}} />
</div> </div>
@ -894,7 +1015,8 @@
<Section name="DesktopRoomConversationView"> <Section name="DesktopRoomConversationView">
<FramedExample <FramedExample
height={254} height={398}
onContentsRendered={invitationRoomStore.activeRoomStore.forcedUpdate}
summary="Desktop room conversation (invitation, text-chat inclusion/scrollbars don't happen in real client)" summary="Desktop room conversation (invitation, text-chat inclusion/scrollbars don't happen in real client)"
width={298}> width={298}>
<div className="fx-embedded"> <div className="fx-embedded">
@ -911,6 +1033,7 @@
<FramedExample <FramedExample
dashed={true} dashed={true}
height={394} height={394}
onContentsRendered={desktopRoomStoreLoading.activeRoomStore.forcedUpdate}
summary="Desktop room conversation (loading)" summary="Desktop room conversation (loading)"
width={298}> width={298}>
{/* Hide scrollbars here. Rotating loading div overflows and causes {/* Hide scrollbars here. Rotating loading div overflows and causes
@ -927,8 +1050,12 @@
</div> </div>
</FramedExample> </FramedExample>
<FramedExample height={254} <FramedExample
summary="Desktop room conversation"> dashed={true}
height={394}
onContentsRendered={roomStore.activeRoomStore.forcedUpdate}
summary="Desktop room conversation"
width={298}>
<div className="fx-embedded"> <div className="fx-embedded">
<DesktopRoomConversationView <DesktopRoomConversationView
dispatcher={dispatcher} dispatcher={dispatcher}
@ -941,8 +1068,46 @@
</div> </div>
</FramedExample> </FramedExample>
<FramedExample dashed={true} <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} height={394}
onContentsRendered={desktopLocalFaceMuteRoomStore.activeRoomStore.forcedUpdate}
summary="Desktop room conversation local face-mute" summary="Desktop room conversation local face-mute"
width={298}> width={298}>
<div className="fx-embedded"> <div className="fx-embedded">
@ -955,7 +1120,9 @@
</div> </div>
</FramedExample> </FramedExample>
<FramedExample dashed={true} height={394} <FramedExample dashed={true}
height={394}
onContentsRendered={desktopRemoteFaceMuteRoomStore.activeRoomStore.forcedUpdate}
summary="Desktop room conversation remote face-mute" summary="Desktop room conversation remote face-mute"
width={298} > width={298} >
<div className="fx-embedded"> <div className="fx-embedded">
@ -964,6 +1131,7 @@
localPosterUrl="sample-img/video-screen-local.png" localPosterUrl="sample-img/video-screen-local.png"
mozLoop={navigator.mozLoop} mozLoop={navigator.mozLoop}
onCallTerminated={function(){}} onCallTerminated={function(){}}
remotePosterUrl="sample-img/video-screen-remote.png"
roomStore={desktopRemoteFaceMuteRoomStore} /> roomStore={desktopRemoteFaceMuteRoomStore} />
</div> </div>
</FramedExample> </FramedExample>
@ -1081,8 +1249,7 @@
isFirefox={true} isFirefox={true}
localPosterUrl="sample-img/video-screen-local.png" localPosterUrl="sample-img/video-screen-local.png"
remotePosterUrl="sample-img/video-screen-remote.png" remotePosterUrl="sample-img/video-screen-remote.png"
roomState={ROOM_STATES.HAS_PARTICIPANTS} roomState={ROOM_STATES.HAS_PARTICIPANTS} />
screenSharePosterUrl="sample-img/video-screen-baz.png" />
</div> </div>
</FramedExample> </FramedExample>
@ -1102,8 +1269,7 @@
isFirefox={true} isFirefox={true}
localPosterUrl="sample-img/video-screen-local.png" localPosterUrl="sample-img/video-screen-local.png"
remotePosterUrl="sample-img/video-screen-remote.png" remotePosterUrl="sample-img/video-screen-remote.png"
roomState={ROOM_STATES.HAS_PARTICIPANTS} roomState={ROOM_STATES.HAS_PARTICIPANTS} />
screenSharePosterUrl="sample-img/video-screen-baz.png" />
</div> </div>
</FramedExample> </FramedExample>
@ -1171,12 +1337,12 @@
cssClass="standalone" cssClass="standalone"
dashed={true} dashed={true}
height={480} height={480}
onContentsRendered={updatingActiveRoomStore.forcedUpdate} onContentsRendered={updatingMobileActiveRoomStore.forcedUpdate}
summary="Standalone room conversation (has-participants, 600x480)" summary="Standalone room conversation (has-participants, 600x480)"
width={600}> width={600}>
<div className="standalone"> <div className="standalone">
<StandaloneRoomView <StandaloneRoomView
activeRoomStore={updatingActiveRoomStore} activeRoomStore={updatingMobileActiveRoomStore}
dispatcher={dispatcher} dispatcher={dispatcher}
isFirefox={true} isFirefox={true}
localPosterUrl="sample-img/video-screen-local.png" localPosterUrl="sample-img/video-screen-local.png"
@ -1189,12 +1355,12 @@
cssClass="standalone" cssClass="standalone"
dashed={true} dashed={true}
height={480} height={480}
onContentsRendered={updatingSharingRoomStore.forcedUpdate} onContentsRendered={updatingSharingRoomMobileStore.forcedUpdate}
summary="Standalone room convo (has-participants, receivingScreenShare, 600x480)" summary="Standalone room convo (has-participants, receivingScreenShare, 600x480)"
width={600} > width={600} >
<div className="standalone" cssClass="standalone"> <div className="standalone" cssClass="standalone">
<StandaloneRoomView <StandaloneRoomView
activeRoomStore={updatingSharingRoomStore} activeRoomStore={updatingSharingRoomMobileStore}
dispatcher={dispatcher} dispatcher={dispatcher}
isFirefox={true} isFirefox={true}
localPosterUrl="sample-img/video-screen-local.png" localPosterUrl="sample-img/video-screen-local.png"
@ -1282,7 +1448,7 @@
// This simulates the mocha layout for errors which means we can run // This simulates the mocha layout for errors which means we can run
// this alongside our other unit tests but use the same harness. // this alongside our other unit tests but use the same harness.
var expectedWarningsCount = 23; var expectedWarningsCount = 18;
var warningsMismatch = caughtWarnings.length !== expectedWarningsCount; var warningsMismatch = caughtWarnings.length !== expectedWarningsCount;
if (uncaughtError || warningsMismatch) { if (uncaughtError || warningsMismatch) {
$("#results").append("<div class='failures'><em>" + $("#results").append("<div class='failures'><em>" +

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -52,6 +52,20 @@ add_task(function*() {
copyRule: false 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", desc: "Test Copy Property Declaration",
node: ruleEditor.rule.textProps[2].editor.nameSpan, node: ruleEditor.rule.textProps[2].editor.nameSpan,
@ -66,6 +80,20 @@ add_task(function*() {
copyRule: false 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", desc: "Test Copy Rule",
node: ruleEditor.rule.textProps[2].editor.nameSpan, node: ruleEditor.rule.textProps[2].editor.nameSpan,
@ -74,6 +102,7 @@ add_task(function*() {
"\tcolor: #F00;[\\r\\n]+" + "\tcolor: #F00;[\\r\\n]+" +
"\tbackground-color: #00F;[\\r\\n]+" + "\tbackground-color: #00F;[\\r\\n]+" +
"\tfont-size: 12px;[\\r\\n]+" + "\tfont-size: 12px;[\\r\\n]+" +
"\tborder-color: #00F !important;[\\r\\n]+" +
"}", "}",
hidden: { hidden: {
copyLocation: true, copyLocation: true,
@ -124,6 +153,7 @@ add_task(function*() {
"\t\/\\* color: #F00; \\*\/[\\r\\n]+" + "\t\/\\* color: #F00; \\*\/[\\r\\n]+" +
"\tbackground-color: #00F;[\\r\\n]+" + "\tbackground-color: #00F;[\\r\\n]+" +
"\tfont-size: 12px;[\\r\\n]+" + "\tfont-size: 12px;[\\r\\n]+" +
"\tborder-color: #00F !important;[\\r\\n]+" +
"}", "}",
hidden: { hidden: {
copyLocation: true, copyLocation: true,

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -97,6 +97,9 @@ quit-button.tooltiptext.mac = Quit %1$S (%2$S)
# approval before you change it. # approval before you change it.
loop-call-button3.label = Hello loop-call-button3.label = Hello
loop-call-button3.tooltiptext = Start a conversation 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.label = Share This Page
social-share-button.tooltiptext = Share this page social-share-button.tooltiptext = Share this page

Просмотреть файл

@ -206,6 +206,8 @@ active_screenshare_button_title=Stop sharing
inactive_screenshare_button_title=Share your screen inactive_screenshare_button_title=Share your screen
share_tabs_button_title2=Share your Tabs share_tabs_button_title2=Share your Tabs
share_windows_button_title=Share other Windows 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 ## LOCALIZATION NOTE (call_with_contact_title): The title displayed
## when calling a contact. Don't translate the part between {{..}} because ## when calling a contact. Don't translate the part between {{..}} because

Просмотреть файл

@ -115,7 +115,7 @@
.titlebar-button { .titlebar-button {
border: none; border: none;
margin: 0 !important; margin: 0 !important;
padding: 12px 17px; padding: 10px 17px;
} }
#main-window[sizemode=maximized] .titlebar-button { #main-window[sizemode=maximized] .titlebar-button {
@ -147,6 +147,20 @@
list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-white); 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 /* 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: */ * 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) { @media (min-resolution: 1.20dppx) and (max-resolution: 1.45dppx) {
@ -222,20 +236,32 @@
background-color: Highlight; background-color: Highlight;
} }
#titlebar-min {
list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-highcontrast);
}
#titlebar-min:hover { #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 { #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 { #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 { #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);
} }
} }
} }

Просмотреть файл

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

Просмотреть файл

@ -9,7 +9,7 @@
fill: none; fill: none;
} }
g:not(#close) { g:not([id|="close"]) {
shape-rendering: crispEdges; shape-rendering: crispEdges;
} }
@ -21,17 +21,36 @@
display: initial; 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; stroke: HighlightText;
} }
[id$="-white"] > g { [id$="-white"] > g {
stroke: #fff; stroke: #fff;
} }
</style> </style>
<g id="close"> <g id="close">
<line x1="1" y1="1" x2="11" y2="11"/> <path d="M1,1 l 10,10 M1,11 l 10,-10"/>
<line x1="11" y1="1" x2="1" y2="11"/>
</g> </g>
<g id="maximize"> <g id="maximize">
<rect x="1.5" y="1.5" width="9" height="9"/> <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"/> <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"/> <polyline points="3.5,3.5 3.5,1.5 10.5,1.5 10.5,8.5 8.5,8.5"/>
</g> </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="close-white" xlink:href="#close"/>
<use id="maximize-white" xlink:href="#maximize"/> <use id="maximize-white" xlink:href="#maximize"/>
<use id="minimize-white" xlink:href="#minimize"/> <use id="minimize-white" xlink:href="#minimize"/>
<use id="restore-white" xlink:href="#restore"/> <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> </svg>

До

Ширина:  |  Высота:  |  Размер: 1.5 KiB

После

Ширина:  |  Высота:  |  Размер: 3.0 KiB

Просмотреть файл

@ -1,6 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public /* 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h" #include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/RecursiveASTVisitor.h"
@ -56,7 +57,6 @@ private:
ScopeChecker(Scope scope_) : ScopeChecker(Scope scope_) :
scope(scope_) {} scope(scope_) {}
virtual void run(const MatchFinder::MatchResult &Result); virtual void run(const MatchFinder::MatchResult &Result);
void noteInferred(QualType T, DiagnosticsEngine &Diag);
private: private:
Scope scope; Scope scope;
}; };
@ -64,7 +64,6 @@ private:
class NonHeapClassChecker : public MatchFinder::MatchCallback { class NonHeapClassChecker : public MatchFinder::MatchCallback {
public: public:
virtual void run(const MatchFinder::MatchResult &Result); virtual void run(const MatchFinder::MatchResult &Result);
void noteInferred(QualType T, DiagnosticsEngine &Diag);
}; };
class ArithmeticArgChecker : public MatchFinder::MatchCallback { class ArithmeticArgChecker : public MatchFinder::MatchCallback {
@ -97,6 +96,16 @@ private:
virtual void run(const MatchFinder::MatchResult &Result); 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 stackClassChecker;
ScopeChecker globalClassChecker; ScopeChecker globalClassChecker;
NonHeapClassChecker nonheapClassChecker; NonHeapClassChecker nonheapClassChecker;
@ -106,6 +115,8 @@ private:
NoAddRefReleaseOnReturnChecker noAddRefReleaseOnReturnChecker; NoAddRefReleaseOnReturnChecker noAddRefReleaseOnReturnChecker;
RefCountedInsideLambdaChecker refCountedInsideLambdaChecker; RefCountedInsideLambdaChecker refCountedInsideLambdaChecker;
ExplicitOperatorBoolChecker explicitOperatorBoolChecker; ExplicitOperatorBoolChecker explicitOperatorBoolChecker;
NeedsNoVTableTypeChecker needsNoVTableTypeChecker;
NonMemMovableChecker nonMemMovableChecker;
MatchFinder astMatcher; 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> { class MozChecker : public ASTConsumer, public RecursiveASTVisitor<MozChecker> {
DiagnosticsEngine &Diag; DiagnosticsEngine &Diag;
const CompilerInstance &CI; const CompilerInstance &CI;
@ -232,29 +288,29 @@ public:
TraverseDecl(ctx.getTranslationUnitDecl()); TraverseDecl(ctx.getTranslationUnitDecl());
} }
static bool hasCustomAnnotation(const Decl *d, const char *spelling) { static bool hasCustomAnnotation(const Decl *D, const char *Spelling) {
AnnotateAttr *attr = d->getAttr<AnnotateAttr>(); iterator_range<specific_attr_iterator<AnnotateAttr> > Attrs =
if (!attr) D->specific_attrs<AnnotateAttr>();
return false;
return attr->getAnnotation() == spelling; for (AnnotateAttr *Attr : Attrs) {
if (Attr->getAnnotation() == Spelling) {
return true;
}
}
return false;
} }
void HandleUnusedExprResult(const Stmt *stmt) { void HandleUnusedExprResult(const Stmt *stmt) {
const Expr* E = dyn_cast_or_null<Expr>(stmt); const Expr* E = dyn_cast_or_null<Expr>(stmt);
if (E) { 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(); QualType T = E->getType();
CXXRecordDecl *decl = T->getAsCXXRecordDecl(); if (MustUse.hasEffectiveAnnotation(T)) {
if (decl) {
decl = decl->getDefinition();
if (decl && hasCustomAnnotation(decl, "moz_must_use")) {
unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID( unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "Unused MOZ_MUST_USE value of type %0"); DiagnosticIDs::Error, "Unused value of must-use type %0");
Diag.Report(E->getLocStart(), errorID) << T; 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. /// A cached data of whether classes are refcounted or not.
typedef DenseMap<const CXXRecordDecl *, typedef DenseMap<const CXXRecordDecl *,
std::pair<const Decl *, bool> > RefCountedMap; std::pair<const Decl *, bool> > RefCountedMap;
@ -527,7 +487,88 @@ bool isClassRefCounted(QualType T) {
while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe()) while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
T = arrTy->getElementType(); T = arrTy->getElementType();
CXXRecordDecl *clazz = T->getAsCXXRecordDecl(); 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> template<class T>
@ -540,6 +581,13 @@ bool IsInSystemHeader(const ASTContext &AC, const T &D) {
return SourceManager.isInSystemHeader(ExpansionLoc); 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 { namespace clang {
@ -548,19 +596,19 @@ namespace ast_matchers {
/// This matcher will match any class with the stack class assertion or an /// This matcher will match any class with the stack class assertion or an
/// array of such classes. /// array of such classes.
AST_MATCHER(QualType, stackClassAggregate) { 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 /// This matcher will match any class with the global class assertion or an
/// array of such classes. /// array of such classes.
AST_MATCHER(QualType, globalClassAggregate) { 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 /// This matcher will match any class with the stack class assertion or an
/// array of such classes. /// array of such classes.
AST_MATCHER(QualType, nonheapClassAggregate) { 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 /// This matcher will match any function declaration that is declared as a heap
@ -694,11 +742,126 @@ AST_POLYMORPHIC_MATCHER_P(equalsBoundNode,
#endif #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 { 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) { bool isPlacementNew(const CXXNewExpr *expr) {
// Regular new expressions aren't placement new // Regular new expressions aren't placement new
if (expr->getNumPlacementArgs() == 0) if (expr->getNumPlacementArgs() == 0)
@ -816,6 +979,18 @@ DiagnosticsMatcher::DiagnosticsMatcher()
astMatcher.addMatcher(methodDecl(anyOf(hasName("operator bool"), astMatcher.addMatcher(methodDecl(anyOf(hasName("operator bool"),
hasName("operator _Bool"))).bind("node"), hasName("operator _Bool"))).bind("node"),
&explicitOperatorBoolChecker); &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( void DiagnosticsMatcher::ScopeChecker::run(
@ -825,7 +1000,9 @@ void DiagnosticsMatcher::ScopeChecker::run(
DiagnosticIDs::Error, "variable of type %0 only valid on the stack"); DiagnosticIDs::Error, "variable of type %0 only valid on the stack");
unsigned globalID = Diag.getDiagnosticIDs()->getCustomDiagID( unsigned globalID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "variable of type %0 only valid as global"); 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 (const VarDecl *d = Result.Nodes.getNodeAs<VarDecl>("node")) {
if (scope == eLocal) { if (scope == eLocal) {
// Ignore the match if it's a local variable. // Ignore the match if it's a local variable.
@ -840,56 +1017,29 @@ void DiagnosticsMatcher::ScopeChecker::run(
return; return;
} }
Diag.Report(d->getLocation(), errorID) << d->getType(); Loc = d->getLocation();
noteInferred(d->getType(), Diag); T = d->getType();
} else if (const CXXNewExpr *expr = } else if (const CXXNewExpr *expr =
Result.Nodes.getNodeAs<CXXNewExpr>("node")) { Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
// If it's placement new, then this match doesn't count. // If it's placement new, then this match doesn't count.
if (scope == eLocal && isPlacementNew(expr)) if (scope == eLocal && isPlacementNew(expr))
return; return;
Diag.Report(expr->getStartLoc(), errorID) << expr->getAllocatedType();
noteInferred(expr->getAllocatedType(), Diag); Loc = expr->getStartLoc();
T = expr->getAllocatedType();
} else if (const CallExpr *expr = } else if (const CallExpr *expr =
Result.Nodes.getNodeAs<CallExpr>("node")) { Result.Nodes.getNodeAs<CallExpr>("node")) {
QualType badType = GetCallReturnType(expr)->getPointeeType(); Loc = expr->getLocStart();
Diag.Report(expr->getLocStart(), errorID) << badType; T = GetCallReturnType(expr)->getPointeeType();
noteInferred(badType, Diag);
}
} }
void DiagnosticsMatcher::ScopeChecker::noteInferred(QualType T, if (scope == eLocal) {
DiagnosticsEngine &Diag) { Diag.Report(Loc, stackID) << T;
unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID( StackClass.dumpAnnotationReason(Diag, T, Loc);
DiagnosticIDs::Note, } else if (scope == eGlobal) {
"%0 is a %2 class because it inherits from a %2 class %1"); Diag.Report(Loc, globalID) << T;
unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID( GlobalClass.dumpAnnotationReason(Diag, T, Loc);
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;
} }
// Recursively follow this back.
noteInferred(cast<ValueDecl>(cause)->getType(), Diag);
} }
void DiagnosticsMatcher::NonHeapClassChecker::run( void DiagnosticsMatcher::NonHeapClassChecker::run(
@ -897,46 +1047,22 @@ void DiagnosticsMatcher::NonHeapClassChecker::run(
DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID( unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Error, "variable of type %0 is not valid on the heap"); 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 (const CXXNewExpr *expr = Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
// If it's placement new, then this match doesn't count. // If it's placement new, then this match doesn't count.
if (isPlacementNew(expr)) if (isPlacementNew(expr))
return; return;
Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType(); Loc = expr->getLocStart();
noteInferred(expr->getAllocatedType(), Diag); T = expr->getAllocatedType();
} else if (const CallExpr *expr = Result.Nodes.getNodeAs<CallExpr>("node")) { } else if (const CallExpr *expr = Result.Nodes.getNodeAs<CallExpr>("node")) {
QualType badType = GetCallReturnType(expr)->getPointeeType(); Loc = expr->getLocStart();
Diag.Report(expr->getLocStart(), stackID) << badType; T = GetCallReturnType(expr)->getPointeeType();
noteInferred(badType, Diag);
}
} }
void DiagnosticsMatcher::NonHeapClassChecker::noteInferred(QualType T, Diag.Report(Loc, stackID) << T;
DiagnosticsEngine &Diag) { NonHeapClass.dumpAnnotationReason(Diag, T, Loc);
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);
} }
void DiagnosticsMatcher::ArithmeticArgChecker::run( 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 { class MozCheckAction : public PluginASTAction {
public: public:
ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI, StringRef fileName) override { ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI, StringRef fileName) override {

Просмотреть файл

@ -16,9 +16,9 @@ void gobble(void *) { }
void misuseGlobalClass(int len) { void misuseGlobalClass(int len) {
Global notValid; // expected-error {{variable of type 'Global' only valid as global}} 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 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(&valid);
gobble(&notValid); gobble(&notValid);
@ -35,7 +35,7 @@ void misuseGlobalClass(int len) {
Global valid; Global valid;
struct RandomClass { 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; static Global staticMember;
}; };
struct MOZ_GLOBAL_CLASS RandomGlobalClass { struct MOZ_GLOBAL_CLASS RandomGlobalClass {
@ -43,7 +43,7 @@ struct MOZ_GLOBAL_CLASS RandomGlobalClass {
static Global staticMember; 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 {}; struct MOZ_GLOBAL_CLASS GoodInherit : Global {};
void misuseGlobalClassEvenMore(int len) { void misuseGlobalClassEvenMore(int len) {

Просмотреть файл

@ -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'}}
}

Просмотреть файл

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

Просмотреть файл

@ -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;
}
}

Просмотреть файл

@ -36,7 +36,7 @@ void misuseNonHeapClass(int len) {
NonHeap validStatic; NonHeap validStatic;
struct RandomClass { 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; static NonHeap staticMember;
}; };
struct MOZ_NONHEAP_CLASS RandomNonHeapClass { struct MOZ_NONHEAP_CLASS RandomNonHeapClass {
@ -44,7 +44,7 @@ struct MOZ_NONHEAP_CLASS RandomNonHeapClass {
static NonHeap staticMember; 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 {}; struct MOZ_NONHEAP_CLASS GoodInherit : NonHeap {};
void useStuffWrongly() { void useStuffWrongly() {
@ -52,11 +52,11 @@ void useStuffWrongly() {
gobble(new RandomClass); // expected-error {{variable of type 'RandomClass' is not valid on the heap}} 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_STACK_CLASS StackClass {};
struct MOZ_NONHEAP_CLASS InferredStackClass : GoodInherit { struct MOZ_NONHEAP_CLASS InferredStackClass : GoodInherit {
NonHeap nonstaticMember; 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}} InferredStackClass global; // expected-error {{variable of type 'InferredStackClass' only valid on the stack}}

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -18,7 +18,7 @@ void misuseStackClass(int len) {
Stack valid; Stack valid;
Stack alsoValid[2]; Stack alsoValid[2];
static Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}} 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(&valid);
gobble(&notValid); gobble(&notValid);
@ -35,7 +35,7 @@ void misuseStackClass(int len) {
Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}} Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}}
struct RandomClass { 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}} static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
}; };
struct MOZ_STACK_CLASS RandomStackClass { 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}} 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 {}; struct MOZ_STACK_CLASS GoodInherit : Stack {};
BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid on the stack}} BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid on the stack}}

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -1710,20 +1710,22 @@ nsDocShell::LoadStream(nsIInputStream* aStream, nsIURI* aURI,
} }
uint32_t loadType = LOAD_NORMAL; uint32_t loadType = LOAD_NORMAL;
nsCOMPtr<nsIPrincipal> requestingPrincipal;
if (aLoadInfo) { if (aLoadInfo) {
nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal; nsDocShellInfoLoadType lt = nsIDocShellLoadInfo::loadNormal;
(void)aLoadInfo->GetLoadType(&lt); (void)aLoadInfo->GetLoadType(&lt);
// Get the appropriate LoadType from nsIDocShellLoadInfo type // Get the appropriate LoadType from nsIDocShellLoadInfo type
loadType = ConvertDocShellLoadInfoToLoadType(lt); 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); NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE);
mLoadType = loadType; mLoadType = loadType;
nsCOMPtr<nsISupports> owner;
aLoadInfo->GetOwner(getter_AddRefs(owner));
nsCOMPtr<nsIPrincipal> requestingPrincipal = do_QueryInterface(owner);
if (!requestingPrincipal) { if (!requestingPrincipal) {
requestingPrincipal = nsContentUtils::GetSystemPrincipal(); requestingPrincipal = nsContentUtils::GetSystemPrincipal();
} }
@ -2953,129 +2955,16 @@ nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue)
nsresult nsresult
nsDocShell::PopProfileTimelineMarkers( nsDocShell::PopProfileTimelineMarkers(
JSContext* aCx, 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 nsTArray<dom::ProfileTimelineMarker> store;
// START marker is found, look for the corresponding END marker and build a SequenceRooter<dom::ProfileTimelineMarker> rooter(aCx, &store);
// {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<mozilla::dom::ProfileTimelineMarker> profileTimelineMarkers; if (IsObserved()) {
SequenceRooter<mozilla::dom::ProfileTimelineMarker> rooter( mObserved->PopMarkers(aCx, store);
aCx, &profileTimelineMarkers);
if (!IsObserved()) {
if (!ToJSValue(aCx, profileTimelineMarkers, aProfileTimelineMarkers)) {
JS_ClearPendingException(aCx);
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
} }
nsTArray<UniquePtr<TimelineMarker>>& markersStore = mObserved.get()->mTimelineMarkers; if (!ToJSValue(aCx, store, aOut)) {
// 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)) {
JS_ClearPendingException(aCx); JS_ClearPendingException(aCx);
return NS_ERROR_UNEXPECTED; return NS_ERROR_UNEXPECTED;
} }

Просмотреть файл

@ -34,4 +34,101 @@ ObservedDocShell::ClearMarkers()
mTimelineMarkers.Clear(); 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 } // namespace mozilla

Просмотреть файл

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

Просмотреть файл

@ -2728,8 +2728,52 @@ Navigator::RequestMediaKeySystemAccess(const nsAString& aKeySystem,
const Optional<Sequence<MediaKeySystemOptions>>& aOptions, const Optional<Sequence<MediaKeySystemOptions>>& aOptions,
ErrorResult& aRv) 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); 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()) { if (aRv.Failed()) {
return nullptr; return nullptr;
} }

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -960,6 +960,25 @@ DOMInterfaces = {
'workers': True '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': { 'Range': {
'nativeType': 'nsRange', 'nativeType': 'nsRange',
'binaryNames': { 'binaryNames': {

Просмотреть файл

@ -12,7 +12,6 @@
#include "mozilla/ipc/BackgroundChild.h" #include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundUtils.h" #include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundChild.h" #include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/Preferences.h"
#include "WorkerPrivate.h" #include "WorkerPrivate.h"
#include "WorkerRunnable.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 } // 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, BroadcastChannel::BroadcastChannel(nsPIDOMWindow* aWindow,
const PrincipalInfo& aPrincipalInfo, const PrincipalInfo& aPrincipalInfo,
const nsACString& aOrigin, const nsACString& aOrigin,

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -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>

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше