Merge m-c to b2ginbound, a=merge
|
@ -88,3 +88,7 @@ GPATH
|
|||
# XCode project cruft
|
||||
^embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/project.xcworkspace/xcuserdata
|
||||
^embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/xcuserdata
|
||||
|
||||
# Ignore mozharness execution files
|
||||
^testing/mozharness/logs/
|
||||
^testing/mozharness/build/
|
||||
|
|
|
@ -204,7 +204,89 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
|||
}
|
||||
|
||||
// Process rendered text change notifications.
|
||||
mTextHash.EnumerateEntries(TextEnumerator, mDocument);
|
||||
for (auto iter = mTextHash.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsCOMPtrHashKey<nsIContent>* entry = iter.Get();
|
||||
nsIContent* textNode = entry->GetKey();
|
||||
Accessible* textAcc = mDocument->GetAccessible(textNode);
|
||||
|
||||
// If the text node is not in tree or doesn't have frame then this case should
|
||||
// have been handled already by content removal notifications.
|
||||
nsINode* containerNode = textNode->GetParentNode();
|
||||
if (!containerNode) {
|
||||
NS_ASSERTION(!textAcc,
|
||||
"Text node was removed but accessible is kept alive!");
|
||||
continue;
|
||||
}
|
||||
|
||||
nsIFrame* textFrame = textNode->GetPrimaryFrame();
|
||||
if (!textFrame) {
|
||||
NS_ASSERTION(!textAcc,
|
||||
"Text node isn't rendered but accessible is kept alive!");
|
||||
continue;
|
||||
}
|
||||
|
||||
nsIContent* containerElm = containerNode->IsElement() ?
|
||||
containerNode->AsElement() : nullptr;
|
||||
|
||||
nsAutoString text;
|
||||
textFrame->GetRenderedText(&text);
|
||||
|
||||
// Remove text accessible if rendered text is empty.
|
||||
if (textAcc) {
|
||||
if (text.IsEmpty()) {
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eTree | logging::eText)) {
|
||||
logging::MsgBegin("TREE", "text node lost its content");
|
||||
logging::Node("container", containerElm);
|
||||
logging::Node("content", textNode);
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
mDocument->ContentRemoved(containerElm, textNode);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update text of the accessible and fire text change events.
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eText)) {
|
||||
logging::MsgBegin("TEXT", "text may be changed");
|
||||
logging::Node("container", containerElm);
|
||||
logging::Node("content", textNode);
|
||||
logging::MsgEntry("old text '%s'",
|
||||
NS_ConvertUTF16toUTF8(textAcc->AsTextLeaf()->Text()).get());
|
||||
logging::MsgEntry("new text: '%s'",
|
||||
NS_ConvertUTF16toUTF8(text).get());
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
TextUpdater::Run(mDocument, textAcc->AsTextLeaf(), text);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Append an accessible if rendered text is not empty.
|
||||
if (!text.IsEmpty()) {
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eTree | logging::eText)) {
|
||||
logging::MsgBegin("TREE", "text node gains new content");
|
||||
logging::Node("container", containerElm);
|
||||
logging::Node("content", textNode);
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Make sure the text node is in accessible document still.
|
||||
Accessible* container = mDocument->GetAccessibleOrContainer(containerNode);
|
||||
NS_ASSERTION(container,
|
||||
"Text node having rendered text hasn't accessible document!");
|
||||
if (container) {
|
||||
nsTArray<nsCOMPtr<nsIContent> > insertedContents;
|
||||
insertedContents.AppendElement(textNode);
|
||||
mDocument->ProcessContentInserted(container, &insertedContents);
|
||||
}
|
||||
}
|
||||
}
|
||||
mTextHash.Clear();
|
||||
|
||||
// Bind hanging child documents.
|
||||
|
@ -314,99 +396,6 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Notification controller: text leaf accessible text update
|
||||
|
||||
PLDHashOperator
|
||||
NotificationController::TextEnumerator(nsCOMPtrHashKey<nsIContent>* aEntry,
|
||||
void* aUserArg)
|
||||
{
|
||||
DocAccessible* document = static_cast<DocAccessible*>(aUserArg);
|
||||
nsIContent* textNode = aEntry->GetKey();
|
||||
Accessible* textAcc = document->GetAccessible(textNode);
|
||||
|
||||
// If the text node is not in tree or doesn't have frame then this case should
|
||||
// have been handled already by content removal notifications.
|
||||
nsINode* containerNode = textNode->GetParentNode();
|
||||
if (!containerNode) {
|
||||
NS_ASSERTION(!textAcc,
|
||||
"Text node was removed but accessible is kept alive!");
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsIFrame* textFrame = textNode->GetPrimaryFrame();
|
||||
if (!textFrame) {
|
||||
NS_ASSERTION(!textAcc,
|
||||
"Text node isn't rendered but accessible is kept alive!");
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsIContent* containerElm = containerNode->IsElement() ?
|
||||
containerNode->AsElement() : nullptr;
|
||||
|
||||
nsAutoString text;
|
||||
textFrame->GetRenderedText(&text);
|
||||
|
||||
// Remove text accessible if rendered text is empty.
|
||||
if (textAcc) {
|
||||
if (text.IsEmpty()) {
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eTree | logging::eText)) {
|
||||
logging::MsgBegin("TREE", "text node lost its content");
|
||||
logging::Node("container", containerElm);
|
||||
logging::Node("content", textNode);
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
document->ContentRemoved(containerElm, textNode);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// Update text of the accessible and fire text change events.
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eText)) {
|
||||
logging::MsgBegin("TEXT", "text may be changed");
|
||||
logging::Node("container", containerElm);
|
||||
logging::Node("content", textNode);
|
||||
logging::MsgEntry("old text '%s'",
|
||||
NS_ConvertUTF16toUTF8(textAcc->AsTextLeaf()->Text()).get());
|
||||
logging::MsgEntry("new text: '%s'",
|
||||
NS_ConvertUTF16toUTF8(text).get());
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
TextUpdater::Run(document, textAcc->AsTextLeaf(), text);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// Append an accessible if rendered text is not empty.
|
||||
if (!text.IsEmpty()) {
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eTree | logging::eText)) {
|
||||
logging::MsgBegin("TREE", "text node gains new content");
|
||||
logging::Node("container", containerElm);
|
||||
logging::Node("content", textNode);
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Make sure the text node is in accessible document still.
|
||||
Accessible* container = document->GetAccessibleOrContainer(containerNode);
|
||||
NS_ASSERTION(container,
|
||||
"Text node having rendered text hasn't accessible document!");
|
||||
if (container) {
|
||||
nsTArray<nsCOMPtr<nsIContent> > insertedContents;
|
||||
insertedContents.AppendElement(textNode);
|
||||
document->ProcessContentInserted(container, &insertedContents);
|
||||
}
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationController: content inserted notification
|
||||
|
||||
|
|
|
@ -297,12 +297,6 @@ private:
|
|||
*/
|
||||
nsTHashtable<nsCOMPtrHashKey<nsIContent> > mTextHash;
|
||||
|
||||
/**
|
||||
* Update the accessible tree for pending rendered text change notifications.
|
||||
*/
|
||||
static PLDHashOperator TextEnumerator(nsCOMPtrHashKey<nsIContent>* aEntry,
|
||||
void* aUserArg);
|
||||
|
||||
/**
|
||||
* Other notifications like DOM events. Don't make this an nsAutoTArray; we
|
||||
* use SwapElements() on it.
|
||||
|
|
|
@ -221,13 +221,6 @@ DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
|
|||
return true;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
DocAccessibleParent::ShutdownAccessibles(ProxyEntry* entry, void*)
|
||||
{
|
||||
ProxyDestroyed(entry->mProxy);
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
bool
|
||||
DocAccessibleParent::RecvShutdown()
|
||||
{
|
||||
|
@ -252,7 +245,10 @@ DocAccessibleParent::Destroy()
|
|||
for (uint32_t i = childDocCount - 1; i < childDocCount; i--)
|
||||
mChildDocs[i]->Destroy();
|
||||
|
||||
mAccessibles.EnumerateEntries(ShutdownAccessibles, nullptr);
|
||||
for (auto iter = mAccessibles.Iter(); !iter.Done(); iter.Next()) {
|
||||
ProxyDestroyed(iter.Get()->mProxy);
|
||||
iter.Remove();
|
||||
}
|
||||
ProxyDestroyed(this);
|
||||
if (mParentDoc)
|
||||
mParentDoc->RemoveChildDoc(this);
|
||||
|
|
|
@ -152,8 +152,6 @@ private:
|
|||
uint32_t aIdxInParent);
|
||||
void CheckDocTree() const;
|
||||
|
||||
static PLDHashOperator ShutdownAccessibles(ProxyEntry* entry, void* unused);
|
||||
|
||||
nsTArray<DocAccessibleParent*> mChildDocs;
|
||||
DocAccessibleParent* mParentDoc;
|
||||
|
||||
|
|
|
@ -8,6 +8,11 @@ let TrackingProtection = {
|
|||
PREF_ENABLED_IN_PRIVATE_WINDOWS: "privacy.trackingprotection.pbmode.enabled",
|
||||
enabledGlobally: false,
|
||||
enabledInPrivateWindows: false,
|
||||
container: null,
|
||||
content: null,
|
||||
icon: null,
|
||||
activeTooltipText: null,
|
||||
disabledTooltipText: null,
|
||||
|
||||
init() {
|
||||
let $ = selector => document.querySelector(selector);
|
||||
|
@ -19,6 +24,11 @@ let TrackingProtection = {
|
|||
Services.prefs.addObserver(this.PREF_ENABLED_GLOBALLY, this, false);
|
||||
Services.prefs.addObserver(this.PREF_ENABLED_IN_PRIVATE_WINDOWS, this, false);
|
||||
|
||||
this.activeTooltipText =
|
||||
gNavigatorBundle.getString("trackingProtection.icon.activeTooltip");
|
||||
this.disabledTooltipText =
|
||||
gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip");
|
||||
|
||||
this.enabledHistogram.add(this.enabledGlobally);
|
||||
},
|
||||
|
||||
|
@ -66,21 +76,14 @@ let TrackingProtection = {
|
|||
this.icon.setAttribute("animate", "true");
|
||||
}
|
||||
|
||||
let {
|
||||
STATE_BLOCKED_TRACKING_CONTENT, STATE_LOADED_TRACKING_CONTENT
|
||||
} = Ci.nsIWebProgressListener;
|
||||
let isBlocking = state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT;
|
||||
let isAllowing = state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT;
|
||||
|
||||
for (let element of [this.icon, this.content]) {
|
||||
if (state & STATE_BLOCKED_TRACKING_CONTENT) {
|
||||
element.setAttribute("state", "blocked-tracking-content");
|
||||
} else if (state & STATE_LOADED_TRACKING_CONTENT) {
|
||||
element.setAttribute("state", "loaded-tracking-content");
|
||||
} else {
|
||||
element.removeAttribute("state");
|
||||
}
|
||||
}
|
||||
if (isBlocking) {
|
||||
this.icon.setAttribute("tooltiptext", this.activeTooltipText);
|
||||
this.icon.setAttribute("state", "blocked-tracking-content");
|
||||
this.content.setAttribute("state", "blocked-tracking-content");
|
||||
|
||||
if (state & STATE_BLOCKED_TRACKING_CONTENT) {
|
||||
// Open the tracking protection introduction panel, if applicable.
|
||||
let introCount = gPrefService.getIntPref("privacy.trackingprotection.introCount");
|
||||
if (introCount < TrackingProtection.MAX_INTROS) {
|
||||
|
@ -88,6 +91,14 @@ let TrackingProtection = {
|
|||
gPrefService.savePrefFile(null);
|
||||
this.showIntroPanel();
|
||||
}
|
||||
} else if (isAllowing) {
|
||||
this.icon.setAttribute("tooltiptext", this.disabledTooltipText);
|
||||
this.icon.setAttribute("state", "loaded-tracking-content");
|
||||
this.content.setAttribute("state", "loaded-tracking-content");
|
||||
} else {
|
||||
this.icon.removeAttribute("tooltiptext");
|
||||
this.icon.removeAttribute("state");
|
||||
this.content.removeAttribute("state");
|
||||
}
|
||||
|
||||
// Telemetry for state change.
|
||||
|
|
|
@ -278,6 +278,8 @@ skip-if = os == "mac" || e10s # bug 967013; e10s: bug 1094761 - test hits the ne
|
|||
[browser_ctrlTab.js]
|
||||
[browser_datareporting_notification.js]
|
||||
skip-if = !datareporting
|
||||
[browser_datachoices_notification.js]
|
||||
skip-if = !datareporting
|
||||
[browser_devedition.js]
|
||||
[browser_devices_get_user_media.js]
|
||||
skip-if = buildapp == 'mulet' || (os == "linux" && debug) || e10s # linux: bug 976544; e10s: bug 1071623
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Pass an empty scope object to the import to prevent "leaked window property"
|
||||
// errors in tests.
|
||||
let Preferences = Cu.import("resource://gre/modules/Preferences.jsm", {}).Preferences;
|
||||
let TelemetryReportingPolicy =
|
||||
Cu.import("resource://gre/modules/TelemetryReportingPolicy.jsm", {}).TelemetryReportingPolicy;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gDatareportingService",
|
||||
() => Cc["@mozilla.org/datareporting/service;1"]
|
||||
.getService(Ci.nsISupports)
|
||||
.wrappedJSObject);
|
||||
|
||||
const PREF_BRANCH = "datareporting.policy.";
|
||||
const PREF_DRS_ENABLED = "datareporting.healthreport.service.enabled";
|
||||
const PREF_BYPASS_NOTIFICATION = PREF_BRANCH + "dataSubmissionPolicyBypassNotification";
|
||||
const PREF_CURRENT_POLICY_VERSION = PREF_BRANCH + "currentPolicyVersion";
|
||||
const PREF_ACCEPTED_POLICY_VERSION = PREF_BRANCH + "dataSubmissionPolicyAcceptedVersion";
|
||||
const PREF_ACCEPTED_POLICY_DATE = PREF_BRANCH + "dataSubmissionPolicyNotifiedTime";
|
||||
|
||||
const TEST_POLICY_VERSION = 37;
|
||||
|
||||
/**
|
||||
* Wait for a tick.
|
||||
*/
|
||||
function promiseNextTick() {
|
||||
return new Promise(resolve => executeSoon(resolve));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a notification to be shown in a notification box.
|
||||
* @param {Object} aNotificationBox The notification box.
|
||||
* @return {Promise} Resolved when the notification is displayed.
|
||||
*/
|
||||
function promiseWaitForAlertActive(aNotificationBox) {
|
||||
let deferred = PromiseUtils.defer();
|
||||
aNotificationBox.addEventListener("AlertActive", function onActive() {
|
||||
aNotificationBox.removeEventListener("AlertActive", onActive, true);
|
||||
deferred.resolve();
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a notification to be closed.
|
||||
* @param {Object} aNotification The notification.
|
||||
* @return {Promise} Resolved when the notification is closed.
|
||||
*/
|
||||
function promiseWaitForNotificationClose(aNotification) {
|
||||
let deferred = PromiseUtils.defer();
|
||||
waitForNotificationClose(aNotification, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
let checkInfobarButton = Task.async(function* (aNotification) {
|
||||
// Check that the button on the data choices infobar does the right thing.
|
||||
let buttons = aNotification.getElementsByTagName("button");
|
||||
Assert.equal(buttons.length, 1, "There is 1 button in the data reporting notification.");
|
||||
let button = buttons[0];
|
||||
|
||||
// Add an observer to ensure the "advanced" pane opened (but don't bother
|
||||
// closing it - we close the entire window when done.)
|
||||
let paneLoadedPromise = promiseTopicObserved("advanced-pane-loaded");
|
||||
|
||||
// Click on the button.
|
||||
button.click();
|
||||
|
||||
// Wait for the preferences panel to open.
|
||||
let preferenceWindow = yield paneLoadedPromise;
|
||||
yield promiseNextTick();
|
||||
// If the prefs are being displayed in a dialog we need to close it.
|
||||
// If in a tab (ie, in-content prefs) it closes with the window.
|
||||
if (!Services.prefs.getBoolPref("browser.preferences.inContent")) {
|
||||
prefWin.close();
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* setup(){
|
||||
const drsEnabled = Preferences.get(PREF_DRS_ENABLED, true);
|
||||
const bypassNotification = Preferences.get(PREF_BYPASS_NOTIFICATION, true);
|
||||
const currentPolicyVersion = Preferences.get(PREF_CURRENT_POLICY_VERSION, 1);
|
||||
|
||||
// Register a cleanup function to reset our preferences.
|
||||
registerCleanupFunction(() => {
|
||||
Preferences.set(PREF_DRS_ENABLED, drsEnabled);
|
||||
Preferences.set(PREF_BYPASS_NOTIFICATION, bypassNotification);
|
||||
Preferences.set(PREF_CURRENT_POLICY_VERSION, currentPolicyVersion);
|
||||
|
||||
// Start polling again.
|
||||
gDatareportingService.policy.startPolling();
|
||||
|
||||
return closeAllNotifications();
|
||||
});
|
||||
|
||||
// Disable Healthreport/Data reporting service.
|
||||
Preferences.set(PREF_DRS_ENABLED, false);
|
||||
// Don't skip the infobar visualisation.
|
||||
Preferences.set(PREF_BYPASS_NOTIFICATION, false);
|
||||
// Set the current policy version.
|
||||
Preferences.set(PREF_CURRENT_POLICY_VERSION, TEST_POLICY_VERSION);
|
||||
|
||||
// Stop the polling to make sure no policy gets displayed by FHR.
|
||||
gDatareportingService.policy.stopPolling();
|
||||
});
|
||||
|
||||
function clearAcceptedPolicy() {
|
||||
// Reset the accepted policy.
|
||||
Preferences.reset(PREF_ACCEPTED_POLICY_VERSION);
|
||||
Preferences.reset(PREF_ACCEPTED_POLICY_DATE);
|
||||
}
|
||||
|
||||
add_task(function* test_single_window(){
|
||||
clearAcceptedPolicy();
|
||||
|
||||
// Close all the notifications, then try to trigger the data choices infobar.
|
||||
yield closeAllNotifications();
|
||||
|
||||
let notificationBox = document.getElementById("global-notificationbox");
|
||||
|
||||
// Make sure that we have a coherent initial state.
|
||||
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_VERSION, 0), 0,
|
||||
"No version should be set on init.");
|
||||
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_DATE, 0), 0,
|
||||
"No date should be set on init.");
|
||||
Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(),
|
||||
"User not notified about datareporting policy.");
|
||||
|
||||
let alertShownPromise = promiseWaitForAlertActive(notificationBox);
|
||||
// This should be false and trigger the Infobar.
|
||||
Assert.ok(!TelemetryReportingPolicy.canUpload(),
|
||||
"User should not be allowed to upload and the infobar should be triggered.");
|
||||
|
||||
// Wait for the infobar to be displayed.
|
||||
yield alertShownPromise;
|
||||
|
||||
Assert.equal(notificationBox.allNotifications.length, 1, "Notification Displayed.");
|
||||
Assert.ok(TelemetryReportingPolicy.canUpload(), "User should be allowed to upload now.");
|
||||
|
||||
yield promiseNextTick();
|
||||
let promiseClosed = promiseWaitForNotificationClose(notificationBox.currentNotification);
|
||||
yield checkInfobarButton(notificationBox.currentNotification);
|
||||
yield promiseClosed;
|
||||
|
||||
Assert.equal(notificationBox.allNotifications.length, 0, "No notifications remain.");
|
||||
|
||||
// Check that we are still clear to upload and that the policy data is saved.
|
||||
Assert.ok(TelemetryReportingPolicy.canUpload());
|
||||
Assert.equal(TelemetryReportingPolicy.testIsUserNotified(), true,
|
||||
"User notified about datareporting policy.");
|
||||
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_VERSION, 0), TEST_POLICY_VERSION,
|
||||
"Version pref set.");
|
||||
Assert.greater(parseInt(Preferences.get(PREF_ACCEPTED_POLICY_DATE, null), 10), -1,
|
||||
"Date pref set.");
|
||||
});
|
||||
|
||||
add_task(function* test_multiple_windows(){
|
||||
clearAcceptedPolicy();
|
||||
|
||||
// Close all the notifications, then try to trigger the data choices infobar.
|
||||
yield closeAllNotifications();
|
||||
|
||||
// Ensure we see the notification on all windows and that action on one window
|
||||
// results in dismiss on every window.
|
||||
let otherWindow = yield BrowserTestUtils.openNewBrowserWindow();
|
||||
|
||||
// Get the notification box for both windows.
|
||||
let notificationBoxes = [
|
||||
document.getElementById("global-notificationbox"),
|
||||
otherWindow.document.getElementById("global-notificationbox")
|
||||
];
|
||||
|
||||
Assert.ok(notificationBoxes[1], "2nd window has a global notification box.");
|
||||
|
||||
// Make sure that we have a coherent initial state.
|
||||
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_VERSION, 0), 0, "No version should be set on init.");
|
||||
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_DATE, 0), 0, "No date should be set on init.");
|
||||
Assert.ok(!TelemetryReportingPolicy.testIsUserNotified(), "User not notified about datareporting policy.");
|
||||
|
||||
let showAlertPromises = [
|
||||
promiseWaitForAlertActive(notificationBoxes[0]),
|
||||
promiseWaitForAlertActive(notificationBoxes[1])
|
||||
];
|
||||
|
||||
// This should be false and trigger the Infobar.
|
||||
Assert.ok(!TelemetryReportingPolicy.canUpload(),
|
||||
"User should not be allowed to upload and the infobar should be triggered.");
|
||||
|
||||
yield Promise.all(showAlertPromises);
|
||||
|
||||
// Both notification were displayed. Close one and check that both gets closed.
|
||||
let closeAlertPromises = [
|
||||
promiseWaitForNotificationClose(notificationBoxes[0].currentNotification),
|
||||
promiseWaitForNotificationClose(notificationBoxes[1].currentNotification)
|
||||
];
|
||||
notificationBoxes[0].currentNotification.close();
|
||||
yield Promise.all(closeAlertPromises);
|
||||
|
||||
// Close the second window we opened.
|
||||
yield BrowserTestUtils.closeWindow(otherWindow);
|
||||
|
||||
// Check that we are clear to upload and that the policy data us saved.
|
||||
Assert.ok(TelemetryReportingPolicy.canUpload(),"User should be allowed to upload now.");
|
||||
Assert.equal(TelemetryReportingPolicy.testIsUserNotified(), true,
|
||||
"User notified about datareporting policy.");
|
||||
Assert.equal(Preferences.get(PREF_ACCEPTED_POLICY_VERSION, 0), TEST_POLICY_VERSION,
|
||||
"Version pref set.");
|
||||
Assert.greater(parseInt(Preferences.get(PREF_ACCEPTED_POLICY_DATE, null), 10), -1,
|
||||
"Date pref set.");
|
||||
});
|
|
@ -47,30 +47,6 @@ function sendNotifyRequest(name) {
|
|||
return [policy, deferred.promise];
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a <notification> to be closed then call the specified callback.
|
||||
*/
|
||||
function waitForNotificationClose(notification, cb) {
|
||||
let parent = notification.parentNode;
|
||||
|
||||
let observer = new MutationObserver(function onMutatations(mutations) {
|
||||
for (let mutation of mutations) {
|
||||
for (let i = 0; i < mutation.removedNodes.length; i++) {
|
||||
let node = mutation.removedNodes.item(i);
|
||||
|
||||
if (node != notification) {
|
||||
continue;
|
||||
}
|
||||
|
||||
observer.disconnect();
|
||||
cb();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(parent, {childList: true});
|
||||
}
|
||||
|
||||
let dumpAppender, rootLogger;
|
||||
|
||||
function test() {
|
||||
|
|
|
@ -46,6 +46,7 @@ function testBenignPage() {
|
|||
ok(!TrackingProtection.container.hidden, "The container is visible");
|
||||
ok(!TrackingProtection.content.hasAttribute("state"), "content: no state");
|
||||
ok(!TrackingProtection.icon.hasAttribute("state"), "icon: no state");
|
||||
ok(!TrackingProtection.icon.hasAttribute("tooltiptext"), "icon: no tooltip");
|
||||
|
||||
ok(hidden("#tracking-protection-icon"), "icon is hidden");
|
||||
ok(hidden("#tracking-action-block"), "blockButton is hidden");
|
||||
|
@ -64,6 +65,8 @@ function testTrackingPage(window) {
|
|||
'content: state="blocked-tracking-content"');
|
||||
is(TrackingProtection.icon.getAttribute("state"), "blocked-tracking-content",
|
||||
'icon: state="blocked-tracking-content"');
|
||||
is(TrackingProtection.icon.getAttribute("tooltiptext"),
|
||||
gNavigatorBundle.getString("trackingProtection.icon.activeTooltip"), "correct tooltip");
|
||||
|
||||
ok(!hidden("#tracking-protection-icon"), "icon is visible");
|
||||
ok(hidden("#tracking-action-block"), "blockButton is hidden");
|
||||
|
@ -90,6 +93,8 @@ function testTrackingPageUnblocked() {
|
|||
'content: state="loaded-tracking-content"');
|
||||
is(TrackingProtection.icon.getAttribute("state"), "loaded-tracking-content",
|
||||
'icon: state="loaded-tracking-content"');
|
||||
is(TrackingProtection.icon.getAttribute("tooltiptext"),
|
||||
gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip"), "correct tooltip");
|
||||
|
||||
ok(!hidden("#tracking-protection-icon"), "icon is visible");
|
||||
ok(!hidden("#tracking-action-block"), "blockButton is visible");
|
||||
|
|
|
@ -9,6 +9,27 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
|
||||
"resource://testing-common/PlacesTestUtils.jsm");
|
||||
|
||||
/**
|
||||
* Wait for a <notification> to be closed then call the specified callback.
|
||||
*/
|
||||
function waitForNotificationClose(notification, cb) {
|
||||
let parent = notification.parentNode;
|
||||
|
||||
let observer = new MutationObserver(function onMutatations(mutations) {
|
||||
for (let mutation of mutations) {
|
||||
for (let i = 0; i < mutation.removedNodes.length; i++) {
|
||||
let node = mutation.removedNodes.item(i);
|
||||
if (node != notification) {
|
||||
continue;
|
||||
}
|
||||
observer.disconnect();
|
||||
cb();
|
||||
}
|
||||
}
|
||||
});
|
||||
observer.observe(parent, {childList: true});
|
||||
}
|
||||
|
||||
function closeAllNotifications () {
|
||||
let notificationBox = document.getElementById("global-notificationbox");
|
||||
|
||||
|
|
Двоичные данные
browser/branding/aurora/content/identity-icons-brand.png
До Ширина: | Высота: | Размер: 1.5 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
Двоичные данные
browser/branding/aurora/content/identity-icons-brand@2x.png
До Ширина: | Высота: | Размер: 3.3 KiB |
|
@ -14,7 +14,6 @@ browser.jar:
|
|||
content/branding/icon16.png (../default16.png)
|
||||
content/branding/icon32.png (../default32.png)
|
||||
content/branding/icon128.png (../mozicon128.png)
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/identity-icons-brand.svg (identity-icons-brand.svg)
|
||||
content/branding/silhouette-40.svg (silhouette-40.svg)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
|
|
Двоичные данные
browser/branding/nightly/content/identity-icons-brand.png
До Ширина: | Высота: | Размер: 1.6 KiB |
После Ширина: | Высота: | Размер: 38 KiB |
Двоичные данные
browser/branding/nightly/content/identity-icons-brand@2x.png
До Ширина: | Высота: | Размер: 4.2 KiB |
|
@ -14,7 +14,6 @@ browser.jar:
|
|||
content/branding/icon16.png (../default16.png)
|
||||
content/branding/icon32.png (../default32.png)
|
||||
content/branding/icon128.png (../mozicon128.png)
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/identity-icons-brand.svg (identity-icons-brand.svg)
|
||||
content/branding/silhouette-40.svg (silhouette-40.svg)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
|
|
Двоичные данные
browser/branding/official/content/identity-icons-brand.png
До Ширина: | Высота: | Размер: 1.3 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
Двоичные данные
browser/branding/official/content/identity-icons-brand@2x.png
До Ширина: | Высота: | Размер: 3.1 KiB |
|
@ -13,7 +13,6 @@ browser.jar:
|
|||
content/branding/icon16.png (../default16.png)
|
||||
content/branding/icon32.png (../default32.png)
|
||||
content/branding/icon128.png (../mozicon128.png)
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/identity-icons-brand.svg (identity-icons-brand.svg)
|
||||
content/branding/silhouette-40.svg (silhouette-40.svg)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
|
|
Двоичные данные
browser/branding/unofficial/content/identity-icons-brand.png
До Ширина: | Высота: | Размер: 1.6 KiB |
После Ширина: | Высота: | Размер: 38 KiB |
Двоичные данные
browser/branding/unofficial/content/identity-icons-brand@2x.png
До Ширина: | Высота: | Размер: 4.2 KiB |
|
@ -14,7 +14,6 @@ browser.jar:
|
|||
content/branding/icon16.png (../default16.png)
|
||||
content/branding/icon32.png (../default32.png)
|
||||
content/branding/icon128.png (../mozicon128.png)
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/identity-icons-brand.svg (identity-icons-brand.svg)
|
||||
content/branding/silhouette-40.svg (silhouette-40.svg)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
|
|
|
@ -638,6 +638,7 @@ loop.roomViews = (function(mozL10n) {
|
|||
return true;
|
||||
|
||||
case ROOM_STATES.READY:
|
||||
case ROOM_STATES.GATHER:
|
||||
case ROOM_STATES.INIT:
|
||||
case ROOM_STATES.JOINING:
|
||||
case ROOM_STATES.SESSION_CONNECTED:
|
||||
|
|
|
@ -638,6 +638,7 @@ loop.roomViews = (function(mozL10n) {
|
|||
return true;
|
||||
|
||||
case ROOM_STATES.READY:
|
||||
case ROOM_STATES.GATHER:
|
||||
case ROOM_STATES.INIT:
|
||||
case ROOM_STATES.JOINING:
|
||||
case ROOM_STATES.SESSION_CONNECTED:
|
||||
|
|
|
@ -372,6 +372,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
return true;
|
||||
|
||||
case ROOM_STATES.READY:
|
||||
case ROOM_STATES.GATHER:
|
||||
case ROOM_STATES.INIT:
|
||||
case ROOM_STATES.JOINING:
|
||||
case ROOM_STATES.SESSION_CONNECTED:
|
||||
|
|
|
@ -372,6 +372,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||
return true;
|
||||
|
||||
case ROOM_STATES.READY:
|
||||
case ROOM_STATES.GATHER:
|
||||
case ROOM_STATES.INIT:
|
||||
case ROOM_STATES.JOINING:
|
||||
case ROOM_STATES.SESSION_CONNECTED:
|
||||
|
|
|
@ -251,18 +251,18 @@ ThreadNode.prototype = {
|
|||
* Uninverts the call tree after its having been built.
|
||||
*/
|
||||
_uninvert: function uninvert() {
|
||||
function mergeOrAddFrameNode(calls, node) {
|
||||
function mergeOrAddFrameNode(calls, node, samples) {
|
||||
// Unlike the inverted call tree, we don't use a root table for the top
|
||||
// level, as in general, there are many fewer entry points than
|
||||
// leaves. Instead, linear search is used regardless of level.
|
||||
for (let i = 0; i < calls.length; i++) {
|
||||
if (calls[i].key === node.key) {
|
||||
let foundNode = calls[i];
|
||||
foundNode._merge(node);
|
||||
foundNode._merge(node, samples);
|
||||
return foundNode.calls;
|
||||
}
|
||||
}
|
||||
let copy = node._clone();
|
||||
let copy = node._clone(samples);
|
||||
calls.push(copy);
|
||||
return copy.calls;
|
||||
}
|
||||
|
@ -280,19 +280,42 @@ ThreadNode.prototype = {
|
|||
|
||||
let node = entry.node;
|
||||
let calls = node.calls;
|
||||
let callSamples = 0;
|
||||
|
||||
if (calls.length === 0) {
|
||||
// We've bottomed out. Reverse the spine and add them to the
|
||||
// uninverted call tree.
|
||||
// Continue the depth-first walk.
|
||||
for (let i = 0; i < calls.length; i++) {
|
||||
workstack.push({ node: calls[i], level: entry.level + 1 });
|
||||
callSamples += calls[i].samples;
|
||||
}
|
||||
|
||||
// The sample delta is used to distinguish stacks.
|
||||
//
|
||||
// Suppose we have the following stack samples:
|
||||
//
|
||||
// A -> B
|
||||
// A -> C
|
||||
// A
|
||||
//
|
||||
// The inverted tree is:
|
||||
//
|
||||
// A
|
||||
// / \
|
||||
// B C
|
||||
//
|
||||
// with A.samples = 3, B.samples = 1, C.samples = 1.
|
||||
//
|
||||
// A is distinguished as being its own stack because
|
||||
// A.samples - (B.samples + C.samples) > 0.
|
||||
//
|
||||
// Note that bottoming out is a degenerate where callSamples = 0.
|
||||
|
||||
let samplesDelta = node.samples - callSamples;
|
||||
if (samplesDelta > 0) {
|
||||
// Reverse the spine and add them to the uninverted call tree.
|
||||
let uninvertedCalls = rootCalls;
|
||||
for (let level = entry.level; level > 0; level--) {
|
||||
let callee = spine[level];
|
||||
uninvertedCalls = mergeOrAddFrameNode(uninvertedCalls, callee.node);
|
||||
}
|
||||
} else {
|
||||
// We still have children. Continue the depth-first walk.
|
||||
for (let i = 0; i < calls.length; i++) {
|
||||
workstack.push({ node: calls[i], level: entry.level + 1 });
|
||||
uninvertedCalls = mergeOrAddFrameNode(uninvertedCalls, callee.node, samplesDelta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -410,19 +433,21 @@ FrameNode.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_clone: function () {
|
||||
_clone: function (samples) {
|
||||
let newNode = new FrameNode(this.key, this, this.isMetaCategory);
|
||||
newNode._merge(this);
|
||||
newNode._merge(this, samples);
|
||||
return newNode;
|
||||
},
|
||||
|
||||
_merge: function (otherNode) {
|
||||
_merge: function (otherNode, samples) {
|
||||
if (this === otherNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.samples += otherNode.samples;
|
||||
this.youngestFrameSamples += otherNode.youngestFrameSamples;
|
||||
this.samples += samples;
|
||||
if (otherNode.youngestFrameSamples > 0) {
|
||||
this.youngestFrameSamples += samples;
|
||||
}
|
||||
|
||||
if (otherNode._optimizations) {
|
||||
let opts = this._optimizations;
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that uninverting the call tree works correctly when there are stacks
|
||||
// in the profile that prefixes of other stacks.
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function () {
|
||||
let { ThreadNode } = devtools.require("devtools/performance/tree-model");
|
||||
let thread = new ThreadNode(gThread, { startTime: 0, endTime: 50 });
|
||||
let root = getFrameNodePath(thread, "(root)");
|
||||
|
||||
/**
|
||||
* Samples
|
||||
*
|
||||
* A->B
|
||||
* C->B
|
||||
* B
|
||||
* A
|
||||
* Z->Y->X
|
||||
* W->Y->X
|
||||
* Y->X
|
||||
*/
|
||||
|
||||
equal(getFrameNodePath(root, "A > B").youngestFrameSamples, 1,
|
||||
"A > B has the correct self count");
|
||||
equal(getFrameNodePath(root, "C > B").youngestFrameSamples, 1,
|
||||
"C > B has the correct self count");
|
||||
equal(getFrameNodePath(root, "B").youngestFrameSamples, 1,
|
||||
"B has the correct self count");
|
||||
equal(getFrameNodePath(root, "A").youngestFrameSamples, 1,
|
||||
"A has the correct self count");
|
||||
equal(getFrameNodePath(root, "Z > Y > X").youngestFrameSamples, 1,
|
||||
"Z > Y > X has the correct self count");
|
||||
equal(getFrameNodePath(root, "W > Y > X").youngestFrameSamples, 1,
|
||||
"W > Y > X has the correct self count");
|
||||
equal(getFrameNodePath(root, "Y > X").youngestFrameSamples, 1,
|
||||
"Y > X has the correct self count");
|
||||
});
|
||||
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A" },
|
||||
{ location: "B" },
|
||||
]
|
||||
}, {
|
||||
time: 10,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "C" },
|
||||
{ location: "B" },
|
||||
]
|
||||
}, {
|
||||
time: 15,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "B" },
|
||||
]
|
||||
},{
|
||||
time: 20,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A" },
|
||||
]
|
||||
}, {
|
||||
time: 21,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "Z" },
|
||||
{ location: "Y" },
|
||||
{ location: "X" },
|
||||
]
|
||||
}, {
|
||||
time: 22,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "W" },
|
||||
{ location: "Y" },
|
||||
{ location: "X" },
|
||||
]
|
||||
}, {
|
||||
time: 23,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "Y" },
|
||||
{ location: "X" },
|
||||
]
|
||||
}]);
|
|
@ -0,0 +1,85 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Like test_tree-model-12, but inverted.
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function () {
|
||||
let { ThreadNode } = devtools.require("devtools/performance/tree-model");
|
||||
let root = new ThreadNode(gThread, { invertTree: true, startTime: 0, endTime: 50 });
|
||||
|
||||
/**
|
||||
* Samples
|
||||
*
|
||||
* A->B
|
||||
* C->B
|
||||
* B
|
||||
* A
|
||||
* Z->Y->X
|
||||
* W->Y->X
|
||||
* Y->X
|
||||
*/
|
||||
|
||||
equal(getFrameNodePath(root, "B").youngestFrameSamples, 3,
|
||||
"B has the correct self count");
|
||||
equal(getFrameNodePath(root, "A").youngestFrameSamples, 1,
|
||||
"A has the correct self count");
|
||||
equal(getFrameNodePath(root, "X").youngestFrameSamples, 3,
|
||||
"X has the correct self count");
|
||||
equal(getFrameNodePath(root, "X > Y").samples, 3,
|
||||
"X > Y has the correct total count");
|
||||
});
|
||||
|
||||
let gThread = synthesizeProfileForTest([{
|
||||
time: 5,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A" },
|
||||
{ location: "B" },
|
||||
]
|
||||
}, {
|
||||
time: 10,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "C" },
|
||||
{ location: "B" },
|
||||
]
|
||||
}, {
|
||||
time: 15,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "B" },
|
||||
]
|
||||
},{
|
||||
time: 20,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "A" },
|
||||
]
|
||||
}, {
|
||||
time: 21,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "Z" },
|
||||
{ location: "Y" },
|
||||
{ location: "X" },
|
||||
]
|
||||
}, {
|
||||
time: 22,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "W" },
|
||||
{ location: "Y" },
|
||||
{ location: "X" },
|
||||
]
|
||||
}, {
|
||||
time: 23,
|
||||
frames: [
|
||||
{ location: "(root)" },
|
||||
{ location: "Y" },
|
||||
{ location: "X" },
|
||||
]
|
||||
}]);
|
|
@ -24,6 +24,8 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
|
|||
[test_tree-model-09.js]
|
||||
[test_tree-model-10.js]
|
||||
[test_tree-model-11.js]
|
||||
[test_tree-model-12.js]
|
||||
[test_tree-model-13.js]
|
||||
[test_waterfall-utils-collapse-01.js]
|
||||
[test_waterfall-utils-collapse-02.js]
|
||||
[test_waterfall-utils-collapse-03.js]
|
||||
|
|
|
@ -343,6 +343,9 @@ trackingProtection.intro.description=When the shield is visible, that means Fire
|
|||
trackingProtection.intro.step1of3=1 of 3
|
||||
trackingProtection.intro.nextButton.label=Next
|
||||
|
||||
trackingProtection.icon.activeTooltip=Tracking attempts blocked
|
||||
trackingProtection.icon.disabledTooltip=Tracking content detected
|
||||
|
||||
# Edit Bookmark UI
|
||||
editBookmarkPanel.pageBookmarkedTitle=Page Bookmarked
|
||||
editBookmarkPanel.pageBookmarkedDescription=%S will always remember this page for you.
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
--toolbarbutton-combined-boxshadow: 0 0 0 1px hsla(0,0%,100%,.2);
|
||||
--toolbarbutton-combined-backgroundimage: linear-gradient(hsla(210,54%,20%,.2) 0, hsla(210,54%,20%,.2) 18px);
|
||||
|
||||
--verified-identity-box-backgroundcolor: #fff;
|
||||
--identity-box-verified-background-color: #fff;
|
||||
}
|
||||
|
||||
#menubar-items {
|
||||
|
@ -986,7 +986,7 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
|
|||
}
|
||||
|
||||
#identity-box.verifiedIdentity:not(:-moz-lwtheme):not(:hover):not([open=true]) {
|
||||
background-color: var(--verified-identity-box-backgroundcolor);
|
||||
background-color: var(--identity-box-verified-background-color);
|
||||
}
|
||||
|
||||
#identity-box:-moz-focusring {
|
||||
|
|
|
@ -174,16 +174,6 @@ panelmultiview[nosubviews=true] > .panel-viewcontainer > .panel-viewstack > .pan
|
|||
flex-direction: column;
|
||||
}
|
||||
|
||||
#app-extension-point-end > #PanelUI-menu-button {
|
||||
padding: 2px 5px;
|
||||
}
|
||||
#app-extension-point-end > #PanelUI-menu-button .toolbarbutton-text {
|
||||
display: none;
|
||||
}
|
||||
#app-extension-point-end > #PanelUI-menu-button .toolbarbutton-icon {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#PanelUI-popup > arrowscrollbox > autorepeatbutton {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
:root[devtoolstheme="dark"] #identity-box {
|
||||
--identity-box-border-color: #5F6670;
|
||||
--identity-box-chrome-color: #46afe3;
|
||||
--verified-identity-box-background-color: transparent;
|
||||
--identity-box-verified-background-color: transparent;
|
||||
--identity-box-selected-background-color: rgba(231,230,230,.2);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
%endif
|
||||
%endif
|
||||
|
||||
border-inline-end: 1px solid;
|
||||
border-inline-end: 1px solid var(--identity-box-border-color);
|
||||
border-image: linear-gradient(transparent 15%,
|
||||
var(--identity-box-border-color) 15%,
|
||||
var(--identity-box-border-color) 85%,
|
||||
|
@ -33,6 +33,7 @@
|
|||
#identity-box:hover,
|
||||
#identity-box[open=true] {
|
||||
background-color: var(--identity-box-selected-background-color);
|
||||
border-image-source: none;
|
||||
}
|
||||
|
||||
#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
|
||||
|
@ -119,8 +120,7 @@
|
|||
}
|
||||
|
||||
.chromeUI > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://branding/content/identity-icons-brand.png);
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
list-style-image: url(chrome://branding/content/identity-icons-brand.svg);
|
||||
}
|
||||
|
||||
.verifiedDomain > #page-proxy-favicon[pageproxystate="valid"],
|
||||
|
@ -146,13 +146,6 @@
|
|||
opacity: 0.3;
|
||||
}
|
||||
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.chromeUI > #page-proxy-favicon[pageproxystate="valid"] {
|
||||
list-style-image: url(chrome://branding/content/identity-icons-brand@2x.png);
|
||||
-moz-image-region: rect(0, 32px, 32px, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#urlbar[actiontype="searchengine"] > #identity-box > #page-proxy-favicon {
|
||||
-moz-image-region: inherit;
|
||||
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
--toolbarbutton-combined-boxshadow: none;
|
||||
--toolbarbutton-combined-backgroundimage: linear-gradient(hsla(210,54%,20%,.2) 0, hsla(210,54%,20%,.2) 16px);
|
||||
|
||||
--verified-identity-box-backgroundcolor: #FFF;
|
||||
--identity-box-verified-background-color: #fff;
|
||||
|
||||
--urlbar-dropmarker-url: url("chrome://browser/skin/urlbar-history-dropmarker.png");
|
||||
--urlbar-dropmarker-region: rect(0px, 11px, 14px, 0px);
|
||||
|
@ -1261,6 +1261,8 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
|
|||
.searchbar-textbox {
|
||||
font-size: 1.15em;
|
||||
min-height: 28px;
|
||||
transition-property: border-color, box-shadow;
|
||||
transition-duration: .1s;
|
||||
}
|
||||
|
||||
:root {
|
||||
|
@ -1435,7 +1437,7 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder,
|
|||
}
|
||||
|
||||
#identity-box.verifiedIdentity:not(:-moz-lwtheme):not(:hover):not([open=true]) {
|
||||
background-color: var(--verified-identity-box-backgroundcolor);
|
||||
background-color: var(--identity-box-verified-background-color);
|
||||
}
|
||||
|
||||
#identity-box:-moz-focusring {
|
||||
|
|
10
configure.in
|
@ -5389,11 +5389,15 @@ if test -n "$MOZ_FMP4"; then
|
|||
MOZ_EME=1
|
||||
fi;
|
||||
|
||||
if test "$MOZ_WIDGET_TOOLKIT" = "gonk" -a -n "$MOZ_FMP4" -a "$ANDROID_VERSION" -ge "18"; then
|
||||
MOZ_GONK_MEDIACODEC=1
|
||||
AC_SUBST(MOZ_GONK_MEDIACODEC)
|
||||
if test x"$MOZ_WIDGET_TOOLKIT" = x"gonk" -a -n "$MOZ_FMP4" -a -n "$ANDROID_VERSION"; then
|
||||
# we now know for sure that $ANDROID_VERSION is not an empty string!
|
||||
if test "$ANDROID_VERSION" -ge "18"; then
|
||||
MOZ_GONK_MEDIACODEC=1
|
||||
AC_SUBST(MOZ_GONK_MEDIACODEC)
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
dnl ========================================================
|
||||
dnl = EME support
|
||||
dnl ========================================================
|
||||
|
|
|
@ -207,6 +207,8 @@ public:
|
|||
virtual
|
||||
~ConsoleRunnable()
|
||||
{
|
||||
// Shutdown the StructuredCloneHelperInternal class.
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -490,15 +490,15 @@ private:
|
|||
return map;
|
||||
}
|
||||
|
||||
static PLDHashOperator SetNodeDirection(nsPtrHashKey<Element>* aEntry, void* aDir)
|
||||
static nsCheapSetOperator SetNodeDirection(nsPtrHashKey<Element>* aEntry, void* aDir)
|
||||
{
|
||||
MOZ_ASSERT(aEntry->GetKey()->IsElement(), "Must be an Element");
|
||||
aEntry->GetKey()->SetDirectionality(*reinterpret_cast<Directionality*>(aDir),
|
||||
true);
|
||||
return PL_DHASH_NEXT;
|
||||
return OpNext;
|
||||
}
|
||||
|
||||
static PLDHashOperator ResetNodeDirection(nsPtrHashKey<Element>* aEntry, void* aData)
|
||||
static nsCheapSetOperator ResetNodeDirection(nsPtrHashKey<Element>* aEntry, void* aData)
|
||||
{
|
||||
MOZ_ASSERT(aEntry->GetKey()->IsElement(), "Must be an Element");
|
||||
// run the downward propagation algorithm
|
||||
|
@ -516,15 +516,15 @@ private:
|
|||
rootNode->ClearHasDirAutoSet();
|
||||
rootNode->UnsetProperty(nsGkAtoms::dirAutoSetBy);
|
||||
}
|
||||
return PL_DHASH_REMOVE;
|
||||
return OpRemove;
|
||||
}
|
||||
|
||||
static PLDHashOperator ClearEntry(nsPtrHashKey<Element>* aEntry, void* aData)
|
||||
static nsCheapSetOperator ClearEntry(nsPtrHashKey<Element>* aEntry, void* aData)
|
||||
{
|
||||
Element* rootNode = aEntry->GetKey();
|
||||
rootNode->ClearHasDirAutoSet();
|
||||
rootNode->UnsetProperty(nsGkAtoms::dirAutoSetBy);
|
||||
return PL_DHASH_REMOVE;
|
||||
return OpRemove;
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
|
@ -23,246 +23,6 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
struct StructuredCloneInfo
|
||||
{
|
||||
PostMessageEvent* event;
|
||||
nsPIDOMWindow* window;
|
||||
|
||||
// This hashtable contains the transferred ports - used to avoid duplicates.
|
||||
nsTArray<nsRefPtr<MessagePortBase>> transferredPorts;
|
||||
|
||||
// This array is populated when the ports are cloned.
|
||||
nsTArray<nsRefPtr<MessagePortBase>> clonedPorts;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
const JSStructuredCloneCallbacks PostMessageEvent::sPostMessageCallbacks = {
|
||||
PostMessageEvent::ReadStructuredClone,
|
||||
PostMessageEvent::WriteStructuredClone,
|
||||
nullptr,
|
||||
PostMessageEvent::ReadTransferStructuredClone,
|
||||
PostMessageEvent::TransferStructuredClone,
|
||||
PostMessageEvent::FreeTransferStructuredClone
|
||||
};
|
||||
|
||||
/* static */ JSObject*
|
||||
PostMessageEvent::ReadStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneReader* reader,
|
||||
uint32_t tag,
|
||||
uint32_t data,
|
||||
void* closure)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
if (tag == SCTAG_DOM_BLOB) {
|
||||
NS_ASSERTION(!data, "Data should be empty");
|
||||
|
||||
// What we get back from the reader is a BlobImpl.
|
||||
// From that we create a new File.
|
||||
BlobImpl* blobImpl;
|
||||
if (JS_ReadBytes(reader, &blobImpl, sizeof(blobImpl))) {
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
// nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
|
||||
// called because the static analysis thinks dereferencing XPCOM objects
|
||||
// can GC (because in some cases it can!), and a return statement with a
|
||||
// JSObject* type means that JSObject* is on the stack as a raw pointer
|
||||
// while destructors are running.
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
{
|
||||
nsRefPtr<Blob> blob = Blob::Create(scInfo->window, blobImpl);
|
||||
if (!ToJSValue(cx, blob, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return &val.toObject();
|
||||
}
|
||||
}
|
||||
|
||||
if (tag == SCTAG_DOM_FILELIST) {
|
||||
NS_ASSERTION(!data, "Data should be empty");
|
||||
|
||||
// What we get back from the reader is a FileListClonedData.
|
||||
// From that we create a new FileList.
|
||||
FileListClonedData* fileListClonedData;
|
||||
if (JS_ReadBytes(reader, &fileListClonedData, sizeof(fileListClonedData))) {
|
||||
MOZ_ASSERT(fileListClonedData);
|
||||
|
||||
// nsRefPtr<FileList> needs to go out of scope before toObjectOrNull() is
|
||||
// called because the static analysis thinks dereferencing XPCOM objects
|
||||
// can GC (because in some cases it can!), and a return statement with a
|
||||
// JSObject* type means that JSObject* is on the stack as a raw pointer
|
||||
// while destructors are running.
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
{
|
||||
nsRefPtr<FileList> fileList =
|
||||
FileList::Create(scInfo->window, fileListClonedData);
|
||||
if (!fileList || !ToJSValue(cx, fileList, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return &val.toObject();
|
||||
}
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks* runtimeCallbacks =
|
||||
js::GetContextStructuredCloneCallbacks(cx);
|
||||
|
||||
if (runtimeCallbacks) {
|
||||
return runtimeCallbacks->read(cx, reader, tag, data, nullptr);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
PostMessageEvent::WriteStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneWriter* writer,
|
||||
JS::Handle<JSObject*> obj,
|
||||
void *closure)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
// See if this is a File/Blob object.
|
||||
{
|
||||
Blob* blob = nullptr;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, obj, blob))) {
|
||||
BlobImpl* blobImpl = blob->Impl();
|
||||
if (JS_WriteUint32Pair(writer, SCTAG_DOM_BLOB, 0) &&
|
||||
JS_WriteBytes(writer, &blobImpl, sizeof(blobImpl))) {
|
||||
scInfo->event->StoreISupports(blobImpl);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See if this is a FileList object.
|
||||
{
|
||||
FileList* fileList = nullptr;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, obj, fileList))) {
|
||||
nsRefPtr<FileListClonedData> fileListClonedData =
|
||||
fileList->CreateClonedData();
|
||||
MOZ_ASSERT(fileListClonedData);
|
||||
FileListClonedData* ptr = fileListClonedData.get();
|
||||
if (JS_WriteUint32Pair(writer, SCTAG_DOM_FILELIST, 0) &&
|
||||
JS_WriteBytes(writer, &ptr, sizeof(ptr))) {
|
||||
scInfo->event->StoreISupports(fileListClonedData);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks* runtimeCallbacks =
|
||||
js::GetContextStructuredCloneCallbacks(cx);
|
||||
|
||||
if (runtimeCallbacks) {
|
||||
return runtimeCallbacks->write(cx, writer, obj, nullptr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
PostMessageEvent::ReadTransferStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneReader* reader,
|
||||
uint32_t tag, void* aData,
|
||||
uint64_t aExtraData,
|
||||
void* aClosure,
|
||||
JS::MutableHandle<JSObject*> returnObject)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
if (tag == SCTAG_DOM_MAP_MESSAGEPORT) {
|
||||
MOZ_ASSERT(!aData);
|
||||
// aExtraData is the index of this port identifier.
|
||||
ErrorResult rv;
|
||||
nsRefPtr<MessagePort> port =
|
||||
MessagePort::Create(scInfo->window,
|
||||
scInfo->event->GetPortIdentifier(aExtraData),
|
||||
rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
scInfo->clonedPorts.AppendElement(port);
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
if (!GetOrCreateDOMReflector(aCx, port, &value)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
returnObject.set(&value.toObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
PostMessageEvent::TransferStructuredClone(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
void* aClosure,
|
||||
uint32_t* aTag,
|
||||
JS::TransferableOwnership* aOwnership,
|
||||
void** aContent,
|
||||
uint64_t* aExtraData)
|
||||
{
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
|
||||
NS_ASSERTION(scInfo, "Must have scInfo!");
|
||||
|
||||
MessagePortBase* port = nullptr;
|
||||
nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (scInfo->transferredPorts.Contains(port)) {
|
||||
// No duplicates.
|
||||
return false;
|
||||
}
|
||||
|
||||
// We use aExtraData to store the index of this new port identifier.
|
||||
MessagePortIdentifier* identifier =
|
||||
scInfo->event->NewPortIdentifier(aExtraData);
|
||||
|
||||
if (!port->CloneAndDisentangle(*identifier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
scInfo->transferredPorts.AppendElement(port);
|
||||
|
||||
*aTag = SCTAG_DOM_MAP_MESSAGEPORT;
|
||||
*aOwnership = JS::SCTAG_TMO_CUSTOM;
|
||||
*aContent = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PostMessageEvent::FreeTransferStructuredClone(uint32_t aTag,
|
||||
JS::TransferableOwnership aOwnership,
|
||||
void *aContent,
|
||||
uint64_t aExtraData,
|
||||
void* aClosure)
|
||||
{
|
||||
if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
|
||||
MOZ_ASSERT(aClosure);
|
||||
MOZ_ASSERT(!aContent);
|
||||
|
||||
StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
|
||||
MessagePort::ForceClose(scInfo->event->GetPortIdentifier(aExtraData));
|
||||
}
|
||||
}
|
||||
|
||||
PostMessageEvent::PostMessageEvent(nsGlobalWindow* aSource,
|
||||
const nsAString& aCallerOrigin,
|
||||
nsGlobalWindow* aTargetWindow,
|
||||
|
@ -282,20 +42,6 @@ PostMessageEvent::~PostMessageEvent()
|
|||
MOZ_COUNT_DTOR(PostMessageEvent);
|
||||
}
|
||||
|
||||
const MessagePortIdentifier&
|
||||
PostMessageEvent::GetPortIdentifier(uint64_t aId)
|
||||
{
|
||||
MOZ_ASSERT(aId < mPortIdentifiers.Length());
|
||||
return mPortIdentifiers[aId];
|
||||
}
|
||||
|
||||
MessagePortIdentifier*
|
||||
PostMessageEvent::NewPortIdentifier(uint64_t* aPosition)
|
||||
{
|
||||
*aPosition = mPortIdentifiers.Length();
|
||||
return mPortIdentifiers.AppendElement();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PostMessageEvent::Run()
|
||||
{
|
||||
|
@ -347,13 +93,9 @@ PostMessageEvent::Run()
|
|||
}
|
||||
}
|
||||
|
||||
// Deserialize the structured clone data
|
||||
JS::Rooted<JS::Value> messageData(cx);
|
||||
StructuredCloneInfo scInfo;
|
||||
scInfo.event = this;
|
||||
scInfo.window = targetWindow;
|
||||
|
||||
if (!mBuffer.read(cx, &messageData, &sPostMessageCallbacks, &scInfo)) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = targetWindow.get();
|
||||
if (!Read(window, cx, &messageData)) {
|
||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||
}
|
||||
|
||||
|
@ -368,7 +110,7 @@ PostMessageEvent::Run()
|
|||
EmptyString(), mSource);
|
||||
|
||||
event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()),
|
||||
scInfo.clonedPorts));
|
||||
GetTransferredPorts()));
|
||||
|
||||
// We can't simply call dispatchEvent on the window because doing so ends
|
||||
// up flipping the trusted bit on the event, and we don't want that to
|
||||
|
@ -392,19 +134,5 @@ PostMessageEvent::Run()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
PostMessageEvent::Write(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
JS::Handle<JS::Value> aTransfer, nsPIDOMWindow* aWindow)
|
||||
{
|
||||
// We *must* clone the data here, or the JS::Value could be modified
|
||||
// by script
|
||||
StructuredCloneInfo scInfo;
|
||||
scInfo.event = this;
|
||||
scInfo.window = aWindow;
|
||||
|
||||
return mBuffer.write(aCx, aMessage, aTransfer, &sPostMessageCallbacks,
|
||||
&scInfo);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#ifndef mozilla_dom_PostMessageEvent_h
|
||||
#define mozilla_dom_PostMessageEvent_h
|
||||
|
||||
#include "js/StructuredClone.h"
|
||||
#include "mozilla/dom/StructuredCloneHelper.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsRefPtr.h"
|
||||
#include "nsTArray.h"
|
||||
|
@ -28,6 +28,7 @@ class MessagePortIdentifier;
|
|||
* which asynchronously creates and dispatches events.
|
||||
*/
|
||||
class PostMessageEvent final : public nsRunnable
|
||||
, public StructuredCloneHelper
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSIRUNNABLE
|
||||
|
@ -38,69 +39,14 @@ public:
|
|||
nsIPrincipal* aProvidedPrincipal,
|
||||
bool aTrustedCaller);
|
||||
|
||||
bool Write(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
JS::Handle<JS::Value> aTransfer, nsPIDOMWindow* aWindow);
|
||||
|
||||
private:
|
||||
~PostMessageEvent();
|
||||
|
||||
const MessagePortIdentifier& GetPortIdentifier(uint64_t aId);
|
||||
|
||||
MessagePortIdentifier* NewPortIdentifier(uint64_t* aPosition);
|
||||
|
||||
bool StoreISupports(nsISupports* aSupports)
|
||||
{
|
||||
mSupportsArray.AppendElement(aSupports);
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
ReadStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneReader* reader,
|
||||
uint32_t tag,
|
||||
uint32_t data,
|
||||
void* closure);
|
||||
|
||||
static bool
|
||||
WriteStructuredClone(JSContext* cx,
|
||||
JSStructuredCloneWriter* writer,
|
||||
JS::Handle<JSObject*> obj,
|
||||
void *closure);
|
||||
|
||||
static bool
|
||||
ReadTransferStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneReader* reader,
|
||||
uint32_t tag, void* aData,
|
||||
uint64_t aExtraData,
|
||||
void* aClosure,
|
||||
JS::MutableHandle<JSObject*> returnObject);
|
||||
|
||||
static bool
|
||||
TransferStructuredClone(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
void* aClosure,
|
||||
uint32_t* aTag,
|
||||
JS::TransferableOwnership* aOwnership,
|
||||
void** aContent,
|
||||
uint64_t* aExtraData);
|
||||
|
||||
static void
|
||||
FreeTransferStructuredClone(uint32_t aTag,
|
||||
JS::TransferableOwnership aOwnership,
|
||||
void *aContent,
|
||||
uint64_t aExtraData,
|
||||
void* aClosure);
|
||||
|
||||
static const JSStructuredCloneCallbacks sPostMessageCallbacks;
|
||||
|
||||
JSAutoStructuredCloneBuffer mBuffer;
|
||||
nsRefPtr<nsGlobalWindow> mSource;
|
||||
nsString mCallerOrigin;
|
||||
nsRefPtr<nsGlobalWindow> mTargetWindow;
|
||||
nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
|
||||
bool mTrustedCaller;
|
||||
nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
|
||||
nsTArray<MessagePortIdentifier> mPortIdentifiers;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
|
||||
#include "StructuredCloneHelper.h"
|
||||
|
||||
#include "mozilla/dom/BlobBinding.h"
|
||||
#include "mozilla/dom/FileListBinding.h"
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -85,7 +89,7 @@ void
|
|||
StructuredCloneCallbacksError(JSContext* aCx,
|
||||
uint32_t aErrorId)
|
||||
{
|
||||
NS_WARNING("Failed to clone data for the Console API in workers.");
|
||||
NS_WARNING("Failed to clone data.");
|
||||
}
|
||||
|
||||
const JSStructuredCloneCallbacks gCallbacks = {
|
||||
|
@ -99,21 +103,61 @@ const JSStructuredCloneCallbacks gCallbacks = {
|
|||
|
||||
} // anonymous namespace
|
||||
|
||||
// StructuredCloneHelperInternal class
|
||||
|
||||
StructuredCloneHelperInternal::StructuredCloneHelperInternal()
|
||||
#ifdef DEBUG
|
||||
: mShutdownCalled(false)
|
||||
#endif
|
||||
{}
|
||||
|
||||
StructuredCloneHelperInternal::~StructuredCloneHelperInternal()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(mShutdownCalled);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
StructuredCloneHelperInternal::Shutdown()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(!mShutdownCalled, "Shutdown already called!");
|
||||
mShutdownCalled = true;
|
||||
#endif
|
||||
|
||||
mBuffer = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
StructuredCloneHelperInternal::Write(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
MOZ_ASSERT(!mBuffer, "Double Write is not allowed");
|
||||
MOZ_ASSERT(!mShutdownCalled, "This method cannot be called after Shutdown.");
|
||||
|
||||
mBuffer = new JSAutoStructuredCloneBuffer(&gCallbacks, this);
|
||||
return mBuffer->write(aCx, aValue, &gCallbacks, this);
|
||||
}
|
||||
|
||||
bool
|
||||
StructuredCloneHelperInternal::Write(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
JS::Handle<JS::Value> aTransfer)
|
||||
{
|
||||
MOZ_ASSERT(!mBuffer, "Double Write is not allowed");
|
||||
MOZ_ASSERT(!mShutdownCalled, "This method cannot be called after Shutdown.");
|
||||
|
||||
mBuffer = new JSAutoStructuredCloneBuffer(&gCallbacks, this);
|
||||
return mBuffer->write(aCx, aValue, aTransfer, &gCallbacks, this);
|
||||
}
|
||||
|
||||
bool
|
||||
StructuredCloneHelperInternal::Read(JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aValue)
|
||||
{
|
||||
MOZ_ASSERT(mBuffer, "Read() without Write() is not allowed.");
|
||||
MOZ_ASSERT(!mShutdownCalled, "This method cannot be called after Shutdown.");
|
||||
|
||||
bool ok = mBuffer->read(aCx, aValue, &gCallbacks, this);
|
||||
mBuffer = nullptr;
|
||||
|
@ -132,7 +176,6 @@ StructuredCloneHelperInternal::ReadTransferCallback(JSContext* aCx,
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
StructuredCloneHelperInternal::WriteTransferCallback(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
|
@ -154,5 +197,222 @@ StructuredCloneHelperInternal::FreeTransferCallback(uint32_t aTag,
|
|||
MOZ_CRASH("Nothing to free.");
|
||||
}
|
||||
|
||||
// StructuredCloneHelper class
|
||||
|
||||
StructuredCloneHelper::StructuredCloneHelper(uint32_t aFlags)
|
||||
: mFlags(aFlags)
|
||||
, mParent(nullptr)
|
||||
{}
|
||||
|
||||
StructuredCloneHelper::~StructuredCloneHelper()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
bool
|
||||
StructuredCloneHelper::Write(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
JS::Handle<JS::Value> aTransfer)
|
||||
{
|
||||
bool ok = StructuredCloneHelperInternal::Write(aCx, aValue, aTransfer);
|
||||
mTransferringPort.Clear();
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
StructuredCloneHelper::Read(nsISupports* aParent,
|
||||
JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aValue)
|
||||
{
|
||||
mozilla::AutoRestore<nsISupports*> guard(mParent);
|
||||
mParent = aParent;
|
||||
|
||||
return StructuredCloneHelperInternal::Read(aCx, aValue);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
StructuredCloneHelper::ReadCallback(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
uint32_t aTag,
|
||||
uint32_t aIndex)
|
||||
{
|
||||
if (aTag == SCTAG_DOM_BLOB) {
|
||||
MOZ_ASSERT(!(mFlags & eBlobNotSupported));
|
||||
|
||||
BlobImpl* blobImpl;
|
||||
if (JS_ReadBytes(aReader, &blobImpl, sizeof(blobImpl))) {
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
||||
// nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
|
||||
// called because the static analysis thinks dereferencing XPCOM objects
|
||||
// can GC (because in some cases it can!), and a return statement with a
|
||||
// JSObject* type means that JSObject* is on the stack as a raw pointer
|
||||
// while destructors are running.
|
||||
JS::Rooted<JS::Value> val(aCx);
|
||||
{
|
||||
nsRefPtr<Blob> blob = Blob::Create(mParent, blobImpl);
|
||||
if (!ToJSValue(aCx, blob, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return &val.toObject();
|
||||
}
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_FILELIST) {
|
||||
MOZ_ASSERT(!(mFlags & eFileListNotSupported));
|
||||
|
||||
FileListClonedData* fileListClonedData;
|
||||
if (JS_ReadBytes(aReader, &fileListClonedData,
|
||||
sizeof(fileListClonedData))) {
|
||||
MOZ_ASSERT(fileListClonedData);
|
||||
|
||||
// nsRefPtr<FileList> needs to go out of scope before toObjectOrNull() is
|
||||
// called because the static analysis thinks dereferencing XPCOM objects
|
||||
// can GC (because in some cases it can!), and a return statement with a
|
||||
// JSObject* type means that JSObject* is on the stack as a raw pointer
|
||||
// while destructors are running.
|
||||
JS::Rooted<JS::Value> val(aCx);
|
||||
{
|
||||
nsRefPtr<FileList> fileList =
|
||||
FileList::Create(mParent, fileListClonedData);
|
||||
if (!fileList || !ToJSValue(aCx, fileList, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return &val.toObject();
|
||||
}
|
||||
}
|
||||
|
||||
return NS_DOMReadStructuredClone(aCx, aReader, aTag, aIndex, nullptr);
|
||||
}
|
||||
|
||||
bool
|
||||
StructuredCloneHelper::WriteCallback(JSContext* aCx,
|
||||
JSStructuredCloneWriter* aWriter,
|
||||
JS::Handle<JSObject*> aObj)
|
||||
{
|
||||
// See if this is a File/Blob object.
|
||||
if (!(mFlags & eBlobNotSupported)) {
|
||||
Blob* blob = nullptr;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
|
||||
BlobImpl* blobImpl = blob->Impl();
|
||||
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB, 0) &&
|
||||
JS_WriteBytes(aWriter, &blobImpl, sizeof(blobImpl)) &&
|
||||
StoreISupports(blobImpl);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(mFlags & eFileListNotSupported)) {
|
||||
FileList* fileList = nullptr;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList))) {
|
||||
nsRefPtr<FileListClonedData> fileListClonedData =
|
||||
fileList->CreateClonedData();
|
||||
MOZ_ASSERT(fileListClonedData);
|
||||
FileListClonedData* ptr = fileListClonedData.get();
|
||||
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST, 0) &&
|
||||
JS_WriteBytes(aWriter, &ptr, sizeof(ptr)) &&
|
||||
StoreISupports(fileListClonedData);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr);
|
||||
}
|
||||
|
||||
bool
|
||||
StructuredCloneHelper::ReadTransferCallback(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
uint32_t aTag,
|
||||
void* aContent,
|
||||
uint64_t aExtraData,
|
||||
JS::MutableHandleObject aReturnObject)
|
||||
{
|
||||
if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
|
||||
MOZ_ASSERT(!(mFlags & eMessagePortNotSupported));
|
||||
|
||||
// This can be null.
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mParent);
|
||||
|
||||
MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
|
||||
const MessagePortIdentifier& portIdentifier = mPortIdentifiers[aExtraData];
|
||||
|
||||
// aExtraData is the index of this port identifier.
|
||||
ErrorResult rv;
|
||||
nsRefPtr<MessagePort> port =
|
||||
MessagePort::Create(window, portIdentifier, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mTransferredPorts.AppendElement(port);
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
if (!GetOrCreateDOMReflector(aCx, port, &value)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
aReturnObject.set(&value.toObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
StructuredCloneHelper::WriteTransferCallback(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
uint32_t* aTag,
|
||||
JS::TransferableOwnership* aOwnership,
|
||||
void** aContent,
|
||||
uint64_t* aExtraData)
|
||||
{
|
||||
if (!(mFlags & eMessagePortNotSupported)) {
|
||||
MessagePortBase* port = nullptr;
|
||||
nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (mTransferringPort.Contains(port)) {
|
||||
// No duplicates.
|
||||
return false;
|
||||
}
|
||||
|
||||
// We use aExtraData to store the index of this new port identifier.
|
||||
*aExtraData = mPortIdentifiers.Length();
|
||||
MessagePortIdentifier* identifier = mPortIdentifiers.AppendElement();
|
||||
|
||||
if (!port->CloneAndDisentangle(*identifier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mTransferringPort.AppendElement(port);
|
||||
|
||||
*aTag = SCTAG_DOM_MAP_MESSAGEPORT;
|
||||
*aOwnership = JS::SCTAG_TMO_CUSTOM;
|
||||
*aContent = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
StructuredCloneHelper::FreeTransferCallback(uint32_t aTag,
|
||||
JS::TransferableOwnership aOwnership,
|
||||
void* aContent,
|
||||
uint64_t aExtraData)
|
||||
{
|
||||
if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
|
||||
MOZ_ASSERT(!(mFlags & eMessagePortNotSupported));
|
||||
MOZ_ASSERT(!aContent);
|
||||
MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
|
||||
MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
|
||||
}
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include "js/StructuredClone.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -15,6 +17,9 @@ namespace dom {
|
|||
class StructuredCloneHelperInternal
|
||||
{
|
||||
public:
|
||||
StructuredCloneHelperInternal();
|
||||
virtual ~StructuredCloneHelperInternal();
|
||||
|
||||
// These methods should be implemented in order to clone data.
|
||||
// Read more documentation in js/public/StructuredClone.h.
|
||||
|
||||
|
@ -27,6 +32,12 @@ public:
|
|||
JSStructuredCloneWriter* aWriter,
|
||||
JS::Handle<JSObject*> aObj) = 0;
|
||||
|
||||
// This method has to be called when this object is not needed anymore.
|
||||
// It will free memory and the buffer. This has to be called because
|
||||
// otherwise the buffer will be freed in the DTOR of this class and at that
|
||||
// point we cannot use the overridden methods.
|
||||
void Shutdown();
|
||||
|
||||
// If these 3 methods are not implement, transfering objects will not be
|
||||
// allowed.
|
||||
|
||||
|
@ -67,6 +78,113 @@ public:
|
|||
|
||||
protected:
|
||||
nsAutoPtr<JSAutoStructuredCloneBuffer> mBuffer;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mShutdownCalled;
|
||||
#endif
|
||||
};
|
||||
|
||||
class MessagePortBase;
|
||||
class MessagePortIdentifier;
|
||||
|
||||
class StructuredCloneHelper : public StructuredCloneHelperInternal
|
||||
{
|
||||
public:
|
||||
enum StructuredCloneHelperFlags {
|
||||
eAll = 0,
|
||||
|
||||
// Disable the cloning of blobs. If a blob is part of the cloning value,
|
||||
// an exception will be thrown.
|
||||
eBlobNotSupported = 1 << 0,
|
||||
|
||||
// Disable the cloning of FileLists. If a FileList is part of the cloning
|
||||
// value, an exception will be thrown.
|
||||
eFileListNotSupported = 1 << 1,
|
||||
|
||||
// MessagePort can just be transfered. Using this flag we do not support
|
||||
// the transfering.
|
||||
eMessagePortNotSupported = 1 << 2,
|
||||
};
|
||||
|
||||
// aFlags is a bitmap of StructuredCloneHelperFlags.
|
||||
explicit StructuredCloneHelper(uint32_t aFlags = eAll);
|
||||
virtual ~StructuredCloneHelper();
|
||||
|
||||
bool Write(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
JS::Handle<JS::Value> aTransfer);
|
||||
|
||||
bool Read(nsISupports* aParent,
|
||||
JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aValue);
|
||||
|
||||
nsTArray<nsRefPtr<MessagePortBase>>& GetTransferredPorts()
|
||||
{
|
||||
MOZ_ASSERT(!(mFlags & eMessagePortNotSupported));
|
||||
return mTransferredPorts;
|
||||
}
|
||||
|
||||
// Custom Callbacks
|
||||
|
||||
virtual JSObject* ReadCallback(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
uint32_t aTag,
|
||||
uint32_t aIndex) override;
|
||||
|
||||
virtual bool WriteCallback(JSContext* aCx,
|
||||
JSStructuredCloneWriter* aWriter,
|
||||
JS::Handle<JSObject*> aObj) override;
|
||||
|
||||
virtual bool ReadTransferCallback(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
uint32_t aTag,
|
||||
void* aContent,
|
||||
uint64_t aExtraData,
|
||||
JS::MutableHandleObject aReturnObject) override;
|
||||
|
||||
virtual bool WriteTransferCallback(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
uint32_t* aTag,
|
||||
JS::TransferableOwnership* aOwnership,
|
||||
void** aContent,
|
||||
uint64_t* aExtraData) override;
|
||||
|
||||
virtual void FreeTransferCallback(uint32_t aTag,
|
||||
JS::TransferableOwnership aOwnership,
|
||||
void* aContent,
|
||||
uint64_t aExtraData) override;
|
||||
private:
|
||||
bool StoreISupports(nsISupports* aSupports)
|
||||
{
|
||||
MOZ_ASSERT(aSupports);
|
||||
mSupportsArray.AppendElement(aSupports);
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is our bitmap.
|
||||
uint32_t mFlags;
|
||||
|
||||
// Useful for the structured clone algorithm:
|
||||
|
||||
nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
|
||||
|
||||
// This raw pointer is set and unset into the ::Read(). It's always null
|
||||
// outside that method. For this reason it's a raw pointer.
|
||||
nsISupports* MOZ_NON_OWNING_REF mParent;
|
||||
|
||||
// This hashtable contains the ports while doing write (transferring and
|
||||
// mapping transferred objects to the objects in the clone). It's an empty
|
||||
// array outside the 'Write()' method.
|
||||
nsTArray<nsRefPtr<MessagePortBase>> mTransferringPort;
|
||||
|
||||
// This array contains the ports once we've finished the reading. It's
|
||||
// generated from the mPortIdentifiers array.
|
||||
nsTArray<nsRefPtr<MessagePortBase>> mTransferredPorts;
|
||||
|
||||
// This array contains the identifiers of the MessagePorts. Based on these we
|
||||
// are able to reconnect the new transferred ports with the other
|
||||
// MessageChannel ports.
|
||||
nsTArray<MessagePortIdentifier> mPortIdentifiers;
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
|
|
|
@ -8582,7 +8582,7 @@ nsGlobalWindow::PostMessageMozOuter(JSContext* aCx, JS::Handle<JS::Value> aMessa
|
|||
JS::Rooted<JS::Value> message(aCx, aMessage);
|
||||
JS::Rooted<JS::Value> transfer(aCx, aTransfer);
|
||||
|
||||
if (!event->Write(aCx, message, transfer, this)) {
|
||||
if (!event->Write(aCx, message, transfer)) {
|
||||
aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -802,5 +802,6 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g'
|
|||
[test_file_negative_date.html]
|
||||
[test_nonascii_blob_url.html]
|
||||
[test_window_element_enumeration.html]
|
||||
[test_referrer_redirect.html]
|
||||
[test_cloning_fileList.html]
|
||||
support-files = script_cloning_fileList.js iframe_cloning_fileList.html
|
||||
|
|
|
@ -1,33 +1,51 @@
|
|||
/*
|
||||
* Test server for iframe, anchor, and area referrer attributes.
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=1175736
|
||||
* Also server for further referrer tests such as redirecting tests
|
||||
* bug 1174913, bug 1175736, bug 1184781
|
||||
*/
|
||||
|
||||
Components.utils.importGlobalProperties(["URLSearchParams"]);
|
||||
const BASE_URL = 'example.com/tests/dom/base/test/referrer_testserver.sjs';
|
||||
const SHARED_KEY = 'referrer_testserver.sjs';
|
||||
const SJS = "referrer_testserver.sjs?";
|
||||
const BASE_URL = "example.com/tests/dom/base/test/" + SJS;
|
||||
const SHARED_KEY = SJS;
|
||||
const SAME_ORIGIN = "mochi.test:8888/tests/dom/base/test/" + SJS;
|
||||
const CROSS_ORIGIN = "test1.example.com/tests/dom/base/test/" + SJS;
|
||||
|
||||
const IMG_BYTES = atob(
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
|
||||
"P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==");
|
||||
|
||||
function createTestUrl(aPolicy, aAction, aName, aType) {
|
||||
return 'http://' + BASE_URL + '?' +
|
||||
'ACTION=' + aAction + '&' +
|
||||
'policy=' + aPolicy + '&' +
|
||||
'name=' + aName + '&' +
|
||||
'type=' + aType;
|
||||
return "http://" + BASE_URL +
|
||||
"ACTION=" + aAction + "&" +
|
||||
"policy=" + aPolicy + "&" +
|
||||
"NAME=" + aName + "&" +
|
||||
"type=" + aType;
|
||||
}
|
||||
|
||||
// test page using iframe referrer attribute
|
||||
function createIframeTestPageUsingRefferer(aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aChangingMethod) {
|
||||
// if aParams are set this creates a test where the iframe url is a redirect
|
||||
function createIframeTestPageUsingRefferer(aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aParams, aChangingMethod) {
|
||||
var metaString = "";
|
||||
if (aMetaPolicy) {
|
||||
metaString = '<meta name="referrer" content="' + aMetaPolicy + '">';
|
||||
metaString = `<meta name="referrer" content="${aMetaPolicy}">`;
|
||||
}
|
||||
var changeString = '';
|
||||
if (aChangingMethod === 'setAttribute') {
|
||||
var changeString = "";
|
||||
if (aChangingMethod === "setAttribute") {
|
||||
changeString = `document.getElementById("myframe").setAttribute("referrer", "${aNewAttributePolicy}")`;
|
||||
} else if (aChangingMethod === 'property') {
|
||||
} else if (aChangingMethod === "property") {
|
||||
changeString = `document.getElementById("myframe").referrer = "${aNewAttributePolicy}"`;
|
||||
}
|
||||
var iFrameString = `<iframe src="" id="myframe" ${aAttributePolicy ? ` referrer='${aAttributePolicy}'` : ""}>iframe</iframe>`;
|
||||
var iFrameString = `<iframe src="" id="myframe" ${aAttributePolicy ? ` referrer="${aAttributePolicy}"` : ""}>iframe</iframe>`;
|
||||
var iframeUrl = "";
|
||||
if (aParams) {
|
||||
aParams.delete("ACTION");
|
||||
aParams.append("ACTION", "redirectIframe");
|
||||
iframeUrl = "http://" + CROSS_ORIGIN + aParams.toString();
|
||||
} else {
|
||||
iframeUrl = createTestUrl(aAttributePolicy, "test", aName, "iframe");
|
||||
}
|
||||
|
||||
return `<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
@ -42,7 +60,7 @@ function createIframeTestPageUsingRefferer(aMetaPolicy, aAttributePolicy, aNewAt
|
|||
document.getElementById("myframe").onload = function(){
|
||||
parent.postMessage("childLoadComplete", "http://mochi.test:8888");
|
||||
};
|
||||
document.getElementById("myframe").src = "${createTestUrl(aAttributePolicy, 'test', aName, 'iframe')}";
|
||||
document.getElementById("myframe").src = "${iframeUrl}";
|
||||
}.bind(window), false);
|
||||
</script>
|
||||
</body>
|
||||
|
@ -71,17 +89,17 @@ function buildAreaString(aMetaPolicy, aReferrerPolicy, aName, aRelString){
|
|||
|
||||
// test page using anchor or area referrer attribute
|
||||
function createAETestPageUsingRefferer(aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aRel, aStringBuilder, aChangingMethod) {
|
||||
var metaString = '';
|
||||
var metaString = "";
|
||||
if (aMetaPolicy) {
|
||||
metaString = '<head><meta name="referrer" content="' + aMetaPolicy + '"></head>';
|
||||
metaString = `<head><meta name="referrer" content="${aMetaPolicy}"></head>`;
|
||||
}
|
||||
var changeString = '';
|
||||
if (aChangingMethod === 'setAttribute') {
|
||||
var changeString = "";
|
||||
if (aChangingMethod === "setAttribute") {
|
||||
changeString = `document.getElementById("link").setAttribute("referrer", "${aNewAttributePolicy}")`;
|
||||
} else if (aChangingMethod === 'property') {
|
||||
} else if (aChangingMethod === "property") {
|
||||
changeString = `document.getElementById("link").referrer = "${aNewAttributePolicy}"`;
|
||||
}
|
||||
var relString = '';
|
||||
var relString = "";
|
||||
if (aRel) {
|
||||
relString = `rel="noreferrer"`;
|
||||
}
|
||||
|
@ -102,41 +120,84 @@ function createAETestPageUsingRefferer(aMetaPolicy, aAttributePolicy, aNewAttrib
|
|||
</html>`;
|
||||
}
|
||||
|
||||
// creates test page with img that is a redirect
|
||||
function createRedirectImgTestCase(aParams, aAttributePolicy) {
|
||||
var metaString = "";
|
||||
if (aParams.has("META_POLICY")) {
|
||||
metaString = `<meta name="referrer" content="${aParams.get('META_POLICY')}">`;
|
||||
}
|
||||
aParams.delete("ACTION");
|
||||
aParams.append("ACTION", "redirectImg");
|
||||
var imgUrl = "http://" + CROSS_ORIGIN + aParams.toString();
|
||||
|
||||
return `<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
${metaString}
|
||||
<title>Test referrer policies on redirect (img)</title>
|
||||
</head>
|
||||
<body>
|
||||
<img id="testImg" src="${imgUrl}" ${aAttributePolicy ? ` referrer="${aAttributePolicy}"` : ""}>
|
||||
<script>
|
||||
window.addEventListener("load", function() {
|
||||
parent.postMessage("childLoadComplete", "http://mochi.test:8888");
|
||||
}.bind(window), false);
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
function handleRequest(request, response) {
|
||||
var params = new URLSearchParams(request.queryString);
|
||||
var action = params.get('ACTION');
|
||||
var action = params.get("ACTION");
|
||||
|
||||
response.setHeader('Cache-Control', 'no-cache', false);
|
||||
response.setHeader('Content-Type', 'text/html; charset=utf-8', false);
|
||||
|
||||
if (action === 'resetState') {
|
||||
if (action === "resetState") {
|
||||
setSharedState(SHARED_KEY, "{}");
|
||||
response.write("");
|
||||
return;
|
||||
}
|
||||
if (action === 'get-test-results') {
|
||||
if (action === "get-test-results") {
|
||||
// ?action=get-result
|
||||
response.setHeader('Cache-Control', 'no-cache', false);
|
||||
response.setHeader('Content-Type', 'text/plain', false);
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
response.write(getSharedState(SHARED_KEY));
|
||||
return;
|
||||
}
|
||||
if (action === 'redirect') {
|
||||
if (action === "redirect") {
|
||||
response.write('<script>parent.postMessage("childLoadComplete", "http://mochi.test:8888");</script>');
|
||||
return;
|
||||
}
|
||||
if (action === 'test') {
|
||||
if (action === "redirectImg"){
|
||||
params.delete("ACTION");
|
||||
params.append("ACTION", "test");
|
||||
params.append("type", "img");
|
||||
// 302 found, 301 Moved Permanently, 303 See Other, 307 Temporary Redirect
|
||||
response.setStatusLine("1.1", 302, "found");
|
||||
response.setHeader("Location", "http://" + CROSS_ORIGIN + params.toString(), false);
|
||||
return;
|
||||
}
|
||||
if (action === "redirectIframe"){
|
||||
params.delete("ACTION");
|
||||
params.append("ACTION", "test");
|
||||
params.append("type", "iframe");
|
||||
// 302 found, 301 Moved Permanently, 303 See Other, 307 Temporary Redirect
|
||||
response.setStatusLine("1.1", 302, "found");
|
||||
response.setHeader("Location", "http://" + CROSS_ORIGIN + params.toString(), false);
|
||||
return;
|
||||
}
|
||||
if (action === "test") {
|
||||
// ?action=test&policy=origin&name=name
|
||||
var policy = params.get('policy');
|
||||
var name = params.get('name');
|
||||
var type = params.get('type');
|
||||
var policy = params.get("policy");
|
||||
var name = params.get("NAME");
|
||||
var type = params.get("type");
|
||||
var result = getSharedState(SHARED_KEY);
|
||||
|
||||
result = result ? JSON.parse(result) : {};
|
||||
|
||||
var referrerLevel = "none";
|
||||
var test = {}
|
||||
if (request.hasHeader('Referer')) {
|
||||
if (request.hasHeader("Referer")) {
|
||||
var referrer = request.getHeader("Referer");
|
||||
if (referrer.indexOf("referrer_testserver") > 0) {
|
||||
referrerLevel = "full";
|
||||
|
@ -148,7 +209,7 @@ function handleRequest(request, response) {
|
|||
}
|
||||
test.referrer = referrer;
|
||||
} else {
|
||||
test.referrer = '';
|
||||
test.referrer = "";
|
||||
}
|
||||
test.policy = referrerLevel;
|
||||
test.expected = policy;
|
||||
|
@ -157,23 +218,34 @@ function handleRequest(request, response) {
|
|||
|
||||
setSharedState(SHARED_KEY, JSON.stringify(result));
|
||||
|
||||
if (type === "img") {
|
||||
// return image
|
||||
response.setHeader("Content-Type", "image/png");
|
||||
response.write(IMG_BYTES);
|
||||
return;
|
||||
}
|
||||
if (type === "iframe") {
|
||||
// return iframe page
|
||||
response.write("<html><body>I am the iframe</body></html>");
|
||||
} else if (type === "link") {
|
||||
return;
|
||||
}
|
||||
if (type === "link") {
|
||||
// forward link click to redirect URL to finish test
|
||||
var loc = "http://" + BASE_URL + "?ACTION=redirect";
|
||||
response.setStatusLine('1.1', 302, 'Found');
|
||||
response.setHeader('Location', loc, false);
|
||||
var loc = "http://" + BASE_URL + "ACTION=redirect";
|
||||
response.setStatusLine("1.1", 302, "Found");
|
||||
response.setHeader("Location", loc, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
response.setHeader("Content-Type", "text/html; charset=utf-8", false);
|
||||
|
||||
// parse test arguments and start test
|
||||
var attributePolicy = params.get("ATTRIBUTE_POLICY") || '';
|
||||
var newAttributePolicy = params.get("NEW_ATTRIBUTE_POLICY") || '';
|
||||
var metaPolicy = params.get("META_POLICY") || '';
|
||||
var rel = params.get("REL") || '';
|
||||
var attributePolicy = params.get("ATTRIBUTE_POLICY") || "";
|
||||
var newAttributePolicy = params.get("NEW_ATTRIBUTE_POLICY") || "";
|
||||
var metaPolicy = params.get("META_POLICY") || "";
|
||||
var rel = params.get("REL") || "";
|
||||
var name = params.get("NAME");
|
||||
|
||||
// anchor & area
|
||||
|
@ -182,45 +254,55 @@ function handleRequest(request, response) {
|
|||
var _getAreaPage = _getPage.bind(null, buildAreaString);
|
||||
|
||||
// aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aChangingMethod, aStringBuilder
|
||||
if (action === 'generate-anchor-policy-test') {
|
||||
if (action === "generate-anchor-policy-test") {
|
||||
response.write(_getAnchorPage());
|
||||
return;
|
||||
}
|
||||
if (action === 'generate-anchor-changing-policy-test-set-attribute') {
|
||||
response.write(_getAnchorPage('setAttribute'));
|
||||
if (action === "generate-anchor-changing-policy-test-set-attribute") {
|
||||
response.write(_getAnchorPage("setAttribute"));
|
||||
return;
|
||||
}
|
||||
if (action === 'generate-anchor-changing-policy-test-property') {
|
||||
response.write(_getAnchorPage('property'));
|
||||
if (action === "generate-anchor-changing-policy-test-property") {
|
||||
response.write(_getAnchorPage("property"));
|
||||
return;
|
||||
}
|
||||
if (action === 'generate-area-policy-test') {
|
||||
if (action === "generate-area-policy-test") {
|
||||
response.write(_getAreaPage());
|
||||
return;
|
||||
}
|
||||
if (action === 'generate-area-changing-policy-test-set-attribute') {
|
||||
response.write(_getAreaPage('setAttribute'));
|
||||
if (action === "generate-area-changing-policy-test-set-attribute") {
|
||||
response.write(_getAreaPage("setAttribute"));
|
||||
return;
|
||||
}
|
||||
if (action === 'generate-area-changing-policy-test-property') {
|
||||
response.write(_getAreaPage('property'));
|
||||
if (action === "generate-area-changing-policy-test-property") {
|
||||
response.write(_getAreaPage("property"));
|
||||
return;
|
||||
}
|
||||
|
||||
// iframe
|
||||
_getPage = createIframeTestPageUsingRefferer.bind(null, metaPolicy, attributePolicy, newAttributePolicy, name);
|
||||
_getPage = createIframeTestPageUsingRefferer.bind(null, metaPolicy, attributePolicy, newAttributePolicy, name, "");
|
||||
|
||||
// aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aChangingMethod
|
||||
if (action === 'generate-iframe-policy-test') {
|
||||
if (action === "generate-iframe-policy-test") {
|
||||
response.write(_getPage());
|
||||
return;
|
||||
}
|
||||
if (action === 'generate-iframe-changing-policy-test-set-attribute') {
|
||||
response.write(_getPage('setAttribute'));
|
||||
if (action === "generate-iframe-changing-policy-test-set-attribute") {
|
||||
response.write(_getPage("setAttribute"));
|
||||
return;
|
||||
}
|
||||
if (action === 'generate-iframe-changing-policy-test-property') {
|
||||
response.write(_getPage('property'));
|
||||
if (action === "generate-iframe-changing-policy-test-property") {
|
||||
response.write(_getPage("property"));
|
||||
return;
|
||||
}
|
||||
|
||||
// redirect tests with img and iframe
|
||||
if (action === "generate-img-redirect-policy-test") {
|
||||
response.write(createRedirectImgTestCase(params, attributePolicy));
|
||||
return;
|
||||
}
|
||||
if (action === "generate-iframe-redirect-policy-test") {
|
||||
response.write(createIframeTestPageUsingRefferer(metaPolicy, attributePolicy, newAttributePolicy, name, params));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test anchor and area policy attribute for Bug 1184781</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<!--
|
||||
Testing referrer headers after redirects.
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1184781
|
||||
-->
|
||||
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
const SJS = "/tests/dom/base/test/referrer_testserver.sjs?";
|
||||
const PARAMS = ["ATTRIBUTE_POLICY", "NEW_ATTRIBUTE_POLICY", "META_POLICY"];
|
||||
|
||||
const testCases = [
|
||||
{ACTION: ["generate-img-redirect-policy-test", "generate-iframe-redirect-policy-test"],
|
||||
TESTS: [
|
||||
{
|
||||
ATTRIBUTE_POLICY: "no-referrer",
|
||||
NAME: "no-referrer-with-no-meta",
|
||||
DESC: "no-referrer (img/iframe) with no meta",
|
||||
RESULT: "none"
|
||||
},
|
||||
{
|
||||
ATTRIBUTE_POLICY: "origin",
|
||||
NAME: "origin-with-no-meta",
|
||||
DESC: "origin (img/iframe) with no meta",
|
||||
RESULT: "origin"
|
||||
},
|
||||
{
|
||||
ATTRIBUTE_POLICY: "unsafe-url",
|
||||
NAME: "unsafe-url-with-no-meta",
|
||||
DESC: "unsafe-url (img/iframe) with no meta",
|
||||
RESULT: "full"
|
||||
},
|
||||
{
|
||||
META_POLICY: "unsafe-url",
|
||||
NAME: "unsafe-url-in-meta",
|
||||
DESC: "unsafe-url in meta",
|
||||
RESULT: "full"
|
||||
},
|
||||
{
|
||||
META_POLICY: "origin",
|
||||
NAME: "origin-in-meta",
|
||||
DESC: "origin in meta",
|
||||
RESULT: "origin"
|
||||
},
|
||||
{
|
||||
META_POLICY: "no-referrer",
|
||||
NAME: "no-referrer-in-meta",
|
||||
DESC: "no-referrer in meta",
|
||||
RESULT: "none"
|
||||
},
|
||||
{
|
||||
META_POLICY: "origin-when-cross-origin",
|
||||
NAME: "origin-when-cross-origin-in-meta",
|
||||
DESC: "origin-when-cross-origin in meta",
|
||||
RESULT: "origin"
|
||||
}]}
|
||||
];
|
||||
</script>
|
||||
<script type="application/javascript;version=1.7" src="/tests/dom/base/test/referrer_helper.js"></script>
|
||||
</head>
|
||||
<body onload="tests.next();">
|
||||
<iframe id="testframe"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -570,15 +570,8 @@ CreateHeadlessANGLE(bool forceEnabled, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
|
|||
nsRefPtr<GLContext> gl;
|
||||
|
||||
#ifdef XP_WIN
|
||||
if (!forceEnabled &&
|
||||
IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_ANGLE))
|
||||
{
|
||||
webgl->GenerateWarning("Refused to create ANGLE OpenGL context"
|
||||
" because of blacklisting.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gl = gl::GLContextProviderEGL::CreateHeadless(requireCompatProfile);
|
||||
gl = gl::GLContextProviderEGL::CreateHeadless(requireCompatProfile,
|
||||
forceEnabled);
|
||||
if (!gl) {
|
||||
webgl->GenerateWarning("Error during ANGLE OpenGL init.");
|
||||
return nullptr;
|
||||
|
|
|
@ -23,12 +23,11 @@ extern PRLogModuleInfo* gMediaDecoderLog;
|
|||
static const int64_t AUDIO_FUZZ_FRAMES = 1;
|
||||
|
||||
AudioSink::AudioSink(MediaQueue<AudioData>& aAudioQueue,
|
||||
ReentrantMonitor& aMonitor,
|
||||
int64_t aStartTime,
|
||||
const AudioInfo& aInfo,
|
||||
dom::AudioChannel aChannel)
|
||||
: mAudioQueue(aAudioQueue)
|
||||
, mDecoderMonitor(aMonitor)
|
||||
, mMonitor("AudioSink::mMonitor")
|
||||
, mStartTime(aStartTime)
|
||||
, mWritten(0)
|
||||
, mLastGoodPosition(0)
|
||||
|
@ -71,7 +70,7 @@ AudioSink::Init()
|
|||
int64_t
|
||||
AudioSink::GetPosition()
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
|
||||
int64_t pos;
|
||||
if (mAudioStream &&
|
||||
|
@ -86,7 +85,7 @@ AudioSink::GetPosition()
|
|||
bool
|
||||
AudioSink::HasUnplayedFrames()
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
// Experimentation suggests that GetPositionInFrames() is zero-indexed,
|
||||
// so we need to add 1 here before comparing it to mWritten.
|
||||
return mAudioStream && mAudioStream->GetPositionInFrames() + 1 < mWritten;
|
||||
|
@ -95,13 +94,14 @@ AudioSink::HasUnplayedFrames()
|
|||
void
|
||||
AudioSink::Shutdown()
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
mStopAudioThread = true;
|
||||
if (mAudioStream) {
|
||||
mAudioStream->Cancel();
|
||||
}
|
||||
GetReentrantMonitor().NotifyAll();
|
||||
|
||||
// Exit the monitor so audio loop can enter the monitor and finish its job.
|
||||
ReentrantMonitorAutoExit exit(GetReentrantMonitor());
|
||||
mThread->Shutdown();
|
||||
mThread = nullptr;
|
||||
|
@ -114,7 +114,7 @@ AudioSink::Shutdown()
|
|||
void
|
||||
AudioSink::SetVolume(double aVolume)
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
mVolume = aVolume;
|
||||
mSetVolume = true;
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ AudioSink::SetVolume(double aVolume)
|
|||
void
|
||||
AudioSink::SetPlaybackRate(double aPlaybackRate)
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
NS_ASSERTION(mPlaybackRate != 0, "Don't set the playbackRate to 0 on AudioStream");
|
||||
mPlaybackRate = aPlaybackRate;
|
||||
mSetPlaybackRate = true;
|
||||
|
@ -131,7 +131,7 @@ AudioSink::SetPlaybackRate(double aPlaybackRate)
|
|||
void
|
||||
AudioSink::SetPreservesPitch(bool aPreservesPitch)
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
mPreservesPitch = aPreservesPitch;
|
||||
mSetPreservesPitch = true;
|
||||
}
|
||||
|
@ -139,11 +139,18 @@ AudioSink::SetPreservesPitch(bool aPreservesPitch)
|
|||
void
|
||||
AudioSink::SetPlaying(bool aPlaying)
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
mPlaying = aPlaying;
|
||||
GetReentrantMonitor().NotifyAll();
|
||||
}
|
||||
|
||||
void
|
||||
AudioSink::NotifyData()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
GetReentrantMonitor().NotifyAll();
|
||||
}
|
||||
|
||||
void
|
||||
AudioSink::AudioLoop()
|
||||
{
|
||||
|
|
|
@ -26,7 +26,6 @@ public:
|
|||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioSink)
|
||||
|
||||
AudioSink(MediaQueue<AudioData>& aAudioQueue,
|
||||
ReentrantMonitor& aMonitor,
|
||||
int64_t aStartTime,
|
||||
const AudioInfo& aInfo,
|
||||
dom::AudioChannel aChannel);
|
||||
|
@ -35,9 +34,10 @@ public:
|
|||
// or rejected if any error.
|
||||
nsRefPtr<GenericPromise> Init();
|
||||
|
||||
/*
|
||||
* All public functions below are thread-safe.
|
||||
*/
|
||||
int64_t GetPosition();
|
||||
|
||||
// Thread-safe. Can be called on any thread.
|
||||
int64_t GetEndTime() const;
|
||||
|
||||
// Check whether we've pushed more frames to the audio hardware than it has
|
||||
|
@ -45,15 +45,17 @@ public:
|
|||
bool HasUnplayedFrames();
|
||||
|
||||
// Shut down the AudioSink's resources.
|
||||
// Must be called with the decoder monitor held.
|
||||
void Shutdown();
|
||||
|
||||
void SetVolume(double aVolume);
|
||||
void SetPlaybackRate(double aPlaybackRate);
|
||||
void SetPreservesPitch(bool aPreservesPitch);
|
||||
|
||||
void SetPlaying(bool aPlaying);
|
||||
|
||||
// Wake up the audio loop if it is waiting for data to play or the audio
|
||||
// queue is finished.
|
||||
void NotifyData();
|
||||
|
||||
private:
|
||||
~AudioSink() {}
|
||||
|
||||
|
@ -104,7 +106,7 @@ private:
|
|||
}
|
||||
|
||||
ReentrantMonitor& GetReentrantMonitor() const {
|
||||
return mDecoderMonitor;
|
||||
return mMonitor;
|
||||
}
|
||||
|
||||
void AssertCurrentThreadInMonitor() const {
|
||||
|
@ -114,7 +116,7 @@ private:
|
|||
void AssertOnAudioThread();
|
||||
|
||||
MediaQueue<AudioData>& mAudioQueue;
|
||||
ReentrantMonitor& mDecoderMonitor;
|
||||
mutable ReentrantMonitor mMonitor;
|
||||
|
||||
// Thread for pushing audio onto the audio hardware.
|
||||
// The "audio push thread".
|
||||
|
|
|
@ -238,7 +238,9 @@ media::TimeIntervals
|
|||
MediaDecoderReader::GetBuffered()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
NS_ENSURE_TRUE(HaveStartTime(), media::TimeIntervals());
|
||||
if (!HaveStartTime()) {
|
||||
return media::TimeIntervals();
|
||||
}
|
||||
AutoPinned<MediaResource> stream(mDecoder->GetResource());
|
||||
|
||||
if (!mDuration.Ref().isSome()) {
|
||||
|
|
|
@ -642,8 +642,9 @@ MediaDecoderStateMachine::Push(AudioData* aSample)
|
|||
UpdateNextFrameStatus();
|
||||
DispatchDecodeTasksIfNeeded();
|
||||
|
||||
// XXXbholley - Still necessary?
|
||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||
if (mAudioSink) {
|
||||
mAudioSink->NotifyData();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -789,6 +790,10 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
|
|||
case DECODER_STATE_DECODING: {
|
||||
CheckIfDecodeComplete();
|
||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||
// Tell AudioSink to wake up for audio queue is finished.
|
||||
if (mAudioSink) {
|
||||
mAudioSink->NotifyData();
|
||||
}
|
||||
// Schedule the state machine to notify track ended as soon as possible.
|
||||
if (mAudioCaptured) {
|
||||
ScheduleStateMachine();
|
||||
|
@ -1053,9 +1058,6 @@ void MediaDecoderStateMachine::StopPlayback()
|
|||
mPlayDuration = GetClock();
|
||||
SetPlayStartTime(TimeStamp());
|
||||
}
|
||||
// Notify the audio sink, so that it notices that we've stopped playing,
|
||||
// so it can pause audio playback.
|
||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||
NS_ASSERTION(!IsPlaying(), "Should report not playing at end of StopPlayback()");
|
||||
|
||||
DispatchDecodeTasksIfNeeded();
|
||||
|
@ -1794,7 +1796,7 @@ MediaDecoderStateMachine::StartAudioThread()
|
|||
|
||||
if (HasAudio() && !mAudioSink) {
|
||||
mAudioCompleted = false;
|
||||
mAudioSink = new AudioSink(mAudioQueue, mDecoder->GetReentrantMonitor(),
|
||||
mAudioSink = new AudioSink(mAudioQueue,
|
||||
GetMediaTime(), mInfo.mAudio,
|
||||
mDecoder->GetAudioChannel());
|
||||
|
||||
|
|
|
@ -1449,10 +1449,14 @@ MediaFormatReader::GetBuffered()
|
|||
return intervals;
|
||||
}
|
||||
int64_t startTime;
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
NS_ENSURE_TRUE(HaveStartTime(), media::TimeIntervals());
|
||||
if (!ForceZeroStartTime()) {
|
||||
if (!HaveStartTime()) {
|
||||
return intervals;
|
||||
}
|
||||
startTime = StartTime();
|
||||
} else {
|
||||
// MSE, start time is assumed to be 0 we can proceeed with what we have.
|
||||
startTime = 0;
|
||||
}
|
||||
// Ensure we have up to date buffered time range.
|
||||
if (HasVideo()) {
|
||||
|
|
|
@ -3473,6 +3473,13 @@ MediaStreamGraphImpl::ApplyAudioContextOperationImpl(AudioNodeStream* aStream,
|
|||
mMixer.RemoveCallback(CurrentDriver()->AsAudioCallbackDriver());
|
||||
CurrentDriver()->SwitchAtNextIteration(driver);
|
||||
}
|
||||
// We are closing or suspending an AudioContext, but we just got resumed.
|
||||
// Queue the operation on the next driver so that the ordering is
|
||||
// preserved.
|
||||
} else if (!audioTrackPresent && CurrentDriver()->Switching()) {
|
||||
MOZ_ASSERT(CurrentDriver()->NextDriver()->AsAudioCallbackDriver());
|
||||
CurrentDriver()->NextDriver()->AsAudioCallbackDriver()->
|
||||
EnqueueStreamAndPromiseForOperation(aStream, aPromise, aOperation);
|
||||
} else {
|
||||
// We are closing or suspending an AudioContext, but something else is
|
||||
// using the audio stream, we can resolve the promise now.
|
||||
|
|
|
@ -184,6 +184,18 @@ CDMCaps::AutoLock::AreCapsKnown()
|
|||
return mData.mCaps != 0;
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::AutoLock::CanRenderAudio()
|
||||
{
|
||||
return mData.HasCap(GMP_EME_CAP_RENDER_AUDIO);
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::AutoLock::CanRenderVideo()
|
||||
{
|
||||
return mData.HasCap(GMP_EME_CAP_RENDER_VIDEO);
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::AutoLock::CanDecryptAndDecodeAudio()
|
||||
{
|
||||
|
|
|
@ -75,6 +75,9 @@ public:
|
|||
// GMP_EME_CAP_* flags from gmp-decryption.h.
|
||||
void SetCaps(uint64_t aCaps);
|
||||
|
||||
bool CanRenderAudio();
|
||||
bool CanRenderVideo();
|
||||
|
||||
bool CanDecryptAndDecodeAudio();
|
||||
bool CanDecryptAndDecodeVideo();
|
||||
|
||||
|
|
|
@ -432,13 +432,13 @@ GeckoMediaPluginServiceParent::AsyncShutdownPluginStates::Update(const nsCString
|
|||
note += pluginIt.Key();
|
||||
note += ":{";
|
||||
bool firstInstance = true;
|
||||
for (auto instanceIt = pluginIt.Data()->ConstIter(); !instanceIt.Done(); instanceIt.Next()) {
|
||||
for (auto instanceIt = pluginIt.UserData()->ConstIter(); !instanceIt.Done(); instanceIt.Next()) {
|
||||
if (!firstInstance) { note += ','; } else { firstInstance = false; }
|
||||
note += instanceIt.Key();
|
||||
note += ":\"";
|
||||
note += instanceIt.Data()->mStateSequence;
|
||||
note += instanceIt.UserData()->mStateSequence;
|
||||
note += '=';
|
||||
note += instanceIt.Data()->mLastStateDescription;
|
||||
note += instanceIt.UserData()->mLastStateDescription;
|
||||
note += '"';
|
||||
}
|
||||
note += '}';
|
||||
|
|
|
@ -125,6 +125,10 @@ typedef int64_t GMPTimestamp;
|
|||
#define GMP_EME_CAP_DECRYPT_AND_DECODE_AUDIO (uint64_t(1) << 2)
|
||||
#define GMP_EME_CAP_DECRYPT_AND_DECODE_VIDEO (uint64_t(1) << 3)
|
||||
|
||||
// Capability; CDM can decrypt and then decode and render encrypted buffers
|
||||
#define GMP_EME_CAP_RENDER_AUDIO (uint64_t(1) << 4)
|
||||
#define GMP_EME_CAP_RENDER_VIDEO (uint64_t(1) << 5)
|
||||
|
||||
// Callbacks to be called from the CDM. Threadsafe.
|
||||
class GMPDecryptorCallback {
|
||||
public:
|
||||
|
|
|
@ -1840,7 +1840,9 @@ nsresult OggReader::SeekBisection(int64_t aTarget,
|
|||
media::TimeIntervals OggReader::GetBuffered()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
NS_ENSURE_TRUE(HaveStartTime(), media::TimeIntervals());
|
||||
if (!HaveStartTime()) {
|
||||
return media::TimeIntervals();
|
||||
}
|
||||
{
|
||||
mozilla::ReentrantMonitorAutoEnter mon(mMonitor);
|
||||
if (mIsChained) {
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<script>
|
||||
|
||||
function boom()
|
||||
{
|
||||
var ac = new window.AudioContext();
|
||||
var oscillator = ac.createOscillator();
|
||||
oscillator.start(0);
|
||||
oscillator.stop(0.1);
|
||||
setTimeout(function() {
|
||||
oscillator.channelCount = 1;
|
||||
oscillator.channelCountMode = "explicit";
|
||||
oscillator.channelInterpretation = "speakers";
|
||||
document.documentElement.removeAttribute("class");
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="boom();"></body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
|
||||
function boom()
|
||||
{
|
||||
new Audio().mozCaptureStreamUntilEnded();
|
||||
var ac = new window.AudioContext();
|
||||
ac.resume();
|
||||
ac.close();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="boom();"></body>
|
||||
</html>
|
||||
|
|
@ -81,6 +81,8 @@ load 1157994.html
|
|||
load 1122218.html
|
||||
load 1127188.html
|
||||
load audiocontext-double-suspend.html
|
||||
load 1185176.html
|
||||
load 1185192.html
|
||||
include ../../mediasource/test/crashtests/crashtests.list
|
||||
|
||||
# This needs to run at the end to avoid leaking busted state into other tests.
|
||||
|
|
|
@ -26,8 +26,11 @@ function startTest() {
|
|||
'Media recorder stream = element stream post recording');
|
||||
stopCount++;
|
||||
if (stopCount == 2) {
|
||||
is(2, dataavailable, 'Should has two dataavailable event');
|
||||
SimpleTest.finish();
|
||||
if (dataavailable >= 2) {
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
ok(false, 'Should have at least two dataavailable events');
|
||||
}
|
||||
}
|
||||
}
|
||||
recorder.ondataavailable = function (evt) {
|
||||
|
@ -41,9 +44,6 @@ function startTest() {
|
|||
info('blob size = ' + evt.data.size);
|
||||
is(evt.data.type, expectedMimeType,
|
||||
'Blob data received should have type = ' + expectedMimeType);
|
||||
} else {
|
||||
is(evt.data.type, '',
|
||||
'Blob data received should have empty type');
|
||||
}
|
||||
dataavailable++;
|
||||
}
|
||||
|
|
|
@ -455,31 +455,31 @@ var commandsPeerConnectionOfferAnswer = [
|
|||
},
|
||||
|
||||
function PC_LOCAL_CHECK_STATS(test) {
|
||||
return test.pcLocal.getStats(null).then(stats => {
|
||||
return test.pcLocal.getStats().then(stats => {
|
||||
test.pcLocal.checkStats(stats, test.steeplechase);
|
||||
});
|
||||
},
|
||||
|
||||
function PC_REMOTE_CHECK_STATS(test) {
|
||||
test.pcRemote.getStats(null).then(stats => {
|
||||
test.pcRemote.getStats().then(stats => {
|
||||
test.pcRemote.checkStats(stats, test.steeplechase);
|
||||
});
|
||||
},
|
||||
|
||||
function PC_LOCAL_CHECK_ICE_CONNECTION_TYPE(test) {
|
||||
test.pcLocal.getStats(null).then(stats => {
|
||||
test.pcLocal.getStats().then(stats => {
|
||||
test.pcLocal.checkStatsIceConnectionType(stats);
|
||||
});
|
||||
},
|
||||
|
||||
function PC_REMOTE_CHECK_ICE_CONNECTION_TYPE(test) {
|
||||
test.pcRemote.getStats(null).then(stats => {
|
||||
test.pcRemote.getStats().then(stats => {
|
||||
test.pcRemote.checkStatsIceConnectionType(stats);
|
||||
});
|
||||
},
|
||||
|
||||
function PC_LOCAL_CHECK_ICE_CONNECTIONS(test) {
|
||||
test.pcLocal.getStats(null).then(stats => {
|
||||
test.pcLocal.getStats().then(stats => {
|
||||
test.pcLocal.checkStatsIceConnections(stats,
|
||||
test._offer_constraints,
|
||||
test._offer_options,
|
||||
|
@ -488,7 +488,7 @@ var commandsPeerConnectionOfferAnswer = [
|
|||
},
|
||||
|
||||
function PC_REMOTE_CHECK_ICE_CONNECTIONS(test) {
|
||||
test.pcRemote.getStats(null).then(stats => {
|
||||
test.pcRemote.getStats().then(stats => {
|
||||
test.pcRemote.checkStatsIceConnections(stats,
|
||||
test._offer_constraints,
|
||||
test._offer_options,
|
||||
|
|
|
@ -294,9 +294,10 @@ AudioNode::SendThreeDPointParameterToStream(uint32_t aIndex, const ThreeDPoint&
|
|||
void
|
||||
AudioNode::SendChannelMixingParametersToStream()
|
||||
{
|
||||
MOZ_ASSERT(mStream, "How come we don't have a stream here?");
|
||||
mStream->SetChannelMixingParameters(mChannelCount, mChannelCountMode,
|
||||
mChannelInterpretation);
|
||||
if (mStream) {
|
||||
mStream->SetChannelMixingParameters(mChannelCount, mChannelCountMode,
|
||||
mChannelInterpretation);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -422,8 +423,9 @@ AudioNode::SetPassThrough(bool aPassThrough)
|
|||
{
|
||||
MOZ_ASSERT(NumberOfInputs() <= 1 && NumberOfOutputs() == 1);
|
||||
mPassThrough = aPassThrough;
|
||||
MOZ_ASSERT(mStream, "How come we don't have a stream here?");
|
||||
mStream->SetPassThrough(mPassThrough);
|
||||
if (mStream) {
|
||||
mStream->SetPassThrough(mPassThrough);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -786,7 +786,9 @@ nsresult WebMReader::SeekInternal(int64_t aTarget)
|
|||
media::TimeIntervals WebMReader::GetBuffered()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
NS_ENSURE_TRUE(HaveStartTime(), media::TimeIntervals());
|
||||
if (!HaveStartTime()) {
|
||||
return media::TimeIntervals();
|
||||
}
|
||||
AutoPinned<MediaResource> resource(mDecoder->GetResource());
|
||||
|
||||
media::TimeIntervals buffered;
|
||||
|
|
|
@ -144,7 +144,7 @@ interface mozRTCPeerConnection : EventTarget {
|
|||
attribute EventHandler onremovestream;
|
||||
attribute EventHandler oniceconnectionstatechange;
|
||||
|
||||
Promise<RTCStatsReport> getStats (MediaStreamTrack? selector);
|
||||
Promise<RTCStatsReport> getStats (optional MediaStreamTrack? selector);
|
||||
|
||||
// Data channel.
|
||||
RTCDataChannel createDataChannel (DOMString label,
|
||||
|
|
|
@ -122,7 +122,7 @@ EGLDisplay __stdcall eglGetPlatformDisplayEXT(EGLenum platform, void *native_dis
|
|||
|
||||
#if !defined(ANGLE_ENABLE_WINDOWS_STORE)
|
||||
// Validate the display device context
|
||||
if (WindowFromDC(displayId) == NULL)
|
||||
if (displayId != EGL_DEFAULT_DISPLAY && WindowFromDC(displayId) == NULL)
|
||||
{
|
||||
return egl::success(EGL_NO_DISPLAY);
|
||||
}
|
||||
|
|
|
@ -196,6 +196,14 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the context is using WARP. This should only be overridden
|
||||
* for an ANGLE implementation.
|
||||
*/
|
||||
virtual bool IsWARP() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we are running on a OpenGL core profile context
|
||||
*/
|
||||
|
|
|
@ -66,6 +66,10 @@ public:
|
|||
return sEGLLibrary.IsANGLE();
|
||||
}
|
||||
|
||||
virtual bool IsWARP() const override {
|
||||
return sEGLLibrary.IsWARP();
|
||||
}
|
||||
|
||||
virtual bool BindTexImage() override;
|
||||
|
||||
virtual bool ReleaseTexImage() override;
|
||||
|
|
|
@ -287,7 +287,7 @@ CreateOffscreenFBOContext(bool requireCompatProfile)
|
|||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderCGL::CreateHeadless(bool requireCompatProfile)
|
||||
GLContextProviderCGL::CreateHeadless(bool requireCompatProfile, bool forceEnabled)
|
||||
{
|
||||
nsRefPtr<GLContextCGL> gl;
|
||||
gl = CreateOffscreenFBOContext(requireCompatProfile);
|
||||
|
|
|
@ -933,9 +933,9 @@ GLContextEGL::CreateEGLPixmapOffscreenContext(const mozilla::gfx::IntSize& size)
|
|||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderEGL::CreateHeadless(bool)
|
||||
GLContextProviderEGL::CreateHeadless(bool requireCompatProfile, bool forceEnabled)
|
||||
{
|
||||
if (!sEGLLibrary.EnsureInitialized()) {
|
||||
if (!sEGLLibrary.EnsureInitialized(forceEnabled)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -1215,7 +1215,7 @@ DONE_CREATING_PIXMAP:
|
|||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderGLX::CreateHeadless(bool)
|
||||
GLContextProviderGLX::CreateHeadless(bool requireCompatProfile, bool forceEnabled)
|
||||
{
|
||||
IntSize dummySize = IntSize(16, 16);
|
||||
nsRefPtr<GLContext> glContext = CreateOffscreenPixmapContext(dummySize);
|
||||
|
|
|
@ -66,7 +66,7 @@ public:
|
|||
|
||||
// Just create a context. We'll add offscreen stuff ourselves.
|
||||
static already_AddRefed<GLContext>
|
||||
CreateHeadless(bool requireCompatProfile);
|
||||
CreateHeadless(bool requireCompatProfile, bool forceEnabled = false);
|
||||
|
||||
/**
|
||||
* Create wrapping Gecko GLContext for external gl context.
|
||||
|
|
|
@ -607,7 +607,7 @@ CreateWindowOffscreenContext()
|
|||
}
|
||||
|
||||
already_AddRefed<GLContext>
|
||||
GLContextProviderWGL::CreateHeadless(bool)
|
||||
GLContextProviderWGL::CreateHeadless(bool requireCompatProfile, bool forceEnabled)
|
||||
{
|
||||
if (!sWGLLib.EnsureInitialized()) {
|
||||
return nullptr;
|
||||
|
|
|
@ -54,6 +54,11 @@
|
|||
#define LOCAL_EGL_PRESERVED_RESOURCES 0x3030
|
||||
#define LOCAL_EGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_EXT 0x3138
|
||||
|
||||
// ANGLE_platform_angle_d3d
|
||||
#define LOCAL_EGL_PLATFORM_ANGLE_ANGLE 0x3201
|
||||
#define LOCAL_EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3202
|
||||
#define LOCAL_EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE 0x3206
|
||||
|
||||
#define LOCAL_GL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
|
||||
#define LOCAL_GL_CONTEXT_LOST 0x9242
|
||||
#define LOCAL_GL_CONTEXT_FLAGS_ARB 0x2094
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "mozilla/Assertions.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsIGfxInfo.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#ifdef XP_WIN
|
||||
#include "nsWindowsHelpers.h"
|
||||
|
@ -101,8 +102,36 @@ LoadLibraryForEGLOnWindows(const nsAString& filename)
|
|||
}
|
||||
return lib;
|
||||
}
|
||||
|
||||
#endif // XP_WIN
|
||||
|
||||
static EGLDisplay
|
||||
GetAndInitWARPDisplay(GLLibraryEGL& egl, void* displayType)
|
||||
{
|
||||
EGLint attrib_list[] = { LOCAL_EGL_PLATFORM_ANGLE_TYPE_ANGLE,
|
||||
LOCAL_EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE,
|
||||
LOCAL_EGL_NONE };
|
||||
EGLDisplay display = egl.fGetPlatformDisplayEXT(LOCAL_EGL_PLATFORM_ANGLE_ANGLE,
|
||||
displayType,
|
||||
attrib_list);
|
||||
|
||||
if (display == EGL_NO_DISPLAY)
|
||||
return EGL_NO_DISPLAY;
|
||||
|
||||
if (!egl.fInitialize(display, nullptr, nullptr))
|
||||
return EGL_NO_DISPLAY;
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsAccelAngleSupported(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
|
||||
{
|
||||
int32_t angleSupport;
|
||||
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_ANGLE, &angleSupport);
|
||||
return (angleSupport == nsIGfxInfo::FEATURE_STATUS_OK);
|
||||
}
|
||||
|
||||
static EGLDisplay
|
||||
GetAndInitDisplay(GLLibraryEGL& egl, void* displayType)
|
||||
{
|
||||
|
@ -117,7 +146,7 @@ GetAndInitDisplay(GLLibraryEGL& egl, void* displayType)
|
|||
}
|
||||
|
||||
bool
|
||||
GLLibraryEGL::EnsureInitialized()
|
||||
GLLibraryEGL::EnsureInitialized(bool forceAccel)
|
||||
{
|
||||
if (mInitialized) {
|
||||
return true;
|
||||
|
@ -277,42 +306,60 @@ GLLibraryEGL::EnsureInitialized()
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
mEGLDisplay = GetAndInitDisplay(*this, EGL_DEFAULT_DISPLAY);
|
||||
|
||||
const char* vendor = (char*)fQueryString(mEGLDisplay, LOCAL_EGL_VENDOR);
|
||||
if (vendor && (strstr(vendor, "TransGaming") != 0 ||
|
||||
strstr(vendor, "Google Inc.") != 0))
|
||||
{
|
||||
mIsANGLE = true;
|
||||
}
|
||||
mEGLDisplay = EGL_NO_DISPLAY;
|
||||
// Check the ANGLE support the system has
|
||||
nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
|
||||
mIsANGLE = IsExtensionSupported(ANGLE_platform_angle_d3d);
|
||||
|
||||
if (mIsANGLE) {
|
||||
EGLDisplay newDisplay = EGL_NO_DISPLAY;
|
||||
bool accelAngleSupport = IsAccelAngleSupported(gfxInfo);
|
||||
bool warpAngleSupport = gfxPlatform::CanUseDirect3D11ANGLE();
|
||||
|
||||
// D3D11 ANGLE only works with OMTC; there's a bug in the non-OMTC layer
|
||||
// manager, and it's pointless to try to fix it. We also don't try
|
||||
// D3D11 ANGLE if the layer manager is prefering D3D9 (hrm, do we care?)
|
||||
if (gfxPrefs::LayersOffMainThreadCompositionEnabled() &&
|
||||
!gfxPrefs::LayersPreferD3D9())
|
||||
{
|
||||
if (gfxPrefs::WebGLANGLEForceD3D11()) {
|
||||
newDisplay = GetAndInitDisplay(*this,
|
||||
LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE);
|
||||
} else if (gfxPrefs::WebGLANGLETryD3D11() && gfxPlatform::CanUseDirect3D11ANGLE()) {
|
||||
newDisplay = GetAndInitDisplay(*this,
|
||||
LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE);
|
||||
bool shouldTryAccel = forceAccel || accelAngleSupport;
|
||||
bool shouldTryWARP = !shouldTryAccel && warpAngleSupport;
|
||||
if (gfxPrefs::WebGLANGLEForceWARP()) {
|
||||
shouldTryWARP = true;
|
||||
shouldTryAccel = false;
|
||||
}
|
||||
|
||||
// Fallback to a WARP display if non-WARP is blacklisted,
|
||||
// or if WARP is forced
|
||||
if (shouldTryWARP) {
|
||||
mEGLDisplay = GetAndInitWARPDisplay(*this,
|
||||
EGL_DEFAULT_DISPLAY);
|
||||
if (mEGLDisplay != EGL_NO_DISPLAY) {
|
||||
mIsWARP = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (newDisplay != EGL_NO_DISPLAY) {
|
||||
DebugOnly<EGLBoolean> success = fTerminate(mEGLDisplay);
|
||||
MOZ_ASSERT(success == LOCAL_EGL_TRUE);
|
||||
|
||||
mEGLDisplay = newDisplay;
|
||||
|
||||
vendor = (char*)fQueryString(mEGLDisplay, LOCAL_EGL_VENDOR);
|
||||
// If falling back to WARP did not work and we don't want to try
|
||||
// using HW accelerated ANGLE, then fail
|
||||
if (mEGLDisplay == EGL_NO_DISPLAY && !shouldTryAccel) {
|
||||
NS_ERROR("Fallback WARP ANGLE context failed to initialize.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hardware accelerated ANGLE path
|
||||
if (mEGLDisplay == EGL_NO_DISPLAY && shouldTryAccel) {
|
||||
// D3D11 ANGLE only works with OMTC; there's a bug in the non-OMTC layer
|
||||
// manager, and it's pointless to try to fix it. We also don't try
|
||||
// D3D11 ANGLE if the layer manager is prefering D3D9 (hrm, do we care?)
|
||||
if (gfxPrefs::LayersOffMainThreadCompositionEnabled() &&
|
||||
!gfxPrefs::LayersPreferD3D9())
|
||||
{
|
||||
if (gfxPrefs::WebGLANGLEForceD3D11()) {
|
||||
mEGLDisplay = GetAndInitDisplay(*this,
|
||||
LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE);
|
||||
} else if (gfxPrefs::WebGLANGLETryD3D11() && gfxPlatform::CanUseDirect3D11ANGLE()) {
|
||||
mEGLDisplay = GetAndInitDisplay(*this,
|
||||
LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mEGLDisplay == EGL_NO_DISPLAY) {
|
||||
mEGLDisplay = GetAndInitDisplay(*this, EGL_DEFAULT_DISPLAY);
|
||||
}
|
||||
|
||||
InitExtensionsFromDisplay(mEGLDisplay);
|
||||
|
|
|
@ -108,7 +108,8 @@ public:
|
|||
GLLibraryEGL()
|
||||
: mInitialized(false),
|
||||
mEGLLibrary(nullptr),
|
||||
mIsANGLE(false)
|
||||
mIsANGLE(false),
|
||||
mIsWARP(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -469,6 +470,10 @@ public:
|
|||
return mIsANGLE;
|
||||
}
|
||||
|
||||
bool IsWARP() const {
|
||||
return mIsWARP;
|
||||
}
|
||||
|
||||
bool HasKHRImageBase() {
|
||||
return IsExtensionSupported(KHR_image) || IsExtensionSupported(KHR_image_base);
|
||||
}
|
||||
|
@ -489,7 +494,7 @@ public:
|
|||
return IsExtensionSupported(EXT_create_context_robustness);
|
||||
}
|
||||
|
||||
bool EnsureInitialized();
|
||||
bool EnsureInitialized(bool forceAccel = false);
|
||||
|
||||
void DumpEGLConfig(EGLConfig cfg);
|
||||
void DumpEGLConfigs();
|
||||
|
@ -619,6 +624,7 @@ private:
|
|||
EGLDisplay mEGLDisplay;
|
||||
|
||||
bool mIsANGLE;
|
||||
bool mIsWARP;
|
||||
};
|
||||
|
||||
extern GLLibraryEGL sEGLLibrary;
|
||||
|
|
|
@ -98,7 +98,10 @@ ClientCanvasLayer::Initialize(const Data& aData)
|
|||
}
|
||||
case mozilla::layers::LayersBackend::LAYERS_D3D11: {
|
||||
#ifdef XP_WIN
|
||||
// Enable surface sharing only if ANGLE and compositing devices
|
||||
// are both WARP or both not WARP
|
||||
if (mGLContext->IsANGLE() &&
|
||||
(mGLContext->IsWARP() == gfxWindowsPlatform::GetPlatform()->IsWARP()) &&
|
||||
gfxWindowsPlatform::GetPlatform()->DoesD3D11TextureSharingWork())
|
||||
{
|
||||
factory = SurfaceFactory_ANGLEShareHandle::Create(mGLContext, caps, forwarder,
|
||||
|
|
|
@ -953,7 +953,7 @@ ClientTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
|
|||
js::ProfileEntry::Category::GRAPHICS);
|
||||
|
||||
mNewValidRegion = aNewValidRegion;
|
||||
Update(aNewValidRegion, aPaintRegion);
|
||||
Update(aNewValidRegion, aPaintRegion, aDirtyRegion);
|
||||
|
||||
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
|
||||
if (PR_IntervalNow() - start > 10) {
|
||||
|
@ -1049,7 +1049,8 @@ void PadDrawTargetOutFromRegion(RefPtr<DrawTarget> drawTarget, nsIntRegion ®i
|
|||
}
|
||||
|
||||
void
|
||||
ClientTiledLayerBuffer::PostValidate(const nsIntRegion& aPaintRegion)
|
||||
ClientTiledLayerBuffer::PostValidate(const nsIntRegion& aPaintRegion,
|
||||
const nsIntRegion& aDirtyRegion)
|
||||
{
|
||||
if (gfxPrefs::TiledDrawTargetEnabled() && mMoz2DTiles.size() > 0) {
|
||||
gfx::TileSet tileset;
|
||||
|
@ -1065,7 +1066,7 @@ ClientTiledLayerBuffer::PostValidate(const nsIntRegion& aPaintRegion)
|
|||
ctx->SetMatrix(
|
||||
ctx->CurrentMatrix().Scale(mResolution, mResolution).Translate(ThebesPoint(-mTilingOrigin)));
|
||||
|
||||
mCallback(mPaintedLayer, ctx, aPaintRegion, aPaintRegion,
|
||||
mCallback(mPaintedLayer, ctx, aPaintRegion, aDirtyRegion,
|
||||
DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
|
||||
mMoz2DTiles.clear();
|
||||
// Reset:
|
||||
|
@ -1095,7 +1096,8 @@ ClientTiledLayerBuffer::UnlockTile(TileClient& aTile)
|
|||
}
|
||||
|
||||
void ClientTiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
|
||||
const nsIntRegion& aPaintRegion)
|
||||
const nsIntRegion& aPaintRegion,
|
||||
const nsIntRegion& aDirtyRegion)
|
||||
{
|
||||
const IntSize scaledTileSize = GetScaledTileSize();
|
||||
const gfx::IntRect newBounds = newValidRegion.GetBounds();
|
||||
|
@ -1148,7 +1150,7 @@ void ClientTiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
|
|||
}
|
||||
}
|
||||
|
||||
PostValidate(aPaintRegion);
|
||||
PostValidate(aPaintRegion, aDirtyRegion);
|
||||
|
||||
for (TileClient& tile : mRetainedTiles) {
|
||||
UnlockTile(tile);
|
||||
|
|
|
@ -419,7 +419,9 @@ public:
|
|||
LayerManager::DrawPaintedLayerCallback aCallback,
|
||||
void* aCallbackData);
|
||||
|
||||
void Update(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion);
|
||||
void Update(const nsIntRegion& aNewValidRegion,
|
||||
const nsIntRegion& aPaintRegion,
|
||||
const nsIntRegion& aDirtyRegion);
|
||||
|
||||
void ReadLock();
|
||||
|
||||
|
@ -449,7 +451,7 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
Update(nsIntRegion(), nsIntRegion());
|
||||
Update(nsIntRegion(), nsIntRegion(), nsIntRegion());
|
||||
mResolution = aResolution;
|
||||
}
|
||||
|
||||
|
@ -467,7 +469,8 @@ protected:
|
|||
const nsIntPoint& aTileRect,
|
||||
const nsIntRegion& dirtyRect);
|
||||
|
||||
void PostValidate(const nsIntRegion& aPaintRegion);
|
||||
void PostValidate(const nsIntRegion& aPaintRegion,
|
||||
const nsIntRegion& aDirtyRegion);
|
||||
|
||||
void UnlockTile(TileClient& aTile);
|
||||
|
||||
|
|
|
@ -1264,6 +1264,7 @@ CompositorParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
|
|||
// race condition.
|
||||
mLayerManager->UpdateRenderBounds(aTargetConfig.naturalBounds());
|
||||
mLayerManager->SetRegionToClear(aTargetConfig.clearRegion());
|
||||
mLayerManager->GetCompositor()->SetScreenRotation(aTargetConfig.rotation());
|
||||
|
||||
mCompositionManager->Updated(aIsFirstPaint, aTargetConfig);
|
||||
Layer* root = aLayerTree->GetRoot();
|
||||
|
|
|
@ -245,11 +245,6 @@ LayerTransactionParent::RecvUpdate(InfallibleTArray<Edit>&& cset,
|
|||
return true;
|
||||
}
|
||||
|
||||
if (mLayerManager && mLayerManager->GetCompositor() &&
|
||||
!targetConfig.naturalBounds().IsEmpty()) {
|
||||
mLayerManager->GetCompositor()->SetScreenRotation(targetConfig.rotation());
|
||||
}
|
||||
|
||||
EditReplyVector replyv;
|
||||
AutoLayerTransactionParentAsyncMessageSender autoAsyncMessageSender(this);
|
||||
|
||||
|
|
|
@ -381,6 +381,7 @@ private:
|
|||
DECL_GFX_PREF(Live, "ui.click_hold_context_menus.delay", UiClickHoldContextMenusDelay, int32_t, 500);
|
||||
DECL_GFX_PREF(Once, "webgl.angle.force-d3d11", WebGLANGLEForceD3D11, bool, false);
|
||||
DECL_GFX_PREF(Once, "webgl.angle.try-d3d11", WebGLANGLETryD3D11, bool, false);
|
||||
DECL_GFX_PREF(Once, "webgl.angle.force-warp", WebGLANGLEForceWARP, bool, false);
|
||||
DECL_GFX_PREF(Live, "webgl.disable-fail-if-major-performance-caveat",
|
||||
WebGLDisableFailIfMajorPerformanceCaveat, bool, false);
|
||||
DECL_GFX_PREF(Once, "webgl.force-layers-readback", WebGLForceLayersReadback, bool, false);
|
||||
|
|
|
@ -2122,6 +2122,12 @@ gfxWindowsPlatform::InitD3D11Devices()
|
|||
mD3D11Status = FeatureStatus::Failed;
|
||||
}
|
||||
|
||||
// Only test for texture sharing on Windows 8 since it puts the device into
|
||||
// an unusable state if used on Windows 7
|
||||
if (mD3D11Device && IsWin8OrLater()) {
|
||||
mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device);
|
||||
}
|
||||
|
||||
if (!mD3D11Device) {
|
||||
// We could not get a D3D11 compositor, and there's nothing more we can try.
|
||||
return;
|
||||
|
|
|
@ -18,7 +18,7 @@ import re
|
|||
|
||||
def env(config):
|
||||
e = dict(os.environ)
|
||||
e['PATH'] = '%s:%s' % (e['PATH'], config['sixgill_bin'])
|
||||
e['PATH'] = ':'.join(p for p in (config.get('gcc_bin'), config.get('sixgill_bin'), e['PATH']) if p)
|
||||
e['XDB'] = '%(sixgill_bin)s/xdb.so' % config
|
||||
e['SOURCE'] = config['source']
|
||||
e['ANALYZED_OBJDIR'] = config['objdir']
|
||||
|
|
|
@ -280,27 +280,34 @@ function edgeCanGC(edge)
|
|||
{
|
||||
if (edge.Kind != "Call")
|
||||
return false;
|
||||
|
||||
var callee = edge.Exp[0];
|
||||
|
||||
while (callee.Kind == "Drf")
|
||||
callee = callee.Exp[0];
|
||||
|
||||
if (callee.Kind == "Var") {
|
||||
var variable = callee.Variable;
|
||||
assert(variable.Kind == "Func");
|
||||
var callee = mangled(variable.Name[0]);
|
||||
if ((callee in gcFunctions) || ((callee + internalMarker) in gcFunctions))
|
||||
return "'" + variable.Name[0] + "'";
|
||||
return null;
|
||||
|
||||
if (variable.Kind == "Func") {
|
||||
var callee = mangled(variable.Name[0]);
|
||||
if ((callee in gcFunctions) || ((callee + internalMarker) in gcFunctions))
|
||||
return "'" + variable.Name[0] + "'";
|
||||
return null;
|
||||
}
|
||||
|
||||
var varName = variable.Name[0];
|
||||
return indirectCallCannotGC(functionName, varName) ? null : "*" + varName;
|
||||
}
|
||||
assert(callee.Kind == "Drf");
|
||||
if (callee.Exp[0].Kind == "Fld") {
|
||||
var field = callee.Exp[0].Field;
|
||||
|
||||
if (callee.Kind == "Fld") {
|
||||
var field = callee.Field;
|
||||
var csuName = field.FieldCSU.Type.Name;
|
||||
var fullFieldName = csuName + "." + field.Name[0];
|
||||
if (fieldCallCannotGC(csuName, fullFieldName))
|
||||
return null;
|
||||
return (fullFieldName in suppressedFunctions) ? null : fullFieldName;
|
||||
}
|
||||
assert(callee.Exp[0].Kind == "Var");
|
||||
var varName = callee.Exp[0].Variable.Name[0];
|
||||
return indirectCallCannotGC(functionName, varName) ? null : "*" + varName;
|
||||
}
|
||||
|
||||
// Search recursively through predecessors from a variable use, returning
|
||||
|
|
|
@ -245,21 +245,6 @@ function ignoreGCFunction(mangled)
|
|||
return false;
|
||||
}
|
||||
|
||||
function isRootedTypeName(name)
|
||||
{
|
||||
if (name == "mozilla::ErrorResult" ||
|
||||
name == "JSErrorResult" ||
|
||||
name == "WrappableJSErrorResult" ||
|
||||
name == "js::frontend::TokenStream" ||
|
||||
name == "js::frontend::TokenStream::Position" ||
|
||||
name == "ModuleCompiler" ||
|
||||
name == "JSAddonId")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function stripUCSAndNamespace(name)
|
||||
{
|
||||
if (name.startsWith('struct '))
|
||||
|
@ -282,16 +267,36 @@ function stripUCSAndNamespace(name)
|
|||
return name;
|
||||
}
|
||||
|
||||
function isRootedPointerTypeName(name)
|
||||
function isRootedGCTypeName(name)
|
||||
{
|
||||
return (name == "JSAddonId");
|
||||
}
|
||||
|
||||
function isRootedGCPointerTypeName(name)
|
||||
{
|
||||
name = stripUCSAndNamespace(name);
|
||||
|
||||
if (name.startsWith('MaybeRooted<'))
|
||||
return /\(js::AllowGC\)1u>::RootType/.test(name);
|
||||
|
||||
if (name == "ErrorResult" ||
|
||||
name == "JSErrorResult" ||
|
||||
name == "WrappableJSErrorResult" ||
|
||||
name == "frontend::TokenStream" ||
|
||||
name == "frontend::TokenStream::Position" ||
|
||||
name == "ModuleCompiler")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return name.startsWith('Rooted') || name.startsWith('PersistentRooted');
|
||||
}
|
||||
|
||||
function isRootedTypeName(name)
|
||||
{
|
||||
return isRootedGCTypeName(name) || isRootedGCPointerTypeName(name);
|
||||
}
|
||||
|
||||
function isUnsafeStorage(typeName)
|
||||
{
|
||||
typeName = stripUCSAndNamespace(typeName);
|
||||
|
@ -357,6 +362,10 @@ function listGCPointers() {
|
|||
'JS::Value',
|
||||
'jsid',
|
||||
|
||||
'js::TypeSet',
|
||||
'js::TypeSet::ObjectKey',
|
||||
'js::TypeSet::Type',
|
||||
|
||||
// AutoCheckCannotGC should also not be held live across a GC function.
|
||||
'JS::AutoCheckCannotGC',
|
||||
];
|
||||
|
|
|
@ -74,44 +74,60 @@ var gcTypes = {}; // map from parent struct => Set of GC typed children
|
|||
var gcPointers = {}; // map from parent struct => Set of GC typed children
|
||||
var nonGCTypes = {}; // set of types that would ordinarily be GC types but we are suppressing
|
||||
var nonGCPointers = {}; // set of types that would ordinarily be GC pointers but we are suppressing
|
||||
var gcFields = {};
|
||||
var gcFields = new Map;
|
||||
|
||||
// "typeName is a (pointer to a)*'depth' GC type because it contains a field
|
||||
// named 'child' of type 'why' (or pointer to 'why' if ptrdness == 1), which
|
||||
function stars(n) { return n ? '*' + stars(n-1) : '' };
|
||||
|
||||
// "typeName is a (pointer to a)^'typePtrLevel' GC type because it contains a field
|
||||
// named 'child' of type 'why' (or pointer to 'why' if fieldPtrLevel == 1), which is
|
||||
// itself a GCThing or GCPointer."
|
||||
function markGCType(typeName, child, why, depth, ptrdness)
|
||||
function markGCType(typeName, child, why, typePtrLevel, fieldPtrLevel, indent)
|
||||
{
|
||||
//printErr(`${indent}${typeName}${stars(typePtrLevel)} may be a gctype/ptr because of its child '${child}' of type ${why}${stars(fieldPtrLevel)}`);
|
||||
|
||||
// Some types, like UniquePtr, do not mark/trace/relocate their contained
|
||||
// pointers and so should not hold them live across a GC. UniquePtr in
|
||||
// particular should be the only thing pointing to a structure containing a
|
||||
// GCPointer, so nothing else can be tracing it and it'll die when the
|
||||
// UniquePtr goes out of scope. So we say that a UniquePtr's memory is just
|
||||
// as unsafe as the stack for storing GC pointers.
|
||||
if (!ptrdness && isUnsafeStorage(typeName)) {
|
||||
// GCPointer, so nothing else can possibly trace it and it'll die when the
|
||||
// UniquePtr goes out of scope. So we say that memory pointed to by a
|
||||
// UniquePtr is just as unsafe as the stack for storing GC pointers.
|
||||
if (!fieldPtrLevel && isUnsafeStorage(typeName)) {
|
||||
// The UniquePtr itself is on the stack but when you dereference the
|
||||
// contained pointer, you get to the unsafe memory that we are treating
|
||||
// as if it were the stack (aka depth 0). Note that
|
||||
// as if it were the stack (aka ptrLevel 0). Note that
|
||||
// UniquePtr<UniquePtr<JSObject*>> is fine, so we don't want to just
|
||||
// hardcode the depth.
|
||||
ptrdness = -1;
|
||||
// hardcode the ptrLevel.
|
||||
fieldPtrLevel = -1;
|
||||
}
|
||||
|
||||
depth += ptrdness;
|
||||
if (depth > 2)
|
||||
// Example: with:
|
||||
// struct Pair { JSObject* foo; int bar; };
|
||||
// struct { Pair** info }***
|
||||
// make a call to:
|
||||
// child='info' typePtrLevel=3 fieldPtrLevel=2
|
||||
// for a final ptrLevel of 5, used to later call:
|
||||
// child='foo' typePtrLevel=5 fieldPtrLevel=1
|
||||
//
|
||||
var ptrLevel = typePtrLevel + fieldPtrLevel;
|
||||
|
||||
// ...except when > 2 levels of pointers away from an actual GC thing, stop
|
||||
// searching the graph. (This would just be > 1, except that a UniquePtr
|
||||
// field might still have a GC pointer.)
|
||||
if (ptrLevel > 2)
|
||||
return;
|
||||
|
||||
if (depth == 0 && isRootedTypeName(typeName))
|
||||
if (ptrLevel == 0 && isRootedGCTypeName(typeName))
|
||||
return;
|
||||
if (depth == 1 && isRootedPointerTypeName(typeName))
|
||||
if (ptrLevel == 1 && isRootedGCPointerTypeName(typeName))
|
||||
return;
|
||||
|
||||
if (depth == 0) {
|
||||
if (ptrLevel == 0) {
|
||||
if (typeName in nonGCTypes)
|
||||
return;
|
||||
if (!(typeName in gcTypes))
|
||||
gcTypes[typeName] = new Set();
|
||||
gcTypes[typeName].add(why);
|
||||
} else if (depth == 1) {
|
||||
} else if (ptrLevel == 1) {
|
||||
if (typeName in nonGCPointers)
|
||||
return;
|
||||
if (!(typeName in gcPointers))
|
||||
|
@ -119,32 +135,34 @@ function markGCType(typeName, child, why, depth, ptrdness)
|
|||
gcPointers[typeName].add(why);
|
||||
}
|
||||
|
||||
if (!(typeName in gcFields))
|
||||
gcFields[typeName] = new Map();
|
||||
gcFields[typeName].set(why, [ child, ptrdness ]);
|
||||
if (ptrLevel < 2) {
|
||||
if (!gcFields.has(typeName))
|
||||
gcFields.set(typeName, new Map());
|
||||
gcFields.get(typeName).set(child, [ why, fieldPtrLevel ]);
|
||||
}
|
||||
|
||||
if (typeName in structureParents) {
|
||||
for (var field of structureParents[typeName]) {
|
||||
var [ holderType, fieldName ] = field;
|
||||
markGCType(holderType, typeName, fieldName, depth, 0);
|
||||
markGCType(holderType, fieldName, typeName, ptrLevel, 0, indent + " ");
|
||||
}
|
||||
}
|
||||
if (typeName in pointerParents) {
|
||||
for (var field of pointerParents[typeName]) {
|
||||
var [ holderType, fieldName ] = field;
|
||||
markGCType(holderType, typeName, fieldName, depth, 1);
|
||||
markGCType(holderType, fieldName, typeName, ptrLevel, 1, indent + " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addGCType(typeName, child, why, depth, ptrdness)
|
||||
function addGCType(typeName, child, why, depth, fieldPtrLevel)
|
||||
{
|
||||
markGCType(typeName, 'annotation', '<annotation>', 0, 0);
|
||||
markGCType(typeName, '<annotation>', '(annotation)', 0, 0, "");
|
||||
}
|
||||
|
||||
function addGCPointer(typeName)
|
||||
{
|
||||
markGCType(typeName, 'annotation', '<pointer-annotation>', 1, 0);
|
||||
markGCType(typeName, '<pointer-annotation>', '(annotation)', 1, 0, "");
|
||||
}
|
||||
|
||||
for (var type of listNonGCTypes())
|
||||
|
@ -163,17 +181,19 @@ function explain(csu, indent, seen) {
|
|||
if (!seen)
|
||||
seen = new Set();
|
||||
seen.add(csu);
|
||||
if (!(csu in gcFields))
|
||||
if (!gcFields.has(csu))
|
||||
return;
|
||||
if (gcFields[csu].has('<annotation>')) {
|
||||
var fields = gcFields.get(csu);
|
||||
|
||||
if (fields.has('<annotation>')) {
|
||||
print(indent + "which is a GCThing because I said so");
|
||||
return;
|
||||
}
|
||||
if (gcFields[csu].has('<pointer-annotation>')) {
|
||||
if (fields.has('<pointer-annotation>')) {
|
||||
print(indent + "which is a GCPointer because I said so");
|
||||
return;
|
||||
}
|
||||
for (var [ field, [ child, ptrdness ] ] of gcFields[csu]) {
|
||||
for (var [ field, [ child, ptrdness ] ] of fields) {
|
||||
var inherit = "";
|
||||
if (field == "field:0")
|
||||
inherit = " (probably via inheritance)";
|
||||
|
|
|
@ -40,7 +40,7 @@ bool
|
|||
CompileFunctionBody(JSContext* cx, MutableHandleFunction fun,
|
||||
const ReadOnlyCompileOptions& options,
|
||||
const AutoNameVector& formals, JS::SourceBufferHolder& srcBuf,
|
||||
HandleObject enclosingStaticScope);
|
||||
Handle<ScopeObject*> enclosingStaticScope);
|
||||
bool
|
||||
CompileStarGeneratorBody(JSContext* cx, MutableHandleFunction fun,
|
||||
const ReadOnlyCompileOptions& options,
|
||||
|
|
|
@ -406,12 +406,9 @@ IonBuilder::DontInline(JSScript* targetScript, const char* reason)
|
|||
bool
|
||||
IonBuilder::hasCommonInliningPath(const JSScript* scriptToInline)
|
||||
{
|
||||
if (this->script() == scriptToInline)
|
||||
return true;
|
||||
|
||||
// Find all previous inlinings of the |scriptToInline| and check for common
|
||||
// inlining paths with the top of the inlining stack.
|
||||
for (IonBuilder* it = this; it; it = it->callerBuilder_) {
|
||||
for (IonBuilder* it = this->callerBuilder_; it; it = it->callerBuilder_) {
|
||||
if (it->script() != scriptToInline)
|
||||
continue;
|
||||
|
||||
|
|
|
@ -4199,7 +4199,7 @@ CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
|
|||
const char* name, unsigned nargs, const char* const* argnames,
|
||||
SourceBufferHolder& srcBuf,
|
||||
HandleObject enclosingDynamicScope,
|
||||
HandleObject enclosingStaticScope,
|
||||
Handle<ScopeObject*> enclosingStaticScope,
|
||||
MutableHandleFunction fun)
|
||||
{
|
||||
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
|
||||
|
|
|
@ -803,7 +803,7 @@ ObjectGroup::newArrayObject(ExclusiveContext* cx,
|
|||
}
|
||||
|
||||
// Get a type which captures all the elements in the array to be created.
|
||||
TypeSet::Type elementType = TypeSet::UnknownType();
|
||||
Rooted<TypeSet::Type> elementType(cx, TypeSet::UnknownType());
|
||||
if (arrayKind != NewArrayKind::UnknownIndex && length != 0) {
|
||||
elementType = GetValueTypeForTable(vp[0]);
|
||||
for (unsigned i = 1; i < length; i++) {
|
||||
|
@ -860,7 +860,7 @@ ObjectGroup::newArrayObject(ExclusiveContext* cx,
|
|||
group->setPreliminaryObjects(preliminaryObjects);
|
||||
}
|
||||
|
||||
if (!p.add(cx, *table, key, group))
|
||||
if (!p.add(cx, *table, ObjectGroupCompartment::ArrayObjectKey(elementType), group))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -272,7 +272,7 @@ class TypeSet
|
|||
// Information about a single concrete type. We pack this into one word,
|
||||
// where small values are particular primitive or other singleton types and
|
||||
// larger values are either specific JS objects or object groups.
|
||||
class Type
|
||||
class Type : public JS::StaticTraceable
|
||||
{
|
||||
friend class TypeSet;
|
||||
|
||||
|
@ -350,6 +350,10 @@ class TypeSet
|
|||
inline ObjectGroup* group() const;
|
||||
inline ObjectGroup* groupNoBarrier() const;
|
||||
|
||||
static void trace(Type* v, JSTracer* trc) {
|
||||
MarkTypeUnbarriered(trc, v, "TypeSet::Type");
|
||||
}
|
||||
|
||||
bool operator == (Type o) const { return data == o.data; }
|
||||
bool operator != (Type o) const { return data != o.data; }
|
||||
};
|
||||
|
|