From 359792367d3496ed96cc11952d5a4e3dae167310 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Thu, 31 Mar 2022 10:32:03 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1199289 - r=valentin,dao (bfaf064b19) - Bug 1202312 - Remove an old forward declaration and typedef. r=kats (1fc905fe5d) - Bug 1170894 - Implement nsIFrameLoader::SwitchProcessAndLoadURI. r=smaug (194b0e81f6) - Bug 1199765 - Add support to TabParent for querying the active state of remote browsers. r=Mossop (a9b24d4083) - Bug 1165796 - Part 1: Make PrefEnabledRunnable usable for other preference names. r=baku (2e83d3ecc7) - Bug 1168933 - Send referrer when downloading worker script. r=khuey (69e48dea42) - Bug 1165796 - Part 2: Implement PerformanceObserver.r=baku (9f16f01051) - Bug 1165796 - Part 0: Unified build fix. r=baku (0605bf4397) - Bug 1165796 - Part 3: Fix PerformanceObserverEntryList::GetEntries filtering for initiatorType. r=baku (12d07cee20) - Bug 1192787 - Readd performance enabled test to ResponseEndHighRes; r=baku (f75fbaba13) - Bug 1197003 - Part 1 - PerformanceObserver::Disconenct() should be called before mPerformance is destroyed. r=baku (d4dd3a960b) - Bug 1195700 - Disconnect performance observer before being destroyed to avoid crash. r=baku (e9e743d4d2) - Bug 1197003 - Part 2 - Implement processing algorithm for PerformanceObserver to notify a batch of entries. r=baku (4a0432765f) - Bug 1155761 followup: Annotate nsPerformance::InsertUserEntry as 'override'. rs=ehsan (0552b7f75f) --- .../test/general/browser_urlHighlight.js | 118 +++++++++ .../test/general/browser_urlbarTrimURLs.js | 105 ++++++++ browser/base/content/urlbarBindings.xml | 58 +++-- docshell/base/nsDefaultURIFixup.cpp | 40 +++- .../unit/test_nsDefaultURIFixup_search.js | 60 +++++ dom/base/PerformanceEntry.h | 6 + dom/base/PerformanceObserver.cpp | 185 ++++++++++++++ dom/base/PerformanceObserver.h | 79 ++++++ dom/base/PerformanceObserverEntryList.cpp | 96 ++++++++ dom/base/PerformanceObserverEntryList.h | 65 +++++ dom/base/PerformanceResourceTiming.h | 5 + dom/base/moz.build | 4 + dom/base/nsAttrValue.cpp | 5 + dom/base/nsFrameLoader.cpp | 87 +++++++ dom/base/nsFrameLoader.h | 2 + dom/base/nsIFrameLoader.idl | 10 +- dom/base/nsPerformance.cpp | 124 +++++++++- dom/base/nsPerformance.h | 17 +- dom/base/test/mochitest.ini | 6 +- dom/base/test/performance_observer.html | 74 ++++++ .../test/test_frameLoader_switchProcess.html | 84 +++++++ dom/base/test/test_performance_observer.html | 17 ++ dom/base/test/test_performance_observer.js | 226 ++++++++++++++++++ dom/browser-element/BrowserElementParent.js | 46 ++-- dom/html/nsBrowserElement.cpp | 9 +- dom/interfaces/base/nsITabParent.idl | 7 +- dom/ipc/PBrowser.ipdl | 4 +- dom/ipc/TabChild.cpp | 2 +- dom/ipc/TabChild.h | 4 +- dom/ipc/TabParent.cpp | 44 +++- dom/ipc/TabParent.h | 7 + dom/webidl/Performance.webidl | 1 + dom/webidl/PerformanceObserver.webidl | 23 ++ .../PerformanceObserverEntryList.webidl | 24 ++ dom/webidl/moz.build | 2 + dom/workers/ScriptLoader.cpp | 8 + dom/workers/test/mochitest.ini | 5 + dom/workers/test/performance_observer.html | 32 +++ dom/workers/test/referrer.sjs | 11 + .../test/test_performance_observer.html | 17 ++ dom/workers/test/test_referrer.html | 35 +++ .../test/worker_performance_observer.js | 4 + toolkit/content/widgets/remote-browser.xml | 5 +- 43 files changed, 1704 insertions(+), 59 deletions(-) create mode 100644 browser/base/content/test/general/browser_urlHighlight.js create mode 100644 browser/base/content/test/general/browser_urlbarTrimURLs.js create mode 100644 dom/base/PerformanceObserver.cpp create mode 100644 dom/base/PerformanceObserver.h create mode 100644 dom/base/PerformanceObserverEntryList.cpp create mode 100644 dom/base/PerformanceObserverEntryList.h create mode 100644 dom/base/test/performance_observer.html create mode 100644 dom/base/test/test_frameLoader_switchProcess.html create mode 100644 dom/base/test/test_performance_observer.html create mode 100644 dom/base/test/test_performance_observer.js create mode 100644 dom/webidl/PerformanceObserver.webidl create mode 100644 dom/webidl/PerformanceObserverEntryList.webidl create mode 100644 dom/workers/test/performance_observer.html create mode 100644 dom/workers/test/referrer.sjs create mode 100644 dom/workers/test/test_performance_observer.html create mode 100644 dom/workers/test/test_referrer.html create mode 100644 dom/workers/test/worker_performance_observer.js diff --git a/browser/base/content/test/general/browser_urlHighlight.js b/browser/base/content/test/general/browser_urlHighlight.js new file mode 100644 index 0000000000..725f04dc68 --- /dev/null +++ b/browser/base/content/test/general/browser_urlHighlight.js @@ -0,0 +1,118 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function testVal(aExpected) { + gURLBar.value = aExpected.replace(/[<>]/g, ""); + + let selectionController = gURLBar.editor.selectionController; + let selection = selectionController.getSelection(selectionController.SELECTION_URLSECONDARY); + let value = gURLBar.editor.rootElement.textContent; + let result = ""; + for (let i = 0; i < selection.rangeCount; i++) { + let range = selection.getRangeAt(i).toString(); + let pos = value.indexOf(range); + result += value.substring(0, pos) + "<" + range + ">"; + value = value.substring(pos + range.length); + } + result += value; + is(result, aExpected, + "Correct part of the urlbar contents is highlighted"); +} + +function test() { + const prefname = "browser.urlbar.formatting.enabled"; + + registerCleanupFunction(function () { + Services.prefs.clearUserPref(prefname); + URLBarSetURI(); + }); + + Services.prefs.setBoolPref(prefname, true); + + gURLBar.focus(); + + testVal("https://mozilla.org"); + + gBrowser.selectedBrowser.focus(); + + testVal("mozilla.org"); + testVal("mözilla.org"); + testVal("mozilla.imaginatory"); + + testVal("mozilla.org"); + testVal("mozilla.org"); + testVal("mozilla.org"); + testVal("mozilla.org"); + testVal("mozilla.org"); + testVal("mozilla.org"); + testVal("mozilla.com"); + testVal("mozilla.com"); + testVal("mozilla.com"); + + testVal("mozilla.org"); + testVal("mozilla.org"); + + testVal("mozilla.org"); + testVal("mozilla.org"); + testVal("mozilla.org"); + testVal("mozilla.org"); + testVal("mozilla.org"); + testVal("mozilla.org"); + + testVal("mozilla.org"); + testVal("mozilla.org"); + testVal("mozilla.org"); + testVal("mozilla.org"); + testVal("mozilla.org"); + testVal("mozilla.org"); + + testVal("mozilla.org<:666/file.ext>"); + testVal("mozilla.org<:666/file.ext>"); + testVal("localhost<:666/file.ext>"); + + let IPs = ["192.168.1.1", + "[::]", + "[::1]", + "[1::]", + "[::]", + "[::1]", + "[1::]", + "[1:2:3:4:5:6:7::]", + "[::1:2:3:4:5:6:7]", + "[1:2:a:B:c:D:e:F]", + "[1::8]", + "[1:2::8]", + "[fe80::222:19ff:fe11:8c76]", + "[0000:0123:4567:89AB:CDEF:abcd:ef00:0000]", + "[::192.168.1.1]", + "[1::0.0.0.0]", + "[1:2::255.255.255.255]", + "[1:2:3::255.255.255.255]", + "[1:2:3:4::255.255.255.255]", + "[1:2:3:4:5::255.255.255.255]", + "[1:2:3:4:5:6:255.255.255.255]"]; + IPs.forEach(function (IP) { + testVal(IP); + testVal(IP + ""); + testVal(IP + "<:666/file.ext>"); + testVal("" + IP); + testVal("" + IP + ""); + testVal("" + IP + "<:666/file.ext>"); + testVal("" + IP + "<:666/file.ext>"); + }); + + testVal("mailto:admin@mozilla.org"); + testVal("gopher://mozilla.org/"); + testVal("about:config"); + testVal("jar:http://mozilla.org/example.jar!/"); + testVal("view-source:http://mozilla.org/"); + testVal("foo9://mozilla.org/"); + testVal("foo+://mozilla.org/"); + testVal("foo.://mozilla.org/"); + testVal("foo-://mozilla.org/"); + + Services.prefs.setBoolPref(prefname, false); + + testVal("https://mozilla.org"); +} diff --git a/browser/base/content/test/general/browser_urlbarTrimURLs.js b/browser/base/content/test/general/browser_urlbarTrimURLs.js new file mode 100644 index 0000000000..781729ed0a --- /dev/null +++ b/browser/base/content/test/general/browser_urlbarTrimURLs.js @@ -0,0 +1,105 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function testVal(originalValue, targetValue) { + gURLBar.value = originalValue; + gURLBar.valueIsTyped = false; + is(gURLBar.textValue, targetValue || originalValue, "url bar value set"); +} + +function test() { + const prefname = "browser.urlbar.trimURLs"; + + gBrowser.selectedTab = gBrowser.addTab(); + + registerCleanupFunction(function () { + gBrowser.removeCurrentTab(); + Services.prefs.clearUserPref(prefname); + URLBarSetURI(); + }); + + Services.prefs.setBoolPref(prefname, true); + + testVal("http://mozilla.org/", "mozilla.org"); + testVal("https://mozilla.org/", "https://mozilla.org"); + testVal("http://mözilla.org/", "mözilla.org"); + testVal("http://mozilla.imaginatory/", "mozilla.imaginatory"); + testVal("http://www.mozilla.org/", "www.mozilla.org"); + testVal("http://sub.mozilla.org/", "sub.mozilla.org"); + testVal("http://sub1.sub2.sub3.mozilla.org/", "sub1.sub2.sub3.mozilla.org"); + testVal("http://mozilla.org/file.ext", "mozilla.org/file.ext"); + testVal("http://mozilla.org/sub/", "mozilla.org/sub/"); + + testVal("http://ftp.mozilla.org/", "http://ftp.mozilla.org"); + testVal("http://ftp1.mozilla.org/", "http://ftp1.mozilla.org"); + testVal("http://ftp42.mozilla.org/", "http://ftp42.mozilla.org"); + testVal("http://ftpx.mozilla.org/", "ftpx.mozilla.org"); + testVal("ftp://ftp.mozilla.org/", "ftp://ftp.mozilla.org"); + testVal("ftp://ftp1.mozilla.org/", "ftp://ftp1.mozilla.org"); + testVal("ftp://ftp42.mozilla.org/", "ftp://ftp42.mozilla.org"); + testVal("ftp://ftpx.mozilla.org/", "ftp://ftpx.mozilla.org"); + + testVal("https://user:pass@mozilla.org/", "https://user:pass@mozilla.org"); + testVal("https://user@mozilla.org/", "https://user@mozilla.org"); + testVal("http://user:pass@mozilla.org/", "user:pass@mozilla.org"); + testVal("http://user@mozilla.org/", "user@mozilla.org"); + testVal("http://sub.mozilla.org:666/", "sub.mozilla.org:666"); + + testVal("https://[fe80::222:19ff:fe11:8c76]/file.ext"); + testVal("http://[fe80::222:19ff:fe11:8c76]/", "[fe80::222:19ff:fe11:8c76]"); + testVal("https://user:pass@[fe80::222:19ff:fe11:8c76]:666/file.ext"); + testVal("http://user:pass@[fe80::222:19ff:fe11:8c76]:666/file.ext", "user:pass@[fe80::222:19ff:fe11:8c76]:666/file.ext"); + + testVal("mailto:admin@mozilla.org"); + testVal("gopher://mozilla.org/"); + testVal("about:config"); + testVal("jar:http://mozilla.org/example.jar!/"); + testVal("view-source:http://mozilla.org/"); + + // Behaviour for hosts with no dots depends on the whitelist: + let fixupWhitelistPref = "browser.fixup.domainwhitelist.localhost"; + Services.prefs.setBoolPref(fixupWhitelistPref, false); + testVal("http://localhost"); + Services.prefs.setBoolPref(fixupWhitelistPref, true); + testVal("http://localhost", "localhost"); + Services.prefs.clearUserPref(fixupWhitelistPref); + + testVal("http:// invalid url"); + + testVal("http://someotherhostwithnodots"); + testVal("http://localhost/ foo bar baz"); + testVal("http://localhost.localdomain/ foo bar baz", "localhost.localdomain/ foo bar baz"); + + Services.prefs.setBoolPref(prefname, false); + + testVal("http://mozilla.org/"); + + Services.prefs.setBoolPref(prefname, true); + + waitForExplicitFinish(); + + gBrowser.selectedBrowser.addEventListener("load", function () { + gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + + is(gBrowser.currentURI.spec, "http://example.com/", "expected page should have loaded"); + + testCopy("example.com", "http://example.com/", function () { + SetPageProxyState("invalid"); + gURLBar.valueIsTyped = true; + testCopy("example.com", "example.com", finish); + }); + }, true); + + gBrowser.loadURI("http://example.com/"); +} + +function testCopy(originalValue, targetValue, cb) { + waitForClipboard(targetValue, function () { + is(gURLBar.textValue, originalValue, "url bar copy value set"); + + gURLBar.focus(); + gURLBar.select(); + goDoCommand("cmd_copy"); + }, cb, cb); +} diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml index b0aa59f30b..037dd5a3ad 100644 --- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -178,11 +178,32 @@ let textNode = this.editor.rootElement.firstChild; let value = textNode.textContent; - - let protocol = value.match(/^[a-z\d.+\-]+:(?=[^\d])/); - if (protocol && - ["http:", "https:", "ftp:"].indexOf(protocol[0]) == -1) + if (!value) return; + + // Get the URL from the fixup service: + let flags = Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS | + Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP; + let uriInfo = Services.uriFixup.getFixupURIInfo(value, flags); + // Ignore if we couldn't make a URI out of this, the URI resulted in a search, + // or the URI has a non-http(s)/ftp protocol. + if (!uriInfo.fixedURI || + uriInfo.keywordProviderName || + ["http", "https", "ftp"].indexOf(uriInfo.fixedURI.scheme) == -1) { + return; + } + + // If we trimmed off the http scheme, ensure we stick it back on before + // trying to figure out what domain we're accessing, so we don't get + // confused by user:pass@host http URLs. We later use + // trimmedLength to ensure we don't count the length of a trimmed protocol + // when determining which parts of the URL to highlight as "preDomain". + let trimmedLength = 0; + if (uriInfo.fixedURI.scheme == "http" && !value.startsWith("http://")) { + value = "http://" + value; + trimmedLength = "http://".length; + } + let matchedURL = value.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/); if (!matchedURL) return; @@ -190,23 +211,20 @@ let [, preDomain, domain] = matchedURL; let baseDomain = domain; let subDomain = ""; - // getBaseDomainFromHost doesn't recognize IPv6 literals in brackets as IPs (bug 667159) - if (domain[0] != "[") { - try { - baseDomain = Services.eTLD.getBaseDomainFromHost(domain); - if (!domain.endsWith(baseDomain)) { - // getBaseDomainFromHost converts its resultant to ACE. - let IDNService = Cc["@mozilla.org/network/idn-service;1"] - .getService(Ci.nsIIDNService); - baseDomain = IDNService.convertACEtoUTF8(baseDomain); - } - } catch (e) {} - } + try { + baseDomain = Services.eTLD.getBaseDomainFromHost(uriInfo.fixedURI.host); + if (!domain.endsWith(baseDomain)) { + // getBaseDomainFromHost converts its resultant to ACE. + let IDNService = Cc["@mozilla.org/network/idn-service;1"] + .getService(Ci.nsIIDNService); + baseDomain = IDNService.convertACEtoUTF8(baseDomain); + } + } catch (e) {} if (baseDomain != domain) { subDomain = domain.slice(0, -baseDomain.length); } - let rangeLength = preDomain.length + subDomain.length; + let rangeLength = preDomain.length + subDomain.length - trimmedLength; if (rangeLength) { let range = document.createRange(); range.setStart(textNode, 0); @@ -214,11 +232,11 @@ selection.addRange(range); } - let startRest = preDomain.length + domain.length; - if (startRest < value.length) { + let startRest = preDomain.length + domain.length - trimmedLength; + if (startRest < value.length - trimmedLength) { let range = document.createRange(); range.setStart(textNode, startRest); - range.setEnd(textNode, value.length); + range.setEnd(textNode, value.length - trimmedLength); selection.addRange(range); } ]]> diff --git a/docshell/base/nsDefaultURIFixup.cpp b/docshell/base/nsDefaultURIFixup.cpp index ded46a5023..0a36113381 100644 --- a/docshell/base/nsDefaultURIFixup.cpp +++ b/docshell/base/nsDefaultURIFixup.cpp @@ -22,6 +22,7 @@ #include "mozilla/dom/ContentChild.h" #include "mozilla/ipc/InputStreamUtils.h" #include "mozilla/ipc/URIUtils.h" +#include "mozilla/Tokenizer.h" #include "nsIObserverService.h" #include "nsXULAppAPI.h" @@ -124,6 +125,35 @@ nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI, return rv; } +// Returns true if the URL contains a user:password@ or user@ +static bool +HasUserPassword(const nsACString& aStringURI) +{ + mozilla::Tokenizer parser(aStringURI); + mozilla::Tokenizer::Token token; + + // May start with any of "protocol:", "protocol://", "//", "://" + if (parser.Check(Tokenizer::TOKEN_WORD, token)) { // Skip protocol if any + } + if (parser.CheckChar(':')) { // Skip colon if found + } + while (parser.CheckChar('/')) { // Skip all of the following slashes + } + + while (parser.Next(token)) { + if (token.Type() == Tokenizer::TOKEN_CHAR) { + if (token.AsChar() == '/') { + return false; + } + if (token.AsChar() == '@') { + return true; + } + } + } + + return false; +} + NS_IMETHODIMP nsDefaultURIFixup::GetFixupURIInfo(const nsACString& aStringURI, uint32_t aFixupFlags, @@ -336,7 +366,14 @@ nsDefaultURIFixup::GetFixupURIInfo(const nsACString& aStringURI, // It's more likely the user wants to search, and so we // chuck this over to their preferred search provider instead: if (!handlerExists) { - TryKeywordFixupForURIInfo(uriString, info, aPostData); + bool hasUserPassword = HasUserPassword(uriString); + if (!hasUserPassword) { + TryKeywordFixupForURIInfo(uriString, info, aPostData); + } else { + // If the given URL has a user:password we can't just pass it to the + // external protocol handler; we'll try using it with http instead later + info->mFixedURI = nullptr; + } } } } @@ -758,6 +795,7 @@ nsDefaultURIFixup::FixupURIProtocol(const nsACString& aURIString, // ftp.no-scheme.com // ftp4.no-scheme.com // no-scheme.com/query?foo=http://www.foo.com + // user:pass@no-scheme.com // int32_t schemeDelim = uriString.Find("://", 0); int32_t firstDelim = uriString.FindCharInSet("/:"); diff --git a/docshell/test/unit/test_nsDefaultURIFixup_search.js b/docshell/test/unit/test_nsDefaultURIFixup_search.js index 1516652923..acf35ad943 100644 --- a/docshell/test/unit/test_nsDefaultURIFixup_search.js +++ b/docshell/test/unit/test_nsDefaultURIFixup_search.js @@ -1,6 +1,7 @@ let urifixup = Cc["@mozilla.org/docshell/urifixup;1"]. getService(Ci.nsIURIFixup); Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/AppConstants.jsm"); Services.prefs.setBoolPref("keyword.enabled", true); @@ -26,6 +27,8 @@ do_register_cleanup(function() { Services.prefs.clearUserPref("keyword.enabled"); }); +let isWin = AppConstants.platform == "win"; + let data = [ { // Valid should not be changed. @@ -37,8 +40,65 @@ let data = [ wrong: 'whatever://this/is/a/test.html', fixed: kSearchEngineURL.replace("{searchTerms}", encodeURIComponent('whatever://this/is/a/test.html')), }, + + // The following tests check that when a user:password is present in the URL + // `user:` isn't treated as an unknown protocol thus leaking the user and + // password to the search engine. + { + wrong: 'user:pass@example.com/this/is/a/test.html', + fixed: 'http://user:pass@example.com/this/is/a/test.html', + }, + { + wrong: 'user@example.com:8080/this/is/a/test.html', + fixed: 'http://user@example.com:8080/this/is/a/test.html', + }, + { + wrong: 'https:pass@example.com/this/is/a/test.html', + fixed: 'https://pass@example.com/this/is/a/test.html', + }, + { + wrong: 'user:pass@example.com:8080/this/is/a/test.html', + fixed: 'http://user:pass@example.com:8080/this/is/a/test.html', + }, + { + wrong: 'http:user:pass@example.com:8080/this/is/a/test.html', + fixed: 'http://user:pass@example.com:8080/this/is/a/test.html', + }, + { + wrong: 'ttp:user:pass@example.com:8080/this/is/a/test.html', + fixed: 'http://user:pass@example.com:8080/this/is/a/test.html', + }, + { + wrong: 'gobbledygook:user:pass@example.com:8080/this/is/a/test.html', + fixed: 'http://gobbledygook:user%3Apass@example.com:8080/this/is/a/test.html', + }, + { + wrong: 'user:@example.com:8080/this/is/a/test.html', + fixed: 'http://user:@example.com:8080/this/is/a/test.html', + }, + { + wrong: '//user:pass@example.com:8080/this/is/a/test.html', + fixed: (isWin ? "http:" : "file://") + '//user:pass@example.com:8080/this/is/a/test.html', + }, + { + wrong: '://user:pass@example.com:8080/this/is/a/test.html', + fixed: 'http://user:pass@example.com:8080/this/is/a/test.html', + }, + { + wrong: 'whatever://this/is/a@b/test.html', + fixed: kSearchEngineURL.replace("{searchTerms}", encodeURIComponent('whatever://this/is/a@b/test.html')), + }, ]; +let extProtocolSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"] + .getService(Ci.nsIExternalProtocolService); + +if (extProtocolSvc && extProtocolSvc.externalProtocolHandlerExists("mailto")) { + data.push({ + wrong: "mailto:foo@bar.com", + fixed: "mailto:foo@bar.com" + }); +} function run_test() { run_next_test(); diff --git a/dom/base/PerformanceEntry.h b/dom/base/PerformanceEntry.h index ec8d2e8efb..bc4f84f1c2 100644 --- a/dom/base/PerformanceEntry.h +++ b/dom/base/PerformanceEntry.h @@ -15,6 +15,7 @@ class nsISupports; namespace mozilla { namespace dom { +class PerformanceResourceTiming; // http://www.w3.org/TR/performance-timeline/#performanceentry class PerformanceEntry : public nsISupports, @@ -78,6 +79,11 @@ public: return 0; } + virtual const PerformanceResourceTiming* ToResourceTiming() const + { + return nullptr; + } + protected: nsCOMPtr mParent; nsString mName; diff --git a/dom/base/PerformanceObserver.cpp b/dom/base/PerformanceObserver.cpp new file mode 100644 index 0000000000..28c09a8836 --- /dev/null +++ b/dom/base/PerformanceObserver.cpp @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "PerformanceObserver.h" + +#include "mozilla/dom/PerformanceBinding.h" +#include "mozilla/dom/PerformanceEntryBinding.h" +#include "mozilla/dom/PerformanceObserverBinding.h" +#include "mozilla/dom/workers/bindings/Performance.h" +#include "nsPerformance.h" +#include "nsPIDOMWindow.h" +#include "nsQueryObject.h" +#include "nsString.h" +#include "PerformanceEntry.h" +#include "PerformanceObserverEntryList.h" +#include "WorkerPrivate.h" +#include "WorkerScope.h" + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::dom::workers; + +NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceObserver) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PerformanceObserver) + tmp->Disconnect(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(PerformanceObserver) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(PerformanceObserver) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(PerformanceObserver) +NS_IMPL_CYCLE_COLLECTING_RELEASE(PerformanceObserver) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceObserver) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +PerformanceObserver::PerformanceObserver(nsPIDOMWindow* aOwner, + PerformanceObserverCallback& aCb) + : mOwner(aOwner) + , mCallback(&aCb) + , mConnected(false) +{ + MOZ_ASSERT(mOwner); + mPerformance = aOwner->GetPerformance(); +} + +PerformanceObserver::PerformanceObserver(WorkerPrivate* aWorkerPrivate, + PerformanceObserverCallback& aCb) + : mCallback(&aCb) + , mConnected(false) +{ + MOZ_ASSERT(aWorkerPrivate); + mPerformance = aWorkerPrivate->GlobalScope()->GetPerformance(); +} + +PerformanceObserver::~PerformanceObserver() +{ + Disconnect(); + MOZ_ASSERT(!mConnected); +} + +// static +already_AddRefed +PerformanceObserver::Constructor(const GlobalObject& aGlobal, + PerformanceObserverCallback& aCb, + ErrorResult& aRv) +{ + if (NS_IsMainThread()) { + nsCOMPtr ownerWindow = + do_QueryInterface(aGlobal.GetAsSupports()); + if (!ownerWindow) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + MOZ_ASSERT(ownerWindow->IsInnerWindow()); + + nsRefPtr observer = + new PerformanceObserver(ownerWindow, aCb); + return observer.forget(); + } + + JSContext* cx = aGlobal.Context(); + WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx); + MOZ_ASSERT(workerPrivate); + + nsRefPtr observer = + new PerformanceObserver(workerPrivate, aCb); + return observer.forget(); +} + +JSObject* +PerformanceObserver::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return PerformanceObserverBinding::Wrap(aCx, this, aGivenProto); +} + +void +PerformanceObserver::Notify() +{ + if (mQueuedEntries.IsEmpty()) { + return; + } + nsRefPtr list = + new PerformanceObserverEntryList(this, mQueuedEntries); + + ErrorResult rv; + mCallback->Call(this, *list, *this, rv); + NS_WARN_IF(rv.Failed()); + mQueuedEntries.Clear(); +} + +void +PerformanceObserver::QueueEntry(PerformanceEntry* aEntry) +{ + MOZ_ASSERT(aEntry); + + nsAutoString entryType; + aEntry->GetEntryType(entryType); + if (!mEntryTypes.Contains(entryType)) { + return; + } + + mQueuedEntries.AppendElement(aEntry); +} + +static nsString sValidTypeNames[7] = { + NS_LITERAL_STRING("composite"), + NS_LITERAL_STRING("mark"), + NS_LITERAL_STRING("measure"), + NS_LITERAL_STRING("navigation"), + NS_LITERAL_STRING("render"), + NS_LITERAL_STRING("resource"), + NS_LITERAL_STRING("server") +}; + +void +PerformanceObserver::Observe(const PerformanceObserverInit& aOptions, + ErrorResult& aRv) +{ + if (aOptions.mEntryTypes.IsEmpty()) { + aRv.Throw(NS_ERROR_DOM_TYPE_ERR); + return; + } + + nsTArray validEntryTypes; + + for (const nsString& validTypeName : sValidTypeNames) { + if (aOptions.mEntryTypes.Contains(validTypeName) && + !validEntryTypes.Contains(validTypeName)) { + validEntryTypes.AppendElement(validTypeName); + } + } + + if (validEntryTypes.IsEmpty()) { + aRv.Throw(NS_ERROR_DOM_TYPE_ERR); + return; + } + + mEntryTypes = validEntryTypes; + + mPerformance->AddObserver(this); + mConnected = true; +} + +void +PerformanceObserver::Disconnect() +{ + if (mConnected) { + MOZ_ASSERT(mPerformance); + mPerformance->RemoveObserver(this); + mConnected = false; + } +} diff --git a/dom/base/PerformanceObserver.h b/dom/base/PerformanceObserver.h new file mode 100644 index 0000000000..ef1a74cf3e --- /dev/null +++ b/dom/base/PerformanceObserver.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_dom_PerformanceObserver_h__ +#define mozilla_dom_PerformanceObserver_h__ + +#include "nsCOMPtr.h" +#include "nsISupports.h" +#include "mozilla/nsRefPtr.h" +#include "nsString.h" +#include "nsTArray.h" +#include "nsWrapperCache.h" + +class nsPIDOMWindow; +class PerformanceBase; + +namespace mozilla { + +class ErrorResult; + +namespace dom { + +class GlobalObject; +class PerformanceEntry; +class PerformanceObserverCallback; +struct PerformanceObserverInit; +namespace workers { +class WorkerPrivate; +} // namespace workers + +class PerformanceObserver final : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PerformanceObserver) + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, + PerformanceObserverCallback& aCb, + ErrorResult& aRv); + + PerformanceObserver(nsPIDOMWindow* aOwner, + PerformanceObserverCallback& aCb); + + PerformanceObserver(workers::WorkerPrivate* aWorkerPrivate, + PerformanceObserverCallback& aCb); + + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + nsISupports* GetParentObject() const { return mOwner; } + + void Observe(const PerformanceObserverInit& aOptions, + mozilla::ErrorResult& aRv); + + void Disconnect(); + + void Notify(); + void QueueEntry(PerformanceEntry* aEntry); + +private: + ~PerformanceObserver(); + + nsCOMPtr mOwner; + nsRefPtr mCallback; + nsRefPtr mPerformance; + nsTArray mEntryTypes; + bool mConnected; + nsTArray> mQueuedEntries; +}; + +} // namespace dom +} // namespace mozilla + +#endif diff --git a/dom/base/PerformanceObserverEntryList.cpp b/dom/base/PerformanceObserverEntryList.cpp new file mode 100644 index 0000000000..19166714cb --- /dev/null +++ b/dom/base/PerformanceObserverEntryList.cpp @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "PerformanceObserverEntryList.h" + +#include "mozilla/dom/PerformanceObserverEntryListBinding.h" +#include "nsPerformance.h" +#include "nsString.h" +#include "PerformanceResourceTiming.h" + +using namespace mozilla; +using namespace mozilla::dom; + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceObserverEntryList, + mOwner, + mEntries) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(PerformanceObserverEntryList) +NS_IMPL_CYCLE_COLLECTING_RELEASE(PerformanceObserverEntryList) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceObserverEntryList) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +PerformanceObserverEntryList::~PerformanceObserverEntryList() +{ +} + +JSObject* +PerformanceObserverEntryList::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return PerformanceObserverEntryListBinding::Wrap(aCx, this, aGivenProto); +} + +void +PerformanceObserverEntryList::GetEntries( + const PerformanceEntryFilterOptions& aFilter, + nsTArray>& aRetval) +{ + aRetval.Clear(); + for (const nsRefPtr& entry : mEntries) { + if (aFilter.mInitiatorType.WasPassed()) { + const PerformanceResourceTiming* resourceEntry = + entry->ToResourceTiming(); + if (!resourceEntry) { + continue; + } + nsAutoString initiatorType; + resourceEntry->GetInitiatorType(initiatorType); + if (!initiatorType.Equals(aFilter.mInitiatorType.Value())) { + continue; + } + } + if (aFilter.mName.WasPassed() && + !entry->GetName().Equals(aFilter.mName.Value())) { + continue; + } + if (aFilter.mEntryType.WasPassed() && + !entry->GetEntryType().Equals(aFilter.mEntryType.Value())) { + continue; + } + + aRetval.AppendElement(entry); + } +} + +void +PerformanceObserverEntryList::GetEntriesByType( + const nsAString& aEntryType, + nsTArray>& aRetval) +{ + aRetval.Clear(); + for (const nsRefPtr& entry : mEntries) { + if (entry->GetEntryType().Equals(aEntryType)) { + aRetval.AppendElement(entry); + } + } +} + +void +PerformanceObserverEntryList::GetEntriesByName( + const nsAString& aName, + const Optional& aEntryType, + nsTArray>& aRetval) +{ + aRetval.Clear(); + for (const nsRefPtr& entry : mEntries) { + if (entry->GetName().Equals(aName)) { + aRetval.AppendElement(entry); + } + } +} diff --git a/dom/base/PerformanceObserverEntryList.h b/dom/base/PerformanceObserverEntryList.h new file mode 100644 index 0000000000..9606fcc762 --- /dev/null +++ b/dom/base/PerformanceObserverEntryList.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_dom_PerformanceObserverEntryList_h__ +#define mozilla_dom_PerformanceObserverEntryList_h__ + +#include "nsCOMPtr.h" +#include "nsISupports.h" +#include "nsTArray.h" +#include "nsWrapperCache.h" +#include "mozilla/dom/PerformanceEntryBinding.h" + +namespace mozilla { +namespace dom { + +struct PerformanceEntryFilterOptions; +class PerformanceEntry; +template class Optional; + +class PerformanceObserverEntryList final : public nsISupports, + public nsWrapperCache +{ + ~PerformanceObserverEntryList(); + +public: + PerformanceObserverEntryList(nsISupports* aOwner, + nsTArray>& + aEntries) + : mOwner(aOwner) + , mEntries(aEntries) + { + } + + nsISupports* GetParentObject() const + { + return mOwner; + } + + virtual JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PerformanceObserverEntryList) + + void GetEntries(const PerformanceEntryFilterOptions& aFilter, + nsTArray>& aRetval); + void GetEntriesByType(const nsAString& aEntryType, + nsTArray>& aRetval); + void GetEntriesByName(const nsAString& aName, + const Optional& aEntryType, + nsTArray>& aRetval); + +private: + nsCOMPtr mOwner; + nsTArray> mEntries; +}; + +} // namespace dom +} // namespace mozilla + + +#endif diff --git a/dom/base/PerformanceResourceTiming.h b/dom/base/PerformanceResourceTiming.h index b6d9c26f65..659429f496 100644 --- a/dom/base/PerformanceResourceTiming.h +++ b/dom/base/PerformanceResourceTiming.h @@ -123,6 +123,11 @@ public: return 0; } + virtual const PerformanceResourceTiming* ToResourceTiming() const override + { + return this; + } + protected: virtual ~PerformanceResourceTiming(); diff --git a/dom/base/moz.build b/dom/base/moz.build index 904b3d013b..18ccc2c90a 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -189,6 +189,8 @@ EXPORTS.mozilla.dom += [ 'PerformanceEntry.h', 'PerformanceMark.h', 'PerformanceMeasure.h', + 'PerformanceObserver.h', + 'PerformanceObserverEntryList.h', 'PerformanceResourceTiming.h', 'ProcessGlobal.h', 'ResponsiveImageSelector.h', @@ -328,6 +330,8 @@ UNIFIED_SOURCES += [ 'PerformanceEntry.cpp', 'PerformanceMark.cpp', 'PerformanceMeasure.cpp', + 'PerformanceObserver.cpp', + 'PerformanceObserverEntryList.cpp', 'PerformanceResourceTiming.cpp', 'PostMessageEvent.cpp', 'ProcessGlobal.cpp', diff --git a/dom/base/nsAttrValue.cpp b/dom/base/nsAttrValue.cpp index 81b7a52f65..c938976f2e 100644 --- a/dom/base/nsAttrValue.cpp +++ b/dom/base/nsAttrValue.cpp @@ -29,6 +29,11 @@ #include "nsIDocument.h" #include +#ifdef LoadImage +// Undefine LoadImage to prevent naming conflict with Windows. +#undef LoadImage +#endif + using namespace mozilla; #define MISC_STR_PTR(_cont) \ diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 5623ebfbf0..9399abbdcb 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -273,6 +273,42 @@ nsFrameLoader::LoadURI(nsIURI* aURI) return rv; } +NS_IMETHODIMP +nsFrameLoader::SwitchProcessAndLoadURI(nsIURI* aURI) +{ + nsCOMPtr URIToLoad = aURI; + nsRefPtr tp = nullptr; + + MutableTabContext context; + nsCOMPtr ownApp = GetOwnApp(); + nsCOMPtr containingApp = GetContainingApp(); + + bool tabContextUpdated = true; + if (ownApp) { + tabContextUpdated = context.SetTabContextForAppFrame(ownApp, containingApp); + } else if (OwnerIsBrowserFrame()) { + // The |else| above is unnecessary; OwnerIsBrowserFrame() implies !ownApp. + tabContextUpdated = context.SetTabContextForBrowserFrame(containingApp); + } else { + tabContextUpdated = context.SetTabContextForNormalFrame(); + } + NS_ENSURE_STATE(tabContextUpdated); + + nsCOMPtr ownerElement = mOwnerContent; + tp = ContentParent::CreateBrowserOrApp(context, ownerElement, nullptr); + if (!tp) { + return NS_ERROR_FAILURE; + } + mRemoteBrowserShown = false; + + nsresult rv = SwapRemoteBrowser(tp); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + LoadURI(URIToLoad); + return NS_OK; +} + NS_IMETHODIMP nsFrameLoader::SetIsPrerendered() { @@ -2573,6 +2609,57 @@ nsFrameLoader::SetRemoteBrowser(nsITabParent* aTabParent) ShowRemoteFrame(ScreenIntSize(0, 0)); } +nsresult +nsFrameLoader::SwapRemoteBrowser(nsITabParent* aTabParent) +{ + nsRefPtr newParent = TabParent::GetFrom(aTabParent); + if (!newParent || !mRemoteBrowser) { + return NS_ERROR_DOM_INVALID_STATE_ERR; + } + if (!IsRemoteFrame()) { + NS_WARNING("Switching from in-process to out-of-process is not supported."); + return NS_ERROR_NOT_IMPLEMENTED; + } + if (!OwnerIsBrowserOrAppFrame()) { + NS_WARNING("Switching process for non-mozbrowser/app frame is not supported."); + return NS_ERROR_NOT_IMPLEMENTED; + } + if (newParent == mRemoteBrowser) { + return NS_OK; + } + nsCOMPtr os = services::GetObserverService(); + if (os) { + os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), + "frameloader-message-manager-will-change", nullptr); + } + + mRemoteBrowser->CacheFrameLoader(nullptr); + mRemoteBrowser->SetOwnerElement(nullptr); + mRemoteBrowser->Detach(); + mRemoteBrowser->Destroy(); + + if (mMessageManager) { + mMessageManager->Disconnect(); + mMessageManager = nullptr; + } + + mRemoteBrowser = newParent; + mRemoteBrowser->Attach(this); + mChildID = mRemoteBrowser->Manager()->ChildID(); + ReallyLoadFrameScripts(); + InitializeBrowserAPI(); + + if (os) { + os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), + "frameloader-message-manager-changed", nullptr); + } + if (!mRemoteBrowserShown) { + ShowRemoteFrame(ScreenIntSize(0, 0)); + } + + return NS_OK; +} + void nsFrameLoader::SetDetachedSubdocFrame(nsIFrame* aDetachedFrame, nsIDocument* aContainerDoc) diff --git a/dom/base/nsFrameLoader.h b/dom/base/nsFrameLoader.h index a46b3a78c5..9d73a5029b 100644 --- a/dom/base/nsFrameLoader.h +++ b/dom/base/nsFrameLoader.h @@ -180,6 +180,8 @@ public: */ void SetRemoteBrowser(nsITabParent* aTabParent); + nsresult SwapRemoteBrowser(nsITabParent* aTabParent); + /** * Stashes a detached nsIFrame on the frame loader. We do this when we're * destroying the nsSubDocumentFrame. If the nsSubdocumentFrame is diff --git a/dom/base/nsIFrameLoader.idl b/dom/base/nsIFrameLoader.idl index 0d57130902..f993b8f07a 100644 --- a/dom/base/nsIFrameLoader.idl +++ b/dom/base/nsIFrameLoader.idl @@ -16,7 +16,7 @@ interface nsIDOMElement; interface nsITabParent; interface nsILoadContext; -[scriptable, builtinclass, uuid(d24f9330-ae4e-11e4-ab27-0800200c9a66)] +[scriptable, builtinclass, uuid(c6e00815-b7a1-4544-b309-a85b86cb1747)] interface nsIFrameLoader : nsISupports { /** @@ -49,6 +49,14 @@ interface nsIFrameLoader : nsISupports */ void loadURI(in nsIURI aURI); + /** + * Loads the specified URI in this frame but using a different process. + * Behaves identically to loadURI, except that this method only works + * with remote frame. + * Throws an exception with non-remote frames. + */ + void switchProcessAndLoadURI(in nsIURI aURI); + /** * Puts the frameloader in prerendering mode. */ diff --git a/dom/base/nsPerformance.cpp b/dom/base/nsPerformance.cpp index 06e39cc08f..2259517fd2 100644 --- a/dom/base/nsPerformance.cpp +++ b/dom/base/nsPerformance.cpp @@ -18,12 +18,14 @@ #include "PerformanceEntry.h" #include "PerformanceMark.h" #include "PerformanceMeasure.h" +#include "PerformanceObserver.h" #include "PerformanceResourceTiming.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/PerformanceBinding.h" #include "mozilla/dom/PerformanceEntryEvent.h" #include "mozilla/dom/PerformanceTimingBinding.h" #include "mozilla/dom/PerformanceNavigationBinding.h" +#include "mozilla/dom/PerformanceObserverBinding.h" #include "mozilla/Preferences.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/TimeStamp.h" @@ -348,7 +350,7 @@ nsPerformanceTiming::ResponseStart() DOMHighResTimeStamp nsPerformanceTiming::ResponseEndHighRes() { - if (!IsInitialized()) { + if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) { return mZeroTime; } if (mResponseEnd.IsNull() || @@ -692,15 +694,17 @@ public: class PrefEnabledRunnable final : public WorkerMainThreadRunnable { public: - explicit PrefEnabledRunnable(WorkerPrivate* aWorkerPrivate) + PrefEnabledRunnable(WorkerPrivate* aWorkerPrivate, + const nsCString& aPrefName) : WorkerMainThreadRunnable(aWorkerPrivate) , mEnabled(false) + , mPrefName(aPrefName) { } bool MainThreadRun() override { MOZ_ASSERT(NS_IsMainThread()); - mEnabled = Preferences::GetBool("dom.enable_user_timing", false); + mEnabled = Preferences::GetBool(mPrefName.get(), false); return true; } @@ -711,9 +715,10 @@ public: private: bool mEnabled; + nsCString mPrefName; }; -} // anonymous namespace +} // namespace /* static */ bool nsPerformance::IsEnabled(JSContext* aCx, JSObject* aGlobal) @@ -727,7 +732,27 @@ nsPerformance::IsEnabled(JSContext* aCx, JSObject* aGlobal) workerPrivate->AssertIsOnWorkerThread(); nsRefPtr runnable = - new PrefEnabledRunnable(workerPrivate); + new PrefEnabledRunnable(workerPrivate, + NS_LITERAL_CSTRING("dom.enable_user_timing")); + runnable->Dispatch(workerPrivate->GetJSContext()); + + return runnable->IsEnabled(); +} + +/* static */ bool +nsPerformance::IsObserverEnabled(JSContext* aCx, JSObject* aGlobal) +{ + if (NS_IsMainThread()) { + return Preferences::GetBool("dom.enable_performance_observer", false); + } + + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + workerPrivate->AssertIsOnWorkerThread(); + + nsRefPtr runnable = + new PrefEnabledRunnable(workerPrivate, + NS_LITERAL_CSTRING("dom.enable_performance_observer")); runnable->Dispatch(workerPrivate->GetJSContext()); return runnable->IsEnabled(); @@ -787,6 +812,7 @@ NS_IMPL_RELEASE_INHERITED(PerformanceBase, DOMEventTargetHelper) PerformanceBase::PerformanceBase() : mResourceTimingBufferSize(kDefaultResourceTimingBufferSize) + , mPendingNotificationObserversTask(false) { MOZ_ASSERT(!NS_IsMainThread()); } @@ -794,6 +820,7 @@ PerformanceBase::PerformanceBase() PerformanceBase::PerformanceBase(nsPIDOMWindow* aWindow) : DOMEventTargetHelper(aWindow) , mResourceTimingBufferSize(kDefaultResourceTimingBufferSize) + , mPendingNotificationObserversTask(false) { MOZ_ASSERT(NS_IsMainThread()); } @@ -1024,6 +1051,8 @@ PerformanceBase::InsertUserEntry(PerformanceEntry* aEntry) { mUserEntries.InsertElementSorted(aEntry, PerformanceEntryComparator()); + + QueueEntry(aEntry); } void @@ -1047,4 +1076,89 @@ PerformanceBase::InsertResourceEntry(PerformanceEntry* aEntry) // call onresourcetimingbufferfull DispatchBufferFullEvent(); } + QueueEntry(aEntry); +} + +void +PerformanceBase::AddObserver(PerformanceObserver* aObserver) +{ + mObservers.AppendElementUnlessExists(aObserver); +} + +void +PerformanceBase::RemoveObserver(PerformanceObserver* aObserver) +{ + mObservers.RemoveElement(aObserver); +} + +void +PerformanceBase::NotifyObservers() +{ + mPendingNotificationObserversTask = false; + NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers, + PerformanceObserver, + Notify, ()); +} + +void +PerformanceBase::CancelNotificationObservers() +{ + mPendingNotificationObserversTask = false; +} + +class NotifyObserversTask final : public nsCancelableRunnable +{ +public: + explicit NotifyObserversTask(PerformanceBase* aPerformance) + : mPerformance(aPerformance) + { + MOZ_ASSERT(mPerformance); + } + + NS_IMETHOD Run() override + { + MOZ_ASSERT(mPerformance); + mPerformance->NotifyObservers(); + return NS_OK; + } + + NS_IMETHOD Cancel() override + { + mPerformance->CancelNotificationObservers(); + mPerformance = nullptr; + return NS_OK; + } + +private: + ~NotifyObserversTask() + { + } + + nsRefPtr mPerformance; +}; + +void +PerformanceBase::RunNotificationObserversTask() +{ + mPendingNotificationObserversTask = true; + nsCOMPtr task = new NotifyObserversTask(this); + nsresult rv = NS_DispatchToCurrentThread(task); + if (NS_WARN_IF(NS_FAILED(rv))) { + mPendingNotificationObserversTask = false; + } +} + +void +PerformanceBase::QueueEntry(PerformanceEntry* aEntry) +{ + if (mObservers.IsEmpty()) { + return; + } + NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers, + PerformanceObserver, + QueueEntry, (aEntry)); + + if (!mPendingNotificationObserversTask) { + RunNotificationObserversTask(); + } } diff --git a/dom/base/nsPerformance.h b/dom/base/nsPerformance.h index 0e3ee2fbcf..a6b4c0d2ed 100644 --- a/dom/base/nsPerformance.h +++ b/dom/base/nsPerformance.h @@ -26,6 +26,7 @@ namespace mozilla { class ErrorResult; namespace dom { class PerformanceEntry; + class PerformanceObserver; } // namespace dom } // namespace mozilla @@ -300,6 +301,7 @@ public: explicit PerformanceBase(nsPIDOMWindow* aWindow); typedef mozilla::dom::PerformanceEntry PerformanceEntry; + typedef mozilla::dom::PerformanceObserver PerformanceObserver; void GetEntries(nsTArray>& aRetval); void GetEntriesByType(const nsAString& aEntryType, @@ -321,6 +323,11 @@ public: void SetResourceTimingBufferSize(uint64_t aMaxSize); + void AddObserver(PerformanceObserver* aObserver); + void RemoveObserver(PerformanceObserver* aObserver); + void NotifyObservers(); + void CancelNotificationObservers(); + protected: virtual ~PerformanceBase(); @@ -353,12 +360,18 @@ protected: void LogEntry(PerformanceEntry* aEntry, const nsACString& aOwner) const; void TimingNotification(PerformanceEntry* aEntry, const nsACString& aOwner, uint64_t epoch); + void RunNotificationObserversTask(); + void QueueEntry(PerformanceEntry* aEntry); + + nsTObserverArray mObservers; + private: nsTArray> mUserEntries; nsTArray> mResourceEntries; uint64_t mResourceTimingBufferSize; static const uint64_t kDefaultResourceTimingBufferSize = 150; + bool mPendingNotificationObserversTask; }; // Script "performance" object @@ -376,6 +389,8 @@ public: static bool IsEnabled(JSContext* aCx, JSObject* aGlobal); + static bool IsObserverEnabled(JSContext* aCx, JSObject* aGlobal); + nsDOMNavigationTiming* GetDOMTiming() const { return mDOMTiming; @@ -426,7 +441,7 @@ private: return this; } - void InsertUserEntry(PerformanceEntry* aEntry); + void InsertUserEntry(PerformanceEntry* aEntry) override; bool IsPerformanceTimingAttribute(const nsAString& aName) override; diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index ad374fe0a7..e1de788188 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -248,6 +248,8 @@ support-files = referrer_testserver.sjs script_postmessages_fileList.js iframe_postMessages.html + test_performance_observer.js + performance_observer.html [test_anonymousContent_api.html] [test_anonymousContent_append_after_reflow.html] @@ -797,8 +799,8 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' [test_bug1081686.html] skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s [test_window_define_nonconfigurable.html] -skip-if = true # bug 1107443 - code for newly-added test was disabled [test_root_iframe.html] +[test_performance_observer.html] [test_performance_user_timing.html] [test_bug1126851.html] skip-if = buildapp == 'mulet' || buildapp == 'b2g' @@ -814,3 +816,5 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' [test_postMessages.html] support-files = worker_postMessages.js [test_window_proto.html] +[test_frameLoader_switchProcess.html] +skip-if = e10s || os != 'linux' || buildapp != 'browser' diff --git a/dom/base/test/performance_observer.html b/dom/base/test/performance_observer.html new file mode 100644 index 0000000000..b8ced9296c --- /dev/null +++ b/dom/base/test/performance_observer.html @@ -0,0 +1,74 @@ + + + + + +Test for performance observer + + + + +
+ + diff --git a/dom/base/test/test_frameLoader_switchProcess.html b/dom/base/test/test_frameLoader_switchProcess.html new file mode 100644 index 0000000000..1b96d323d7 --- /dev/null +++ b/dom/base/test/test_frameLoader_switchProcess.html @@ -0,0 +1,84 @@ + + + + Test frameLoader SwitchProcessAndLoadURI + + + + + + + + diff --git a/dom/base/test/test_performance_observer.html b/dom/base/test/test_performance_observer.html new file mode 100644 index 0000000000..d368783155 --- /dev/null +++ b/dom/base/test/test_performance_observer.html @@ -0,0 +1,17 @@ + + + +Test for performance observer + + +
+ diff --git a/dom/base/test/test_performance_observer.js b/dom/base/test/test_performance_observer.js new file mode 100644 index 0000000000..9716570e29 --- /dev/null +++ b/dom/base/test/test_performance_observer.js @@ -0,0 +1,226 @@ +setup({ explicit_done: true }); + +test(t => { + assert_throws({name: "TypeError"}, function() { + new PerformanceObserver(); + }, "PerformanceObserver constructor should throw TypeError if no argument is specified."); + + assert_throws({name: "TypeError"}, function() { + new PerformanceObserver({}); + }, "PerformanceObserver constructor should throw TypeError if the argument is not a function."); +}, "Test that PerformanceObserver constructor throws exception"); + +test(t => { + var observer = new PerformanceObserver(() => { + }); + + assert_throws({name: "TypeError"}, function() { + observer.observe(); + }, "observe() should throw TypeError exception if no option specified."); + + assert_throws({name: "TypeError"}, function() { + observer.observe({ unsupportedAttribute: "unsupported" }); + }, "obsrve() should throw TypeError exception if the option has no 'entryTypes' attribute."); + + assert_throws({name: "TypeError"}, function() { + observer.observe({ entryTypes: [] }); + }, "obsrve() should throw TypeError exception if 'entryTypes' attribute is an empty sequence."); + + assert_throws({name: "TypeError"}, function() { + observer.observe({ entryTypes: null }); + }, "obsrve() should throw TypeError exception if 'entryTypes' attribute is null."); + + assert_throws({name: "TypeError"}, function() { + observer.observe({ entryTypes: ["invalid"]}); + }, "obsrve() should throw TypeError exception if 'entryTypes' attribute value is invalid."); +}, "Test that PerformanceObserver.observe throws exception"); + +function promiseObserve(test, options) { + return new Promise(resolve => { + performance.clearMarks(); + performance.clearMeasures(); + + var observer = new PerformanceObserver(list => resolve(list)); + observer.observe(options); + test.add_cleanup(() => observer.disconnect()); + }); +} + +promise_test(t => { + var promise = promiseObserve(t, {entryTypes: ['mark', 'measure']}); + + performance.mark("test-start"); + performance.mark("test-end"); + performance.measure("test-measure", "test-start", "test-end"); + + return promise.then(list => { + assert_equals(list.getEntries().length, 3, + "There should be three observed entries."); + + var markEntries = list.getEntries().filter(entry => { + return entry.entryType == "mark"; + }); + assert_array_equals(markEntries, performance.getEntriesByType("mark"), + "Observed 'mark' entries should equal to entries obtained by getEntriesByType."); + + var measureEntries = list.getEntries().filter(entry => { + return entry.entryType == "measure"; + }); + assert_array_equals(measureEntries, performance.getEntriesByType("measure"), + "Observed 'measure' entries should equal to entries obtained by getEntriesByType."); + }); +}, "Test for user-timing with PerformanceObserver"); + +promise_test(t => { + var promise = new Promise((resolve, reject) => { + performance.clearMarks(); + performance.clearMeasures(); + + var observer = new PerformanceObserver(list => reject(list)); + observer.observe({entryTypes: ['mark', 'measure']}); + observer.disconnect(); + t.step_timeout(resolve, 100); + }); + + performance.mark("test-start"); + performance.mark("test-end"); + performance.measure("test-measure", "test-start", "test-end"); + + return promise.then(() => { + assert_equals(performance.getEntriesByType("mark").length, 2); + assert_equals(performance.getEntriesByType("measure").length, 1); + }, list => { + assert_unreached("Observer callback should never be called."); + }); + +}, "Nothing should be notified after disconnecting observer"); + +promise_test(t => { + var promise = promiseObserve(t, {entryTypes: ['mark']}); + + performance.mark("test"); + + return promise.then(list => { + assert_array_equals(list.getEntries({"entryType": "mark"}), + performance.getEntriesByType("mark"), + "getEntries with entryType filter should return correct results."); + + assert_array_equals(list.getEntries({"name": "test"}), + performance.getEntriesByName("test"), + "getEntries with name filter should return correct results."); + + assert_array_equals(list.getEntries({"name": "test", + "entryType": "mark"}), + performance.getEntriesByName("test"), + "getEntries with name and entryType filter should return correct results."); + + assert_array_equals(list.getEntries({"name": "invalid"}), + [], + "getEntries with non-existent name filter should return an empty array."); + + assert_array_equals(list.getEntries({"name": "test", + "entryType": "measure"}), + [], + "getEntries with name filter and non-existent entryType should return an empty array."); + + assert_array_equals(list.getEntries({"name": "invalid", + "entryType": "mark"}), + [], + "getEntries with non-existent name and entryType filter should return an empty array."); + + assert_array_equals(list.getEntries({initiatorType: "xmlhttprequest"}), + [], + "getEntries with initiatorType filter should return an empty array."); + }); +}, "Test for PerformanceObserverEntryList.getEntries"); + +promise_test(t => { + var promise = promiseObserve(t, {entryTypes: ['mark', 'measure']}); + + performance.mark("test"); + performance.measure("test-measure", "test", "test"); + + return promise.then(list => { + assert_array_equals(list.getEntriesByType("mark"), + performance.getEntriesByType("mark")); + assert_array_equals(list.getEntriesByType("measure"), + performance.getEntriesByType("measure")); + }); +}, "Test for PerformanceObserverEntryList.getEntriesByType"); + +promise_test(t => { + var promise = promiseObserve(t, {entryTypes: ['mark', 'measure']}); + + performance.mark("test"); + performance.measure("test-measure", "test", "test"); + + return promise.then(list => { + assert_array_equals(list.getEntriesByName("test"), + performance.getEntriesByName("test")); + assert_array_equals(list.getEntriesByName("test-measure"), + performance.getEntriesByName("test-measure")); + }); +}, "Test for PerformanceObserverEntryList.getEntriesByName"); + +promise_test(t => { + var promise = new Promise(resolve => { + performance.clearMarks(); + performance.clearMeasures(); + + var observer = new PerformanceObserver(list => resolve(list)); + observer.observe({entryTypes: ['mark', 'measure']}); + observer.observe({entryTypes: ['mark', 'measure']}); + t.add_cleanup(() => observer.disconnect()); + }); + + performance.mark("test-start"); + performance.mark("test-end"); + performance.measure("test-measure", "test-start", "test-end"); + + return promise.then(list => { + assert_equals(list.getEntries().length, 3, + "Observed user timing entries should have only three entries."); + }); +}, "Test that invoking observe method twice affects nothing"); + +promise_test(t => { + var promise = new Promise(resolve => { + performance.clearMarks(); + performance.clearMeasures(); + + var observer = new PerformanceObserver(list => resolve(list)); + observer.observe({entryTypes: ['mark', 'measure']}); + observer.observe({entryTypes: ['mark']}); + t.add_cleanup(() => observer.disconnect()); + }); + + performance.mark("test-start"); + performance.mark("test-end"); + performance.measure("test-measure", "test-start", "test-end"); + + return promise.then(list => { + assert_equals(list.getEntries().length, 2, + "Observed user timing entries should have only two entries."); + }); +}, "Test that observing filter is replaced by a new filter"); + +promise_test(t => { + var promise = new Promise(resolve => { + performance.clearMarks(); + performance.clearMeasures(); + + var observer = new PerformanceObserver(list => resolve(list)); + observer.observe({entryTypes: ['mark']}); + observer.observe({entryTypes: ['measure']}); + t.add_cleanup(() => observer.disconnect()); + }); + + performance.mark("test-start"); + performance.mark("test-end"); + performance.measure("test-measure", "test-start", "test-end"); + + return promise.then(list => { + assert_equals(list.getEntries().length, 1, + "Observed user timing entries should have only 1 entries."); + }); +}, "Test that observing filter is replaced by a new filter"); diff --git a/dom/browser-element/BrowserElementParent.js b/dom/browser-element/BrowserElementParent.js index 1afeda7823..fae40c5edf 100644 --- a/dom/browser-element/BrowserElementParent.js +++ b/dom/browser-element/BrowserElementParent.js @@ -89,6 +89,8 @@ function BrowserElementParent() { Services.obs.addObserver(this, 'oop-frameloader-crashed', /* ownsWeak = */ true); Services.obs.addObserver(this, 'copypaste-docommand', /* ownsWeak = */ true); Services.obs.addObserver(this, 'ask-children-to-execute-copypaste-command', /* ownsWeak = */ true); + Services.obs.addObserver(this, 'frameloader-message-manager-will-change', /* ownsWeak = */ true); + Services.obs.addObserver(this, 'frameloader-message-manager-changed', /* ownsWeak = */ true); } BrowserElementParent.prototype = { @@ -169,10 +171,17 @@ BrowserElementParent.prototype = { _setupMessageListener: function() { this._mm = this._frameLoader.messageManager; - let self = this; - let isWidget = this._frameLoader - .QueryInterface(Ci.nsIFrameLoader) - .ownerIsWidget; + this._isWidget = this._frameLoader + .QueryInterface(Ci.nsIFrameLoader) + .ownerIsWidget; + this._mm.addMessageListener('browser-element-api:call', this); + this._mm.loadFrameScript("chrome://global/content/extensions.js", true); + }, + + receiveMessage: function(aMsg) { + if (!this._isAlive()) { + return; + } // Messages we receive are handed to functions which take a (data) argument, // where |data| is the message manager's data object. @@ -228,18 +237,15 @@ BrowserElementParent.prototype = { "opentab": this._fireEventFromMsg }; - this._mm.addMessageListener('browser-element-api:call', function(aMsg) { - if (!self._isAlive()) { - return; - } + if (aMsg.data.msg_name in mmCalls) { + return mmCalls[aMsg.data.msg_name].apply(this, arguments); + } else if (!this._isWidget && aMsg.data.msg_name in mmSecuritySensitiveCalls) { + return mmSecuritySensitiveCalls[aMsg.data.msg_name].apply(this, arguments); + } + }, - if (aMsg.data.msg_name in mmCalls) { - return mmCalls[aMsg.data.msg_name].apply(self, arguments); - } else if (!isWidget && aMsg.data.msg_name in mmSecuritySensitiveCalls) { - return mmSecuritySensitiveCalls[aMsg.data.msg_name] - .apply(self, arguments); - } - }); + _removeMessageListener: function() { + this._mm.removeMessageListener('browser-element-api:call', this); }, /** @@ -1082,6 +1088,16 @@ BrowserElementParent.prototype = { this._sendAsyncMsg('copypaste-do-command', { command: data }); } break; + case 'frameloader-message-manager-will-change': + if (this._isAlive() && subject == this._frameLoader) { + this._removeMessageListener(); + } + break; + case 'frameloader-message-manager-changed': + if (this._isAlive() && subject == this._frameLoader) { + this._setupMessageListener(); + } + break; default: debug('Unknown topic: ' + topic); break; diff --git a/dom/html/nsBrowserElement.cpp b/dom/html/nsBrowserElement.cpp index 8c4335c953..234f35d5e2 100644 --- a/dom/html/nsBrowserElement.cpp +++ b/dom/html/nsBrowserElement.cpp @@ -66,10 +66,13 @@ nsBrowserElement::InitBrowserElementAPI() return; } - mBrowserElementAPI = do_CreateInstance("@mozilla.org/dom/browser-element-api;1"); - if (mBrowserElementAPI) { - mBrowserElementAPI->SetFrameLoader(frameLoader); + if (!mBrowserElementAPI) { + mBrowserElementAPI = do_CreateInstance("@mozilla.org/dom/browser-element-api;1"); + if (NS_WARN_IF(!mBrowserElementAPI)) { + return; + } } + mBrowserElementAPI->SetFrameLoader(frameLoader); } void diff --git a/dom/interfaces/base/nsITabParent.idl b/dom/interfaces/base/nsITabParent.idl index c09fba5d55..ba81a3cee1 100644 --- a/dom/interfaces/base/nsITabParent.idl +++ b/dom/interfaces/base/nsITabParent.idl @@ -5,7 +5,7 @@ #include "domstubs.idl" -[scriptable, uuid(CE6F563B-BD77-4EF2-9D7C-A94C587353E4)] +[scriptable, uuid(3dd203e4-66ec-40fd-acde-43f0b35c98e9)] interface nsITabParent : nsISupports { void injectTouchEvent(in AString aType, @@ -23,7 +23,10 @@ interface nsITabParent : nsISupports readonly attribute boolean useAsyncPanZoom; - void setIsDocShellActive(in bool aIsActive); + /** + * Manages the docshell active state of the remote browser. + */ + attribute boolean docShellIsActive; readonly attribute uint64_t tabId; diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 71133d098a..a0c6814244 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -702,9 +702,9 @@ child: SetUpdateHitRegion(bool aEnabled); /** - * Tell the child to update its docShell's active state. + * Update the child side docShell active (resource use) state. */ - SetIsDocShellActive(bool aIsActive); + SetDocShellIsActive(bool aIsActive); /** * Navigate by key (Tab/Shift+Tab/F6/Shift+f6). diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 53af4fd362..d79491e183 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -2497,7 +2497,7 @@ TabChild::RecvSetUpdateHitRegion(const bool& aEnabled) } bool -TabChild::RecvSetIsDocShellActive(const bool& aIsActive) +TabChild::RecvSetDocShellIsActive(const bool& aIsActive) { nsCOMPtr docShell = do_GetInterface(WebNavigation()); if (docShell) { diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index 4299994f61..291f03193e 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -49,7 +49,6 @@ class RenderFrameChild; namespace layers { class APZEventState; class ImageCompositeNotification; -struct SetTargetAPZCCallback; } // namespace layers namespace widget { @@ -230,7 +229,6 @@ class TabChild final : public TabChildBase, typedef mozilla::dom::ClonedMessageData ClonedMessageData; typedef mozilla::layout::RenderFrameChild RenderFrameChild; typedef mozilla::layers::APZEventState APZEventState; - typedef mozilla::layers::SetTargetAPZCCallback SetTargetAPZCCallback; typedef mozilla::layers::SetAllowedTouchBehaviorCallback SetAllowedTouchBehaviorCallback; public: @@ -503,7 +501,7 @@ protected: virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override; virtual bool RecvDestroy() override; virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) override; - virtual bool RecvSetIsDocShellActive(const bool& aIsActive) override; + virtual bool RecvSetDocShellIsActive(const bool& aIsActive) override; virtual bool RecvNavigateByKey(const bool& aForward, const bool& aForDocumentNavigation) override; virtual bool RecvRequestNotifyAfterRemotePaint() override; diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 61366065da..826a9310c7 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -273,8 +273,10 @@ TabParent::TabParent(nsIContentParent* aManager, , mDefaultScale(0) , mUpdatedDimensions(false) , mManager(aManager) + , mDocShellIsActive(false) , mMarkedDestroying(false) , mIsDestroyed(false) + , mIsDetached(true) , mAppPackageFileDescriptorSent(false) , mSendOfflineStatus(true) , mChromeFlags(aChromeFlags) @@ -477,6 +479,35 @@ TabParent::Destroy() mMarkedDestroying = true; } +void +TabParent::Detach() +{ + if (mIsDetached) { + return; + } + RemoveWindowListeners(); + if (RenderFrameParent* frame = GetRenderFrame()) { + RemoveTabParentFromTable(frame->GetLayersId()); + } + mIsDetached = true; +} + +void +TabParent::Attach(nsFrameLoader* aFrameLoader) +{ + MOZ_ASSERT(mIsDetached); + if (!mIsDetached) { + return; + } + Element* ownerElement = aFrameLoader->GetOwnerContent(); + SetOwnerElement(ownerElement); + if (RenderFrameParent* frame = GetRenderFrame()) { + AddTabParentToTable(frame->GetLayersId(), this); + frame->OwnerContentChanged(ownerElement); + } + mIsDetached = false; +} + bool TabParent::Recv__delete__() { @@ -2981,10 +3012,19 @@ TabParent::GetUseAsyncPanZoom(bool* useAsyncPanZoom) return NS_OK; } +// defined in nsITabParent NS_IMETHODIMP -TabParent::SetIsDocShellActive(bool isActive) +TabParent::SetDocShellIsActive(bool isActive) { - unused << SendSetIsDocShellActive(isActive); + mDocShellIsActive = isActive; + unused << SendSetDocShellIsActive(isActive); + return NS_OK; +} + +NS_IMETHODIMP +TabParent::GetDocShellIsActive(bool* aIsActive) +{ + *aIsActive = mDocShellIsActive; return NS_OK; } diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index b44d996dee..0f9984a9e3 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -122,6 +122,8 @@ public: nsIXULBrowserWindow* GetXULBrowserWindow(); void Destroy(); + void Detach(); + void Attach(nsFrameLoader* aFrameLoader); void RemoveWindowListeners(); void AddWindowListeners(); @@ -494,6 +496,9 @@ private: bool AsyncPanZoomEnabled() const; + // Cached value indicating the docshell active state of the remote browser. + bool mDocShellIsActive; + // Update state prior to routing an APZ-aware event to the child process. // |aOutTargetGuid| will contain the identifier // of the APZC instance that handled the event. aOutTargetGuid may be null. @@ -508,6 +513,8 @@ private: bool mMarkedDestroying; // When true, the TabParent is invalid and we should not send IPC messages anymore. bool mIsDestroyed; + // When true, the TabParent is detached from the frame loader. + bool mIsDetached; // Whether we have already sent a FileDescriptor for the app package. bool mAppPackageFileDescriptorSent; diff --git a/dom/webidl/Performance.webidl b/dom/webidl/Performance.webidl index ea1f0b21c6..64c4bdb7b4 100644 --- a/dom/webidl/Performance.webidl +++ b/dom/webidl/Performance.webidl @@ -71,3 +71,4 @@ partial interface Performance { [Func="nsPerformance::IsEnabled"] void clearMeasures(optional DOMString measureName); }; + diff --git a/dom/webidl/PerformanceObserver.webidl b/dom/webidl/PerformanceObserver.webidl new file mode 100644 index 0000000000..c98bc1d02b --- /dev/null +++ b/dom/webidl/PerformanceObserver.webidl @@ -0,0 +1,23 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + * + * The origin of this IDL file is + * https://w3c.github.io/performance-timeline/#the-performance-observer-interface + */ + +dictionary PerformanceObserverInit { + required sequence entryTypes; +}; + +callback PerformanceObserverCallback = void (PerformanceObserverEntryList entries, PerformanceObserver observer); + +[Func="nsPerformance::IsObserverEnabled", + Constructor(PerformanceObserverCallback callback), + Exposed=(Window,Worker)] +interface PerformanceObserver { + [Throws] + void observe(PerformanceObserverInit options); + void disconnect(); +}; diff --git a/dom/webidl/PerformanceObserverEntryList.webidl b/dom/webidl/PerformanceObserverEntryList.webidl new file mode 100644 index 0000000000..fa17568e83 --- /dev/null +++ b/dom/webidl/PerformanceObserverEntryList.webidl @@ -0,0 +1,24 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + * + * The origin of this IDL file is + * https://w3c.github.io/performance-timeline/#the-performanceobserverentrylist-interface + */ + +// XXX should be moved into Performance.webidl. +dictionary PerformanceEntryFilterOptions { + DOMString name; + DOMString entryType; + DOMString initiatorType; +}; + +[Func="nsPerformance::IsObserverEnabled", Exposed=(Window,Worker)] +interface PerformanceObserverEntryList { + PerformanceEntryList getEntries(optional PerformanceEntryFilterOptions filter); + PerformanceEntryList getEntriesByType(DOMString entryType); + PerformanceEntryList getEntriesByName(DOMString name, + optional DOMString entryType); +}; + diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index f2f0cc26a0..951e17f1d5 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -353,6 +353,8 @@ WEBIDL_FILES = [ 'PerformanceMark.webidl', 'PerformanceMeasure.webidl', 'PerformanceNavigation.webidl', + 'PerformanceObserver.webidl', + 'PerformanceObserverEntryList.webidl', 'PerformanceResourceTiming.webidl', 'PerformanceTiming.webidl', 'PeriodicWave.webidl', diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp index efc60f7cda..399a103a62 100644 --- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -200,6 +200,14 @@ ChannelFromScriptURL(nsIPrincipal* principal, NS_ENSURE_SUCCESS(rv, rv); + if (nsCOMPtr httpChannel = do_QueryInterface(channel)) { + rv = nsContentUtils::SetFetchReferrerURIWithPolicy(principal, parentDoc, + httpChannel); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + channel.forget(aChannel); return rv; } diff --git a/dom/workers/test/mochitest.ini b/dom/workers/test/mochitest.ini index 32404b49d0..a4e216b48c 100644 --- a/dom/workers/test/mochitest.ini +++ b/dom/workers/test/mochitest.ini @@ -106,7 +106,10 @@ support-files = bug1132924_worker.js empty.html worker_performance_user_timing.js + worker_performance_observer.js sharedworker_performance_user_timing.js + referrer.sjs + performance_observer.html [test_404.html] [test_atob.html] @@ -167,6 +170,7 @@ skip-if = buildapp == 'mulet' [test_onLine.html] skip-if = (toolkit == 'gonk' && debug) #debug-only failure [test_performance_user_timing.html] +[test_performance_observer.html] [test_promise.html] [test_promise_resolved_with_string.html] [test_recursion.html] @@ -216,3 +220,4 @@ skip-if = buildapp == 'b2g' || e10s [test_xhr_timeout.html] skip-if = (os == "win") || (os == "mac") || toolkit == 'android' || e10s #bug 798220 [test_xhrAbort.html] +[test_referrer.html] diff --git a/dom/workers/test/performance_observer.html b/dom/workers/test/performance_observer.html new file mode 100644 index 0000000000..613762f521 --- /dev/null +++ b/dom/workers/test/performance_observer.html @@ -0,0 +1,32 @@ + + + + + +Test for performance observer in worker + + +
+ + diff --git a/dom/workers/test/referrer.sjs b/dom/workers/test/referrer.sjs new file mode 100644 index 0000000000..0497bb7296 --- /dev/null +++ b/dom/workers/test/referrer.sjs @@ -0,0 +1,11 @@ +function handleRequest(request, response) +{ + if (request.queryString == "result") { + response.write(getState("referer")); + } else { + response.setHeader("Content-Type", "text/javascript", false); + response.write("onmessage = function() { postMessage(42); }"); + setState("referer", request.getHeader("referer")); + } +} + diff --git a/dom/workers/test/test_performance_observer.html b/dom/workers/test/test_performance_observer.html new file mode 100644 index 0000000000..7efc09c957 --- /dev/null +++ b/dom/workers/test/test_performance_observer.html @@ -0,0 +1,17 @@ + + + +Test for performance observer in worker + + +
+ diff --git a/dom/workers/test/test_referrer.html b/dom/workers/test/test_referrer.html new file mode 100644 index 0000000000..9f7dacc36e --- /dev/null +++ b/dom/workers/test/test_referrer.html @@ -0,0 +1,35 @@ + + + + + Test the referrer of workers + + + + +

+ +

+
+
+
+
diff --git a/dom/workers/test/worker_performance_observer.js b/dom/workers/test/worker_performance_observer.js
new file mode 100644
index 0000000000..5b150956ee
--- /dev/null
+++ b/dom/workers/test/worker_performance_observer.js
@@ -0,0 +1,4 @@
+importScripts(['/resources/testharness.js']);
+importScripts(['../../../dom/base/test/test_performance_observer.js']);
+
+done();
diff --git a/toolkit/content/widgets/remote-browser.xml b/toolkit/content/widgets/remote-browser.xml
index 92d5b3d140..edd7b34398 100644
--- a/toolkit/content/widgets/remote-browser.xml
+++ b/toolkit/content/widgets/remote-browser.xml
@@ -223,13 +223,14 @@