From 63487b682165168dcbc65d380c1b426fb89b870f Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 20 Sep 2013 15:07:51 -0700 Subject: [PATCH 01/24] Fix window.focus() in content processes (bug 902715, r=enndeakin). --- dom/base/nsGlobalWindow.cpp | 4 ++++ dom/ipc/PBrowser.ipdl | 6 ++++++ dom/ipc/TabParent.cpp | 22 ++++++++++++++++++++++ dom/ipc/TabParent.h | 1 + 4 files changed, 33 insertions(+) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 8e70a8fdaf14..326a8551340f 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -34,6 +34,7 @@ #include "nsIPermissionManager.h" #include "nsIScriptContext.h" #include "nsIScriptTimeoutHandler.h" +#include "mozilla/dom/TabChild.h" #ifdef XP_WIN // Thanks so much, Microsoft! :( @@ -5711,6 +5712,9 @@ nsGlobalWindow::Focus() return fm->SetFocus(frameElement, flags); } } + else if (TabChild *child = TabChild::GetFrom(this)) { + child->SendRequestFocus(canFocus); + } else if (canFocus) { // if there is no parent, this must be a toplevel window, so raise the // window if canFocus is true diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 9fde7924c99e..266b0543db57 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -163,6 +163,12 @@ parent: */ sync EndIMEComposition(bool cancel) returns (nsString composition); + /** + * Request that the parent process move focus to the browser's frame. If + * canRaise is true, the window can be raised if it is inactive. + */ + RequestFocus(bool canRaise); + sync GetInputContext() returns (int32_t IMEEnabled, int32_t IMEOpen, intptr_t NativeIMEContext); diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index a26fd31754c9..d59f241a2a1e 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -897,6 +897,28 @@ TabParent::RecvNotifyIMETextHint(const nsString& aText) return true; } +bool +TabParent::RecvRequestFocus(const bool& aCanFocus) +{ + nsCOMPtr fm = nsFocusManager::GetFocusManager(); + if (!fm) { + return true; + } + + nsCOMPtr content = do_QueryInterface(mFrameElement); + if (!content || !content->OwnerDoc()) { + return true; + } + + uint32_t flags = nsIFocusManager::FLAG_NOSCROLL; + if (aCanFocus) + flags |= nsIFocusManager::FLAG_RAISE; + + nsCOMPtr node = do_QueryInterface(mFrameElement); + fm->SetFocus(node, flags); + return true; +} + /** * Try to answer query event using cached text. * diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index bc8f8cd11d11..d8efbc679daa 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -149,6 +149,7 @@ public: const nsString& aActionHint, const int32_t& aCause, const int32_t& aFocusChange); + virtual bool RecvRequestFocus(const bool& aCanFocus); virtual bool RecvSetCursor(const uint32_t& aValue); virtual bool RecvSetBackgroundColor(const nscolor& aValue); virtual bool RecvSetStatus(const uint32_t& aType, const nsString& aStatus); From aa92fb5ac6de0444e600fd0ca44ddd1108661576 Mon Sep 17 00:00:00 2001 From: Nicholas Hurley Date: Fri, 20 Sep 2013 15:24:06 -0700 Subject: [PATCH 02/24] Bug 881804 (part 1) - Add support for predictive network actions. r=mcmanus f=honzab sr=biesi --- modules/libpref/src/init/all.js | 18 + netwerk/base/public/moz.build | 2 + netwerk/base/public/nsINetworkSeer.idl | 164 ++ .../base/public/nsINetworkSeerVerifier.idl | 31 + netwerk/base/public/nsISpeculativeConnect.idl | 28 + netwerk/base/src/Seer.cpp | 2146 +++++++++++++++++ netwerk/base/src/Seer.h | 206 ++ netwerk/base/src/moz.build | 1 + netwerk/build/nsNetCID.h | 11 + netwerk/build/nsNetModule.cpp | 4 + netwerk/protocol/http/nsHttpConnectionMgr.cpp | 87 +- netwerk/protocol/http/nsHttpConnectionMgr.h | 2 +- netwerk/test/unit/test_seer.js | 295 +++ netwerk/test/unit/xpcshell.ini | 1 + toolkit/components/telemetry/Histograms.json | 120 + 15 files changed, 3100 insertions(+), 16 deletions(-) create mode 100644 netwerk/base/public/nsINetworkSeer.idl create mode 100644 netwerk/base/public/nsINetworkSeerVerifier.idl create mode 100644 netwerk/base/src/Seer.cpp create mode 100644 netwerk/base/src/Seer.h create mode 100644 netwerk/test/unit/test_seer.js diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index a6abc7a7b786..79ae4ef07220 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1348,6 +1348,24 @@ pref("network.dir.format", 2); // enables the prefetch service (i.e., prefetching of URLs). pref("network.prefetch-next", true); +// enables the predictive service +pref("network.seer.enabled", true); +pref("network.seer.enable-hover-on-ssl", false); +pref("network.seer.page-degradation.day", 0); +pref("network.seer.page-degradation.week", 5); +pref("network.seer.page-degradation.month", 10); +pref("network.seer.page-degradation.year", 25); +pref("network.seer.page-degradation.max", 50); +pref("network.seer.subresource-degradation.day", 1); +pref("network.seer.subresource-degradation.week", 10); +pref("network.seer.subresource-degradation.month", 25); +pref("network.seer.subresource-degradation.year", 50); +pref("network.seer.subresource-degradation.max", 100); +pref("network.seer.preconnect-min-confidence", 90); +pref("network.seer.preresolve-min-confidence", 60); +pref("network.seer.redirect-likely-confidence", 75); +pref("network.seer.max-queue-size", 50); + // The following prefs pertain to the negotiate-auth extension (see bug 17578), // which provides transparent Kerberos or NTLM authentication using the SPNEGO diff --git a/netwerk/base/public/moz.build b/netwerk/base/public/moz.build index dc7467efc96a..84f5b7127dbd 100644 --- a/netwerk/base/public/moz.build +++ b/netwerk/base/public/moz.build @@ -57,6 +57,8 @@ XPIDL_SOURCES += [ 'nsINetUtil.idl', 'nsINetworkLinkService.idl', 'nsINetworkProperties.idl', + 'nsINetworkSeer.idl', + 'nsINetworkSeerVerifier.idl', 'nsIParentChannel.idl', 'nsIParentRedirectingChannel.idl', 'nsIPermission.idl', diff --git a/netwerk/base/public/nsINetworkSeer.idl b/netwerk/base/public/nsINetworkSeer.idl new file mode 100644 index 000000000000..5f7f09605398 --- /dev/null +++ b/netwerk/base/public/nsINetworkSeer.idl @@ -0,0 +1,164 @@ +/* vim: set ts=2 sts=2 et sw=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/. */ + +#include "nsISupports.idl" + +interface nsIURI; +interface nsILoadContext; +interface nsINetworkSeerVerifier; + +typedef unsigned long SeerPredictReason; +typedef unsigned long SeerLearnReason; + +/** + * nsINetworkSeer - learn about pages users visit, and allow us to take + * predictive actions upon future visits. + * NOTE: nsINetworkSeer should only be used on the main thread + */ +[scriptable, uuid(884a39a0-a3ed-4855-826a-fabb73ae878d)] +interface nsINetworkSeer : nsISupports +{ + /** + * Prediction reasons + * + * PREDICT_LINK - we are being asked to take predictive action because + * the user is hovering over a link. + * + * PREDICT_LOAD - we are being asked to take predictive action because + * the user has initiated a pageload. + * + * PREDICT_STARTUP - we are being asked to take predictive action + * because the browser is starting up. + */ + const SeerPredictReason PREDICT_LINK = 0; + const SeerPredictReason PREDICT_LOAD = 1; + const SeerPredictReason PREDICT_STARTUP = 2; + + /** + * Start taking predictive actions + * + * Calling this will cause the seer to (possibly) start + * taking actions such as DNS prefetch and/or TCP preconnect based on + * (1) the host name that we are given, and (2) the reason we are being + * asked to take actions. + * + * @param targetURI - The URI we are being asked to take actions based on. + * @param sourceURI - The URI that is currently loaded. This is so we can + * avoid doing predictive actions for link hover on an HTTPS page (for + * example). + * @param reason - The reason we are being asked to take actions. Can be + * any of the PREDICT_* values above. + * In the case of PREDICT_LINK, targetURI should be the URI of the link + * that is being hovered over, and sourceURI should be the URI of the page + * on which the link appears. + * In the case of PREDICT_LOAD, targetURI should be the URI of the page that + * is being loaded and sourceURI should be null. + * In the case of PREDICT_STARTUP, both targetURI and sourceURI should be + * null. + * @param loadContext - The nsILoadContext of the page load we are predicting + * about. + * @param verifier - An nsINetworkSeerVerifier used in testing to ensure we're + * predicting the way we expect to. Not necessary (or desired) for normal + * operation. + */ + void predict(in nsIURI targetURI, + in nsIURI sourceURI, + in SeerPredictReason reason, + in nsILoadContext loadContext, + in nsINetworkSeerVerifier verifier); + + + /* + * Reasons we are learning something + * + * LEARN_LOAD_TOPLEVEL - we are learning about the toplevel resource of a + * pageload (NOTE: this should ONLY be used by tests) + * + * LEARN_LOAD_SUBRESOURCE - we are learning a subresource from a pageload + * + * LEARN_LOAD_REDIRECT - we are learning about the re-direct of a URI + * + * LEARN_STARTUP - we are learning about a page loaded during startup + */ + const SeerLearnReason LEARN_LOAD_TOPLEVEL = 0; + const SeerLearnReason LEARN_LOAD_SUBRESOURCE = 1; + const SeerLearnReason LEARN_LOAD_REDIRECT = 2; + const SeerLearnReason LEARN_STARTUP = 3; + + /** + * Add to our compendium of knowledge + * + * This adds to our prediction database to make things (hopefully) + * smarter next time we predict something. + * + * @param targetURI - The URI that was loaded that we are keeping track of. + * @param sourceURI - The URI that caused targetURI to be loaded (for page + * loads). This means the DOCUMENT URI. + * @param reason - The reason we are learning this bit of knowledge. + * Reason can be any of the LEARN_* values. + * In the case of LEARN_LOAD_SUBRESOURCE, targetURI should be the URI of a + * subresource of a page, and sourceURI should be the top-level URI. + * In the case of LEARN_LOAD_REDIRECT, targetURI is the NEW URI of a + * top-level resource that was redirected to, and sourceURI is the + * ORIGINAL URI of said top-level resource. + * In the case of LEARN_STARTUP, targetURI should be the URI of a page + * that was loaded immediately after browser startup, and sourceURI should + * be null. + * @param loadContext - The nsILoadContext for the page load that we are + * learning about. + */ + void learn(in nsIURI targetURI, + in nsIURI sourceURI, + in SeerLearnReason reason, + in nsILoadContext loadContext); + + /** + * Clear out all our learned knowledge + * + * This removes everything from our database so that any predictions begun + * after this completes will start from a blank slate. + */ + void reset(); +}; + +%{C++ +// Wrapper functions to make use of the seer easier and less invasive +class nsIChannel; +class nsIDocument; +class nsILoadContext; +class nsILoadGroup; +class nsINetworkSeerVerifier; + +namespace mozilla { +namespace net { + +nsresult SeerPredict(nsIURI *targetURI, + nsIURI *sourceURI, + SeerPredictReason reason, + nsILoadContext *loadContext, + nsINetworkSeerVerifier *verifier); + +nsresult SeerLearn(nsIURI *targetURI, + nsIURI *sourceURI, + SeerLearnReason reason, + nsILoadContext *loadContext); + +nsresult SeerLearn(nsIURI *targetURI, + nsIURI *sourceURI, + SeerLearnReason reason, + nsILoadGroup *loadGroup); + +nsresult SeerLearn(nsIURI *targetURI, + nsIURI *sourceURI, + SeerLearnReason reason, + nsIDocument *document); + +nsresult SeerLearnRedirect(nsIURI *targetURI, + nsIChannel *channel, + nsILoadContext *loadContext); + +} // mozilla::net +} // mozilla +%} diff --git a/netwerk/base/public/nsINetworkSeerVerifier.idl b/netwerk/base/public/nsINetworkSeerVerifier.idl new file mode 100644 index 000000000000..b8fabe360e3f --- /dev/null +++ b/netwerk/base/public/nsINetworkSeerVerifier.idl @@ -0,0 +1,31 @@ +/* vim: set ts=2 sts=2 et sw=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/. */ + +/** + * nsINetworkSeerVerifier - used for testing the network seer to ensure it + * does what we expect it to do. + */ + +#include "nsISupports.idl" + +interface nsIURI; + +[scriptable, uuid(ea273653-43a8-4632-8b30-4032e0918e8b)] +interface nsINetworkSeerVerifier : nsISupports +{ + /** + * Callback for when we do a predictive preconnect + * + * @param uri - The URI that was preconnected to + */ + void onPredictPreconnect(in nsIURI uri); + + /** + * Callback for when we do a predictive DNS lookup + * + * @param uri - The URI that was looked up + */ + void onPredictDNS(in nsIURI uri); +}; diff --git a/netwerk/base/public/nsISpeculativeConnect.idl b/netwerk/base/public/nsISpeculativeConnect.idl index f77628f45cbe..f4bcd3cf4039 100644 --- a/netwerk/base/public/nsISpeculativeConnect.idl +++ b/netwerk/base/public/nsISpeculativeConnect.idl @@ -30,3 +30,31 @@ interface nsISpeculativeConnect : nsISupports }; +/** + * This is used to override the default values for various values (documented + * inline) to determine whether or not to actually make a speculative + * connection. + */ +[builtinclass, uuid(2b6d6fb6-ab28-4f4c-af84-bfdbb7866d72)] +interface nsISpeculativeConnectionOverrider : nsISupports +{ + /** + * Used to determine the maximum number of unused speculative connections + * we will have open for a host at any one time + */ + [infallible] readonly attribute unsigned long parallelSpeculativeConnectLimit; + + /** + * Used to loosen the restrictions nsHttpConnectionMgr::RestrictConnections + * to allow more speculative connections when we're unsure if a host will + * connect via SPDY or not. + */ + [infallible] readonly attribute boolean ignorePossibleSpdyConnections; + + /** + * Used to determine if we will ignore the existence of any currently idle + * connections when we decide whether or not to make a speculative + * connection. + */ + [infallible] readonly attribute boolean ignoreIdle; +}; diff --git a/netwerk/base/src/Seer.cpp b/netwerk/base/src/Seer.cpp new file mode 100644 index 000000000000..861d0f12e4ef --- /dev/null +++ b/netwerk/base/src/Seer.cpp @@ -0,0 +1,2146 @@ +/* vim: set ts=2 sts=2 et sw=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/. */ + +#include + +#include "Seer.h" + +#include "nsAppDirectoryServiceDefs.h" +#include "nsICancelable.h" +#include "nsIChannel.h" +#include "nsIDNSListener.h" +#include "nsIDNSService.h" +#include "nsIDocument.h" +#include "nsIFile.h" +#include "nsILoadContext.h" +#include "nsILoadGroup.h" +#include "nsINetworkSeerVerifier.h" +#include "nsIObserverService.h" +#include "nsIPrefBranch.h" +#include "nsIPrefService.h" +#include "nsISpeculativeConnect.h" +#include "nsIURI.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" +#include "nsTArray.h" +#include "nsThreadUtils.h" +#include "prlog.h" + +#include "mozIStorageConnection.h" +#include "mozIStorageService.h" +#include "mozIStorageStatement.h" +#include "mozStorageHelper.h" + +#include "mozilla/Preferences.h" +#include "mozilla/storage.h" +#include "mozilla/Telemetry.h" + +using namespace mozilla; + +namespace mozilla { +namespace net { + +#define RETURN_IF_FAILED(_rv) \ + do { \ + if (NS_FAILED(_rv)) { \ + return; \ + } \ + } while (0) + +const char SEER_ENABLED_PREF[] = "network.seer.enabled"; +const char SEER_SSL_HOVER_PREF[] = "network.seer.enable-hover-on-ssl"; + +const char SEER_PAGE_DELTA_DAY_PREF[] = "network.seer.page-degradation.day"; +const int SEER_PAGE_DELTA_DAY_DEFAULT = 0; +const char SEER_PAGE_DELTA_WEEK_PREF[] = "network.seer.page-degradation.week"; +const int SEER_PAGE_DELTA_WEEK_DEFAULT = 5; +const char SEER_PAGE_DELTA_MONTH_PREF[] = "network.seer.page-degradation.month"; +const int SEER_PAGE_DELTA_MONTH_DEFAULT = 10; +const char SEER_PAGE_DELTA_YEAR_PREF[] = "network.seer.page-degradation.year"; +const int SEER_PAGE_DELTA_YEAR_DEFAULT = 25; +const char SEER_PAGE_DELTA_MAX_PREF[] = "network.seer.page-degradation.max"; +const int SEER_PAGE_DELTA_MAX_DEFAULT = 50; +const char SEER_SUB_DELTA_DAY_PREF[] = + "network.seer.subresource-degradation.day"; +const int SEER_SUB_DELTA_DAY_DEFAULT = 1; +const char SEER_SUB_DELTA_WEEK_PREF[] = + "network.seer.subresource-degradation.week"; +const int SEER_SUB_DELTA_WEEK_DEFAULT = 10; +const char SEER_SUB_DELTA_MONTH_PREF[] = + "network.seer.subresource-degradation.month"; +const int SEER_SUB_DELTA_MONTH_DEFAULT = 25; +const char SEER_SUB_DELTA_YEAR_PREF[] = + "network.seer.subresource-degradation.year"; +const int SEER_SUB_DELTA_YEAR_DEFAULT = 50; +const char SEER_SUB_DELTA_MAX_PREF[] = + "network.seer.subresource-degradation.max"; +const int SEER_SUB_DELTA_MAX_DEFAULT = 100; + +const char SEER_PRECONNECT_MIN_PREF[] = + "network.seer.preconnect-min-confidence"; +const int PRECONNECT_MIN_DEFAULT = 90; +const char SEER_PRERESOLVE_MIN_PREF[] = + "network.seer.preresolve-min-confidence"; +const int PRERESOLVE_MIN_DEFAULT = 60; +const char SEER_REDIRECT_LIKELY_PREF[] = + "network.seer.redirect-likely-confidence"; +const int REDIRECT_LIKELY_DEFAULT = 75; + +const char SEER_MAX_QUEUE_SIZE_PREF[] = "network.seer.max-queue-size"; +const uint32_t SEER_MAX_QUEUE_SIZE_DEFAULT = 50; + +// All these time values are in usec +const long long ONE_DAY = 86400LL * 1000000LL; +const long long ONE_WEEK = 7LL * ONE_DAY; +const long long ONE_MONTH = 30LL * ONE_DAY; +const long long ONE_YEAR = 365LL * ONE_DAY; + +const long STARTUP_WINDOW = 5L * 60L * 1000000L; // 5min + +// Version for the database schema +static const int32_t SEER_SCHEMA_VERSION = 1; + +struct SeerTelemetryAccumulators { + Telemetry::AutoCounter mPredictAttempts; + Telemetry::AutoCounter mLearnAttempts; + Telemetry::AutoCounter mPredictFullQueue; + Telemetry::AutoCounter mLearnFullQueue; + Telemetry::AutoCounter mTotalPredictions; + Telemetry::AutoCounter mTotalPreconnects; + Telemetry::AutoCounter mTotalPreresolves; + Telemetry::AutoCounter mPredictionsCalculated; +}; + +// Are you ready for the fun part? Because here comes the fun part. The seer, +// which will do awesome stuff as you browse to make your browsing experience +// faster. + +static Seer *gSeer = nullptr; + +#if defined(PR_LOGGING) +static PRLogModuleInfo *gSeerLog = nullptr; +#define SEER_LOG(args) PR_LOG(gSeerLog, 4, args) +#else +#define SEER_LOG(args) +#endif + +NS_IMPL_ISUPPORTS5(Seer, + nsINetworkSeer, + nsIObserver, + nsIDNSListener, + nsISpeculativeConnectionOverrider, + nsIInterfaceRequestor) + +Seer::Seer() + :mInitialized(false) + ,mEnabled(true) + ,mEnableHoverOnSSL(false) + ,mPageDegradationDay(SEER_PAGE_DELTA_DAY_DEFAULT) + ,mPageDegradationWeek(SEER_PAGE_DELTA_WEEK_DEFAULT) + ,mPageDegradationMonth(SEER_PAGE_DELTA_MONTH_DEFAULT) + ,mPageDegradationYear(SEER_PAGE_DELTA_YEAR_DEFAULT) + ,mPageDegradationMax(SEER_PAGE_DELTA_MAX_DEFAULT) + ,mSubresourceDegradationDay(SEER_SUB_DELTA_DAY_DEFAULT) + ,mSubresourceDegradationWeek(SEER_SUB_DELTA_WEEK_DEFAULT) + ,mSubresourceDegradationMonth(SEER_SUB_DELTA_MONTH_DEFAULT) + ,mSubresourceDegradationYear(SEER_SUB_DELTA_YEAR_DEFAULT) + ,mSubresourceDegradationMax(SEER_SUB_DELTA_MAX_DEFAULT) + ,mPreconnectMinConfidence(PRECONNECT_MIN_DEFAULT) + ,mPreresolveMinConfidence(PRERESOLVE_MIN_DEFAULT) + ,mRedirectLikelyConfidence(REDIRECT_LIKELY_DEFAULT) + ,mMaxQueueSize(SEER_MAX_QUEUE_SIZE_DEFAULT) + ,mStatements(mDB) + ,mLastStartupTime(0) + ,mStartupCount(0) + ,mQueueSize(0) + ,mQueueSizeLock("Seer.mQueueSizeLock") +{ +#if defined(PR_LOGGING) + gSeerLog = PR_NewLogModule("NetworkSeer"); +#endif + + MOZ_ASSERT(!gSeer, "multiple Seer instances!"); + gSeer = this; +} + +Seer::~Seer() +{ + if (mInitialized) + Shutdown(); + + RemoveObserver(); + + gSeer = nullptr; +} + +// Seer::nsIObserver + +nsresult +Seer::InstallObserver() +{ + MOZ_ASSERT(NS_IsMainThread(), "Installing observer off main thread"); + + nsresult rv = NS_OK; + nsCOMPtr obs = + mozilla::services::GetObserverService(); + if (!obs) { + return NS_ERROR_NOT_AVAILABLE; + } + + rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); + if (!prefs) { + return NS_ERROR_NOT_AVAILABLE; + } + + Preferences::AddBoolVarCache(&mEnabled, SEER_ENABLED_PREF, true); + Preferences::AddBoolVarCache(&mEnableHoverOnSSL, SEER_SSL_HOVER_PREF, false); + Preferences::AddIntVarCache(&mPageDegradationDay, SEER_PAGE_DELTA_DAY_PREF, + SEER_PAGE_DELTA_DAY_DEFAULT); + Preferences::AddIntVarCache(&mPageDegradationWeek, SEER_PAGE_DELTA_WEEK_PREF, + SEER_PAGE_DELTA_WEEK_DEFAULT); + Preferences::AddIntVarCache(&mPageDegradationMonth, + SEER_PAGE_DELTA_MONTH_PREF, + SEER_PAGE_DELTA_MONTH_DEFAULT); + Preferences::AddIntVarCache(&mPageDegradationYear, SEER_PAGE_DELTA_YEAR_PREF, + SEER_PAGE_DELTA_YEAR_DEFAULT); + Preferences::AddIntVarCache(&mPageDegradationMax, SEER_PAGE_DELTA_MAX_PREF, + SEER_PAGE_DELTA_MAX_DEFAULT); + + Preferences::AddIntVarCache(&mSubresourceDegradationDay, + SEER_SUB_DELTA_DAY_PREF, + SEER_SUB_DELTA_DAY_DEFAULT); + Preferences::AddIntVarCache(&mSubresourceDegradationWeek, + SEER_SUB_DELTA_WEEK_PREF, + SEER_SUB_DELTA_WEEK_DEFAULT); + Preferences::AddIntVarCache(&mSubresourceDegradationMonth, + SEER_SUB_DELTA_MONTH_PREF, + SEER_SUB_DELTA_MONTH_DEFAULT); + Preferences::AddIntVarCache(&mSubresourceDegradationYear, + SEER_SUB_DELTA_YEAR_PREF, + SEER_SUB_DELTA_YEAR_DEFAULT); + Preferences::AddIntVarCache(&mSubresourceDegradationMax, + SEER_SUB_DELTA_MAX_PREF, + SEER_SUB_DELTA_MAX_DEFAULT); + + Preferences::AddIntVarCache(&mPreconnectMinConfidence, + SEER_PRECONNECT_MIN_PREF, + PRECONNECT_MIN_DEFAULT); + Preferences::AddIntVarCache(&mPreresolveMinConfidence, + SEER_PRERESOLVE_MIN_PREF, + PRERESOLVE_MIN_DEFAULT); + Preferences::AddIntVarCache(&mRedirectLikelyConfidence, + SEER_REDIRECT_LIKELY_PREF, + REDIRECT_LIKELY_DEFAULT); + + Preferences::AddIntVarCache(&mMaxQueueSize, SEER_MAX_QUEUE_SIZE_PREF, + SEER_MAX_QUEUE_SIZE_DEFAULT); + + return rv; +} + +void +Seer::RemoveObserver() +{ + MOZ_ASSERT(NS_IsMainThread(), "Removing observer off main thread"); + + nsCOMPtr obs = + mozilla::services::GetObserverService(); + if (obs) { + obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); + } +} + +NS_IMETHODIMP +Seer::Observe(nsISupports *subject, const char *topic, + const PRUnichar *data_unicode) +{ + nsresult rv = NS_OK; + + if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic)) { + gSeer->Shutdown(); + } + + return rv; +} + +// Seer::nsIDNSListener + +NS_IMETHODIMP +Seer::OnLookupComplete(nsICancelable *request, nsIDNSRecord *rec, + nsresult status) +{ + return NS_OK; +} + +// Seer::nsISpeculativeConnectionOverrider + +NS_IMETHODIMP +Seer::GetIgnoreIdle(bool *ignoreIdle) +{ + *ignoreIdle = true; + return NS_OK; +} + +NS_IMETHODIMP +Seer::GetIgnorePossibleSpdyConnections(bool *ignorePossibleSpdyConnections) +{ + *ignorePossibleSpdyConnections = true; + return NS_OK; +} + +NS_IMETHODIMP +Seer::GetParallelSpeculativeConnectLimit( + uint32_t *parallelSpeculativeConnectLimit) +{ + *parallelSpeculativeConnectLimit = 6; + return NS_OK; +} + +// Seer::nsIInterfaceRequestor + +NS_IMETHODIMP +Seer::GetInterface(const nsIID &iid, void **result) +{ + return QueryInterface(iid, result); +} + +// Seer::nsINetworkSeer + +nsresult +Seer::Init() +{ + if (!NS_IsMainThread()) { + MOZ_ASSERT(false, "Seer::Init called off the main thread!"); + return NS_ERROR_UNEXPECTED; + } + + nsresult rv = NS_OK; + + mStartupTime = PR_Now(); + + mAccumulators = new SeerTelemetryAccumulators(); + + rv = InstallObserver(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = NS_NewNamedThread("Network Seer", getter_AddRefs(mIOThread)); + NS_ENSURE_SUCCESS(rv, rv); + + mSpeculativeService = do_GetService("@mozilla.org/network/io-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(mDBFile)); + NS_ENSURE_SUCCESS(rv, rv); + rv = mDBFile->AppendNative(NS_LITERAL_CSTRING("seer.sqlite")); + NS_ENSURE_SUCCESS(rv, rv); + + mInitialized = true; + + return rv; +} + +// Make sure that our sqlite storage is all set up with all the tables we need +// to do the work. It isn't the end of the world if this fails, since this is +// all an optimization, anyway. + +nsresult +Seer::EnsureInitStorage() +{ + MOZ_ASSERT(!NS_IsMainThread(), "Initializing seer storage on main thread"); + + if (mDB) { + return NS_OK; + } + + nsresult rv; + + rv = mStorageService->OpenDatabase(mDBFile, getter_AddRefs(mDB)); + if (NS_FAILED(rv)) { + // Retry once by trashing the file and trying to open again. If this fails, + // we can just bail, and hope for better luck next time. + rv = mDBFile->Remove(false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mStorageService->OpenDatabase(mDBFile, getter_AddRefs(mDB)); + NS_ENSURE_SUCCESS(rv, rv); + } + + mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF;")); + mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA foreign_keys = ON;")); + + // A table to make sure we're working with the database layout we expect + rv = mDB->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_seer_version (\n" + " version INTEGER NOT NULL\n" + ");\n")); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr stmt; + rv = mDB->CreateStatement( + NS_LITERAL_CSTRING("SELECT version FROM moz_seer_version;\n"), + getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasRows; + rv = stmt->ExecuteStep(&hasRows); + NS_ENSURE_SUCCESS(rv, rv); + if (hasRows) { + int32_t currentVersion; + rv = stmt->GetInt32(0, ¤tVersion); + NS_ENSURE_SUCCESS(rv, rv); + + // This is what we do while we only have one schema version. Later, we'll + // have to change this to actually upgrade things as appropriate. + MOZ_ASSERT(currentVersion == SEER_SCHEMA_VERSION, + "Invalid seer schema version!"); + if (currentVersion != SEER_SCHEMA_VERSION) { + return NS_ERROR_UNEXPECTED; + } + } else { + stmt = nullptr; + rv = mDB->CreateStatement( + NS_LITERAL_CSTRING("INSERT INTO moz_seer_version (version) VALUES " + "(:seer_version);"), + getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("seer_version"), + SEER_SCHEMA_VERSION); + NS_ENSURE_SUCCESS(rv, rv); + + stmt->Execute(); + } + + stmt = nullptr; + + // This table keeps track of the hosts we've seen at the top level of a + // pageload so we can map them to hosts used for subresources of a pageload. + rv = mDB->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_hosts (\n" + " id INTEGER PRIMARY KEY AUTOINCREMENT,\n" + " origin TEXT NOT NULL,\n" + " loads INTEGER DEFAULT 0,\n" + " last_load INTEGER DEFAULT 0\n" + ");\n")); + NS_ENSURE_SUCCESS(rv, rv); + + // And this is the table that keeps track of the hosts for subresources of a + // pageload. + rv = mDB->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_subhosts (\n" + " id INTEGER PRIMARY KEY AUTOINCREMENT,\n" + " hid INTEGER NOT NULL,\n" + " origin TEXT NOT NULL,\n" + " hits INTEGER DEFAULT 0,\n" + " last_hit INTEGER DEFAULT 0,\n" + " FOREIGN KEY(hid) REFERENCES moz_hosts(id) ON DELETE CASCADE\n" + ");\n")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mDB->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS subhost_hid_origin_index " + "ON moz_subhosts (hid, origin);")); + NS_ENSURE_SUCCESS(rv, rv); + + // Table to keep track of how many times we've started up, and when the last + // time was. + rv = mDB->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_startups (\n" + " startups INTEGER,\n" + " last_startup INTEGER\n" + ");\n")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mDB->CreateStatement( + NS_LITERAL_CSTRING("SELECT startups, last_startup FROM moz_startups;\n"), + getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + // We'll go ahead and keep track of our startup count here, since we can + // (mostly) equate "the service was created and asked to do stuff" with + // "the browser was started up". + rv = stmt->ExecuteStep(&hasRows); + NS_ENSURE_SUCCESS(rv, rv); + if (hasRows) { + // We've started up before. Update our startup statistics + stmt->GetInt32(0, &mStartupCount); + stmt->GetInt64(1, &mLastStartupTime); + + // This finalizes the statement + stmt = nullptr; + + rv = mDB->CreateStatement( + NS_LITERAL_CSTRING("UPDATE moz_startups SET startups = :startup_count " + "last_startup = :startup_time;\n"), + getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("startup_count"), + mStartupCount + 1); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startup_time"), + mStartupTime); + NS_ENSURE_SUCCESS(rv, rv); + + stmt->Execute(); + } else { + // This is our first startup, so let's go ahead and mark it as such + mStartupCount = 1; + + rv = mDB->CreateStatement( + NS_LITERAL_CSTRING("INSERT INTO moz_startups (startups, last_startup) " + "VALUES (1, :startup_time);\n"), + getter_AddRefs(stmt)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startup_time"), + mStartupTime); + NS_ENSURE_SUCCESS(rv, rv); + + stmt->Execute(); + } + + // This finalizes the statement + stmt = nullptr; + + // This table lists URIs loaded at startup, along with how many startups + // they've been loaded during, and when the last time was. + rv = mDB->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_startup_pages (\n" + " id INTEGER PRIMARY KEY AUTOINCREMENT,\n" + " uri TEXT NOT NULL,\n" + " hits INTEGER DEFAULT 0,\n" + " last_hit INTEGER DEFAULT 0\n" + ");\n")); + NS_ENSURE_SUCCESS(rv, rv); + + // This table is similar to moz_hosts above, but uses full URIs instead of + // hosts so that we can get more specific predictions for URIs that people + // visit often (such as their email or social network home pages). + rv = mDB->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_pages (\n" + " id integer PRIMARY KEY AUTOINCREMENT,\n" + " uri TEXT NOT NULL,\n" + " loads INTEGER DEFAULT 0,\n" + " last_load INTEGER DEFAULT 0\n" + ");\n")); + NS_ENSURE_SUCCESS(rv, rv); + + // This table is similar to moz_subhosts above, but is instead related to + // moz_pages for finer granularity. + rv = mDB->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_subresources (\n" + " id integer PRIMARY KEY AUTOINCREMENT,\n" + " pid INTEGER NOT NULL,\n" + " uri TEXT NOT NULL,\n" + " hits INTEGER DEFAULT 0,\n" + " last_hit INTEGER DEFAULT 0,\n" + " FOREIGN KEY(pid) REFERENCES moz_pages(id) ON DELETE CASCADE\n" + ");\n")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mDB->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS subresource_pid_uri_index " + "ON moz_subresources (pid, uri);")); + NS_ENSURE_SUCCESS(rv, rv); + + // This table keeps track of URIs and what they end up finally redirecting to + // so we can handle redirects in a sane fashion, as well. + rv = mDB->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_redirects (\n" + " id integer PRIMARY KEY AUTOINCREMENT,\n" + " pid integer NOT NULL,\n" + " uri TEXT NOT NULL,\n" + " origin TEXT NOT NULL,\n" + " hits INTEGER DEFAULT 0,\n" + " last_hit INTEGER DEFAULT 0,\n" + " FOREIGN KEY(pid) REFERENCES moz_pages(id)\n" + ");\n")); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +class SeerThreadShutdownRunner : public nsRunnable +{ +public: + SeerThreadShutdownRunner(nsIThread *ioThread) + :mIOThread(ioThread) + { } + + NS_IMETHODIMP Run() MOZ_OVERRIDE + { + MOZ_ASSERT(NS_IsMainThread(), "Shut down seer io thread off main thread"); + mIOThread->Shutdown(); + return NS_OK; + } + +private: + nsCOMPtr mIOThread; +}; + +class SeerDBShutdownRunner : public nsRunnable +{ +public: + SeerDBShutdownRunner(nsIThread *ioThread, nsINetworkSeer *seer) + :mIOThread(ioThread) + { + mSeer = new nsMainThreadPtrHolder(seer); + } + + NS_IMETHODIMP Run() MOZ_OVERRIDE + { + MOZ_ASSERT(!NS_IsMainThread(), "Shutting down DB on main thread"); + + gSeer->mStatements.FinalizeStatements(); + gSeer->mDB->Close(); + gSeer->mDB = nullptr; + + nsRefPtr runner = + new SeerThreadShutdownRunner(mIOThread); + NS_DispatchToMainThread(runner); + + return NS_OK; + } + +private: + nsCOMPtr mIOThread; + + // Death grip to keep seer alive while we cleanly close its DB connection + nsMainThreadPtrHandle mSeer; +}; + +void +Seer::Shutdown() +{ + if (!NS_IsMainThread()) { + MOZ_ASSERT(false, "Seer::Shutdown called off the main thread!"); + return; + } + + mInitialized = false; + + if (mIOThread) { + nsCOMPtr ioThread; + mIOThread.swap(ioThread); + + if (mDB) { + nsRefPtr runner = + new SeerDBShutdownRunner(ioThread, this); + ioThread->Dispatch(runner, NS_DISPATCH_NORMAL); + } else { + nsRefPtr runner = + new SeerThreadShutdownRunner(ioThread); + NS_DispatchToMainThread(runner); + } + } +} + +nsresult +Seer::Create(nsISupports *aOuter, const nsIID& aIID, + void **aResult) +{ + nsresult rv; + + if (aOuter != nullptr) { + return NS_ERROR_NO_AGGREGATION; + } + + nsRefPtr svc = new Seer(); + + rv = svc->Init(); + if (NS_FAILED(rv)) { + SEER_LOG(("Failed to initialize seer, seer will be a noop")); + } + + // We treat init failure the same as the service being disabled, since this + // is all an optimization anyway. No need to freak people out. That's why we + // gladly continue on QI'ing here. + rv = svc->QueryInterface(aIID, aResult); + + return rv; +} + +// Get the full origin (scheme, host, port) out of a URI (maybe should be part +// of nsIURI instead?) +static void +ExtractOrigin(nsIURI *uri, nsAutoCString &s) +{ + s.Truncate(); + + nsAutoCString scheme; + nsresult rv = uri->GetScheme(scheme); + RETURN_IF_FAILED(rv); + + nsAutoCString host; + rv = uri->GetAsciiHost(host); + RETURN_IF_FAILED(rv); + + int32_t port; + rv = uri->GetPort(&port); + RETURN_IF_FAILED(rv); + + s.Assign(scheme); + s.AppendLiteral("://"); + s.Append(host); + if (port != -1) { + s.AppendLiteral(":"); + s.AppendInt(port); + } +} + +// An event to do the work for a prediction that needs to hit the sqlite +// database. These events should be created on the main thread, and run on +// the seer thread. +class SeerPredictionEvent : public nsRunnable +{ +public: + SeerPredictionEvent(nsIURI *targetURI, nsIURI *sourceURI, + SeerPredictReason reason, + nsINetworkSeerVerifier *verifier) + :mReason(reason) + { + MOZ_ASSERT(NS_IsMainThread(), "Creating prediction event off main thread"); + + mEnqueueTime = TimeStamp::Now(); + + if (verifier) { + mVerifier = new nsMainThreadPtrHolder(verifier); + } + if (targetURI) { + targetURI->GetAsciiSpec(mTargetURI.spec); + ExtractOrigin(targetURI, mTargetURI.origin); + } + if (sourceURI) { + sourceURI->GetAsciiSpec(mSourceURI.spec); + ExtractOrigin(sourceURI, mSourceURI.origin); + } + } + + NS_IMETHOD Run() MOZ_OVERRIDE + { + MOZ_ASSERT(!NS_IsMainThread(), "Running prediction event on main thread"); + + Telemetry::AccumulateTimeDelta(Telemetry::SEER_PREDICT_WAIT_TIME, + mEnqueueTime); + + TimeStamp startTime = TimeStamp::Now(); + + nsresult rv = NS_OK; + + switch (mReason) { + case nsINetworkSeer::PREDICT_LOAD: + gSeer->PredictForPageload(mTargetURI, mVerifier, 0, mEnqueueTime); + break; + case nsINetworkSeer::PREDICT_STARTUP: + gSeer->PredictForStartup(mVerifier, mEnqueueTime); + break; + default: + MOZ_ASSERT(false, "Got unexpected value for predict reason"); + rv = NS_ERROR_UNEXPECTED; + } + + gSeer->FreeSpaceInQueue(); + + Telemetry::AccumulateTimeDelta(Telemetry::SEER_PREDICT_WORK_TIME, + startTime); + + return rv; + } + +private: + Seer::UriInfo mTargetURI; + Seer::UriInfo mSourceURI; + SeerPredictReason mReason; + SeerVerifierHandle mVerifier; + TimeStamp mEnqueueTime; +}; + +// Predicting for a link is easy, and doesn't require the round-trip to the +// seer thread and back to the main thread, since we don't have to hit the db +// for that. +void +Seer::PredictForLink(nsIURI *targetURI, nsIURI *sourceURI, + nsINetworkSeerVerifier *verifier) +{ + MOZ_ASSERT(NS_IsMainThread(), "Predicting for link off main thread"); + + if (!mSpeculativeService) { + return; + } + + if (!mEnableHoverOnSSL) { + bool isSSL = false; + sourceURI->SchemeIs("https", &isSSL); + if (isSSL) { + // We don't want to predict from an HTTPS page, to avoid info leakage + SEER_LOG(("Not predicting for link hover - on an SSL page")); + return; + } + } + + mSpeculativeService->SpeculativeConnect(targetURI, this); + if (verifier) { + verifier->OnPredictPreconnect(targetURI); + } +} + +// This runnable runs on the main thread, and is responsible for actually +// firing off predictive actions (such as TCP/TLS preconnects and DNS lookups) +class SeerPredictionRunner : public nsRunnable +{ +public: + SeerPredictionRunner(SeerVerifierHandle &verifier, TimeStamp predictStartTime) + :mVerifier(verifier) + ,mPredictStartTime(predictStartTime) + { } + + void AddPreconnect(const nsACString &uri) + { + mPreconnects.AppendElement(uri); + } + + void AddPreresolve(const nsACString &uri) + { + mPreresolves.AppendElement(uri); + } + + bool HasWork() const + { + return !(mPreconnects.IsEmpty() && mPreresolves.IsEmpty()); + } + + NS_IMETHOD Run() MOZ_OVERRIDE + { + MOZ_ASSERT(NS_IsMainThread(), "Running prediction off main thread"); + + Telemetry::AccumulateTimeDelta(Telemetry::SEER_PREDICT_TIME_TO_ACTION, + mPredictStartTime); + + uint32_t len, i; + + len = mPreconnects.Length(); + for (i = 0; i < len; ++i) { + nsCOMPtr uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), mPreconnects[i]); + if (NS_FAILED(rv)) { + continue; + } + + ++gSeer->mAccumulators->mTotalPredictions; + ++gSeer->mAccumulators->mTotalPreconnects; + gSeer->mSpeculativeService->SpeculativeConnect(uri, gSeer); + if (mVerifier) { + mVerifier->OnPredictPreconnect(uri); + } + } + + len = mPreresolves.Length(); + nsCOMPtr mainThread = do_GetMainThread(); + for (i = 0; i < len; ++i) { + nsCOMPtr uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), mPreresolves[i]); + if (NS_FAILED(rv)) { + continue; + } + + ++gSeer->mAccumulators->mTotalPredictions; + ++gSeer->mAccumulators->mTotalPreresolves; + nsAutoCString hostname; + uri->GetAsciiHost(hostname); + nsCOMPtr tmpCancelable; + gSeer->mDnsService->AsyncResolve(hostname, + (nsIDNSService::RESOLVE_PRIORITY_MEDIUM | + nsIDNSService::RESOLVE_SPECULATE), + gSeer, mainThread, + getter_AddRefs(tmpCancelable)); + if (mVerifier) { + mVerifier->OnPredictDNS(uri); + } + } + + mPreconnects.Clear(); + mPreresolves.Clear(); + + return NS_OK; + } + +private: + nsTArray mPreconnects; + nsTArray mPreresolves; + SeerVerifierHandle mVerifier; + TimeStamp mPredictStartTime; +}; + +// This calculates how much to degrade our confidence in our data based on +// the last time this top-level resource was loaded. This "global degradation" +// applies to *all* subresources we have associated with the top-level +// resource. This will be in addition to any reduction in confidence we have +// associated with a particular subresource. +int +Seer::CalculateGlobalDegradation(PRTime now, PRTime lastLoad) +{ + int globalDegradation; + PRTime delta = now - lastLoad; + if (delta < ONE_DAY) { + globalDegradation = mPageDegradationDay; + } else if (delta < ONE_WEEK) { + globalDegradation = mPageDegradationWeek; + } else if (delta < ONE_MONTH) { + globalDegradation = mPageDegradationMonth; + } else if (delta < ONE_YEAR) { + globalDegradation = mPageDegradationYear; + } else { + globalDegradation = mPageDegradationMax; + } + + Telemetry::Accumulate(Telemetry::SEER_GLOBAL_DEGRADATION, globalDegradation); + return globalDegradation; +} + +// This calculates our overall confidence that a particular subresource will be +// loaded as part of a top-level load. +// @param baseConfidence - the basic confidence we have for this subresource, +// which is the percentage of time this top-level load +// loads the subresource in question +// @param lastHit - the timestamp of the last time we loaded this subresource as +// part of this top-level load +// @param lastPossible - the timestamp of the last time we performed this +// top-level load +// @param globalDegradation - the degradation for this top-level load as +// determined by CalculateGlobalDegradation +int +Seer::CalculateConfidence(int baseConfidence, PRTime lastHit, + PRTime lastPossible, int globalDegradation) +{ + ++mAccumulators->mPredictionsCalculated; + + int maxConfidence = 100; + int confidenceDegradation = 0; + + if (lastHit < lastPossible) { + // We didn't load this subresource the last time this top-level load was + // performed, so let's not bother preconnecting (at the very least). + maxConfidence = mPreconnectMinConfidence - 1; + + // Now calculate how much we want to degrade our confidence based on how + // long it's been between the last time we did this top-level load and the + // last time this top-level load included this subresource. + PRTime delta = lastPossible - lastHit; + if (delta == 0) { + confidenceDegradation = 0; + } else if (delta < ONE_DAY) { + confidenceDegradation = mSubresourceDegradationDay; + } else if (delta < ONE_WEEK) { + confidenceDegradation = mSubresourceDegradationWeek; + } else if (delta < ONE_MONTH) { + confidenceDegradation = mSubresourceDegradationMonth; + } else if (delta < ONE_YEAR) { + confidenceDegradation = mSubresourceDegradationYear; + } else { + confidenceDegradation = mSubresourceDegradationMax; + maxConfidence = 0; + } + } + + // Calculate our confidence and clamp it to between 0 and maxConfidence + // (<= 100) + int confidence = baseConfidence - confidenceDegradation - globalDegradation; + confidence = std::max(confidence, 0); + confidence = std::min(confidence, maxConfidence); + + Telemetry::Accumulate(Telemetry::SEER_BASE_CONFIDENCE, baseConfidence); + Telemetry::Accumulate(Telemetry::SEER_SUBRESOURCE_DEGRADATION, + confidenceDegradation); + Telemetry::Accumulate(Telemetry::SEER_CONFIDENCE, confidence); + return confidence; +} + +// (Maybe) adds a predictive action to the prediction runner, based on our +// calculated confidence for the subresource in question. +void +Seer::SetupPrediction(int confidence, const nsACString &uri, + SeerPredictionRunner *runner) +{ + if (confidence >= mPreconnectMinConfidence) { + runner->AddPreconnect(uri); + } else if (confidence >= mPreresolveMinConfidence) { + runner->AddPreresolve(uri); + } +} + +// This gets the data about the top-level load from our database, either from +// the pages table (which is specific to a particular URI), or from the hosts +// table (which is for a particular origin). +bool +Seer::LookupTopLevel(QueryType queryType, const nsACString &key, + TopLevelInfo &info) +{ + MOZ_ASSERT(!NS_IsMainThread(), "LookupTopLevel called on main thread."); + + nsCOMPtr stmt; + if (queryType == QUERY_PAGE) { + stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("SELECT id, loads, last_load FROM moz_pages WHERE " + "uri = :key;")); + } else { + stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("SELECT id, loads, last_load FROM moz_hosts WHERE " + "origin = :key;")); + } + NS_ENSURE_TRUE(stmt, false); + mozStorageStatementScoper scope(stmt); + + nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("key"), key); + NS_ENSURE_SUCCESS(rv, false); + + bool hasRows; + rv = stmt->ExecuteStep(&hasRows); + NS_ENSURE_SUCCESS(rv, false); + + if (!hasRows) { + return false; + } + + rv = stmt->GetInt32(0, &info.id); + NS_ENSURE_SUCCESS(rv, false); + + rv = stmt->GetInt32(1, &info.loadCount); + NS_ENSURE_SUCCESS(rv, false); + + rv = stmt->GetInt64(2, &info.lastLoad); + NS_ENSURE_SUCCESS(rv, false); + + return true; +} + +// Insert data about either a top-level page or a top-level origin into +// the database. +void +Seer::AddTopLevel(QueryType queryType, const nsACString &key, PRTime now) +{ + MOZ_ASSERT(!NS_IsMainThread(), "AddTopLevel called on main thread."); + + nsCOMPtr stmt; + if (queryType == QUERY_PAGE) { + stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("INSERT INTO moz_pages (uri, loads, last_load) " + "VALUES (:key, 1, :now);")); + } else { + stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("INSERT INTO moz_hosts (origin, loads, last_load) " + "VALUES (:key, 1, :now);")); + } + if (!stmt) { + return; + } + mozStorageStatementScoper scope(stmt); + + // Loading a page implicitly makes the seer learn about the page, + // so since we don't have it already, let's add it. + nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("key"), key); + RETURN_IF_FAILED(rv); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("now"), now); + RETURN_IF_FAILED(rv); + + rv = stmt->Execute(); +} + +// Update data about either a top-level page or a top-level origin in the +// database. +void +Seer::UpdateTopLevel(QueryType queryType, const TopLevelInfo &info, PRTime now) +{ + MOZ_ASSERT(!NS_IsMainThread(), "UpdateTopLevel called on main thread."); + + nsCOMPtr stmt; + if (queryType == QUERY_PAGE) { + stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("UPDATE moz_pages SET loads = :load_count, " + "last_load = :now WHERE id = :id;")); + } else { + stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("UPDATE moz_hosts SET loads = :load_count, " + "last_load = :now WHERE id = :id;")); + } + if (!stmt) { + return; + } + mozStorageStatementScoper scope(stmt); + + // First, let's update the page in the database, since loading a page + // implicitly learns about the page. + nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("load_count"), + info.loadCount + 1); + RETURN_IF_FAILED(rv); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("now"), now); + RETURN_IF_FAILED(rv); + + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("id"), info.id); + RETURN_IF_FAILED(rv); + + rv = stmt->Execute(); +} + +// Tries to predict for a top-level load (either page-based or origin-based). +// Returns false if it failed to predict at all, true if it did some sort of +// prediction. +// @param queryType - whether to predict based on page or origin +// @param info - the db info about the top-level resource +bool +Seer::TryPredict(QueryType queryType, const TopLevelInfo &info, PRTime now, + SeerVerifierHandle &verifier, TimeStamp &predictStartTime) +{ + MOZ_ASSERT(!NS_IsMainThread(), "TryPredict called on main thread."); + + int globalDegradation = CalculateGlobalDegradation(now, info.lastLoad); + + // Now let's look up the subresources we know about for this page + nsCOMPtr stmt; + if (queryType == QUERY_PAGE) { + stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("SELECT uri, hits, last_hit FROM moz_subresources " + "WHERE pid = :id;")); + } else { + stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("SELECT origin, hits, last_hit FROM moz_subhosts " + "WHERE hid = :id;")); + } + NS_ENSURE_TRUE(stmt, false); + mozStorageStatementScoper scope(stmt); + + nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("id"), info.id); + NS_ENSURE_SUCCESS(rv, false); + + bool hasRows; + rv = stmt->ExecuteStep(&hasRows); + if (NS_FAILED(rv) || !hasRows) { + return false; + } + + nsRefPtr runner = + new SeerPredictionRunner(verifier, predictStartTime); + + while (hasRows) { + int32_t hitCount; + PRTime lastHit; + nsAutoCString subresource; + int baseConfidence, confidence; + + // We use goto nextrow here instead of just failling, because we want + // to do some sort of prediction if at all possible. Of course, it's + // probably unlikely that subsequent rows will succeed if one fails, but + // it's worth a shot. + + rv = stmt->GetUTF8String(0, subresource); + if NS_FAILED(rv) { + goto nextrow; + } + + rv = stmt->GetInt32(1, &hitCount); + if (NS_FAILED(rv)) { + goto nextrow; + } + + rv = stmt->GetInt64(2, &lastHit); + if (NS_FAILED(rv)) { + goto nextrow; + } + + baseConfidence = (hitCount * 100) / info.loadCount; + confidence = CalculateConfidence(baseConfidence, lastHit, info.lastLoad, + globalDegradation); + SetupPrediction(confidence, subresource, runner); + +nextrow: + rv = stmt->ExecuteStep(&hasRows); + NS_ENSURE_SUCCESS(rv, false); + } + + bool predicted = false; + + if (runner->HasWork()) { + NS_DispatchToMainThread(runner); + predicted = true; + } + + return predicted; +} + +// Find out if a top-level page is likely to redirect. +bool +Seer::WouldRedirect(const TopLevelInfo &info, PRTime now, UriInfo &newUri) +{ + MOZ_ASSERT(!NS_IsMainThread(), "WouldRedirect called on main thread."); + + nsCOMPtr stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("SELECT uri, origin, hits, last_hit " + "FROM moz_redirects WHERE pid = :id;")); + NS_ENSURE_TRUE(stmt, false); + mozStorageStatementScoper scope(stmt); + + nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("id"), info.id); + NS_ENSURE_SUCCESS(rv, false); + + bool hasRows; + rv = stmt->ExecuteStep(&hasRows); + if (NS_FAILED(rv) || !hasRows) { + return false; + } + + rv = stmt->GetUTF8String(0, newUri.spec); + NS_ENSURE_SUCCESS(rv, false); + + rv = stmt->GetUTF8String(1, newUri.origin); + NS_ENSURE_SUCCESS(rv, false); + + int32_t hitCount; + rv = stmt->GetInt32(2, &hitCount); + NS_ENSURE_SUCCESS(rv, false); + + PRTime lastHit; + rv = stmt->GetInt64(3, &lastHit); + NS_ENSURE_SUCCESS(rv, false); + + int globalDegradation = CalculateGlobalDegradation(now, info.lastLoad); + int baseConfidence = (hitCount * 100) / info.loadCount; + int confidence = CalculateConfidence(baseConfidence, lastHit, info.lastLoad, + globalDegradation); + + if (confidence > mRedirectLikelyConfidence) { + return true; + } + + return false; +} + +// This will add a page to our list of startup pages if it's being loaded +// before our startup window has expired. +void +Seer::MaybeLearnForStartup(const UriInfo &uri, const PRTime now) +{ + MOZ_ASSERT(!NS_IsMainThread(), "MaybeLearnForStartup called on main thread."); + + if ((now - mStartupTime) < STARTUP_WINDOW) { + LearnForStartup(uri); + } +} + +const int MAX_PAGELOAD_DEPTH = 10; + +// This is the driver for prediction based on a new pageload. +void +Seer::PredictForPageload(const UriInfo &uri, SeerVerifierHandle &verifier, + int stackCount, TimeStamp &predictStartTime) +{ + MOZ_ASSERT(!NS_IsMainThread(), "PredictForPageload called on main thread."); + + if (stackCount > MAX_PAGELOAD_DEPTH) { + SEER_LOG(("Too deep into pageload prediction")); + return; + } + + if (NS_FAILED(EnsureInitStorage())) { + return; + } + + PRTime now = PR_Now(); + + MaybeLearnForStartup(uri, now); + + TopLevelInfo pageInfo; + TopLevelInfo originInfo; + bool havePage = LookupTopLevel(QUERY_PAGE, uri.spec, pageInfo); + bool haveOrigin = LookupTopLevel(QUERY_ORIGIN, uri.origin, originInfo); + + if (!havePage) { + AddTopLevel(QUERY_PAGE, uri.spec, now); + } else { + UpdateTopLevel(QUERY_PAGE, pageInfo, now); + } + + if (!haveOrigin) { + AddTopLevel(QUERY_ORIGIN, uri.origin, now); + } else { + UpdateTopLevel(QUERY_ORIGIN, originInfo, now); + } + + UriInfo newUri; + if (havePage && WouldRedirect(pageInfo, now, newUri)) { + nsRefPtr runner = + new SeerPredictionRunner(verifier, predictStartTime); + runner->AddPreconnect(newUri.spec); + NS_DispatchToMainThread(runner); + PredictForPageload(newUri, verifier, stackCount + 1, predictStartTime); + return; + } + + bool predicted = false; + + // We always try to be as specific as possible in our predictions, so try + // to predict based on the full URI before we fall back to the origin. + if (havePage) { + predicted = TryPredict(QUERY_PAGE, pageInfo, now, verifier, + predictStartTime); + } + + if (!predicted && haveOrigin) { + predicted = TryPredict(QUERY_ORIGIN, originInfo, now, verifier, + predictStartTime); + } + + if (!predicted) { + Telemetry::AccumulateTimeDelta(Telemetry::SEER_PREDICT_TIME_TO_INACTION, + predictStartTime); + } +} + +// This is the driver for predicting at browser startup time based on pages that +// have previously been loaded close to startup. +void +Seer::PredictForStartup(SeerVerifierHandle &verifier, + TimeStamp &predictStartTime) +{ + MOZ_ASSERT(!NS_IsMainThread(), "PredictForStartup called on main thread"); + + if (NS_FAILED(EnsureInitStorage())) { + return; + } + + nsCOMPtr stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("SELECT uri, hits, last_hit FROM moz_startup_pages;")); + if (!stmt) { + return; + } + mozStorageStatementScoper scope(stmt); + nsresult rv; + bool hasRows; + + nsRefPtr runner = + new SeerPredictionRunner(verifier, predictStartTime); + + rv = stmt->ExecuteStep(&hasRows); + RETURN_IF_FAILED(rv); + + while (hasRows) { + nsAutoCString uri; + int32_t hitCount; + PRTime lastHit; + int baseConfidence, confidence; + + // We use goto nextrow here instead of just failling, because we want + // to do some sort of prediction if at all possible. Of course, it's + // probably unlikely that subsequent rows will succeed if one fails, but + // it's worth a shot. + + rv = stmt->GetUTF8String(0, uri); + if (NS_FAILED(rv)) { + goto nextrow; + } + + rv = stmt->GetInt32(1, &hitCount); + if (NS_FAILED(rv)) { + goto nextrow; + } + + rv = stmt->GetInt64(2, &lastHit); + if (NS_FAILED(rv)) { + goto nextrow; + } + + baseConfidence = (hitCount * 100) / mStartupCount; + confidence = CalculateConfidence(baseConfidence, lastHit, + mLastStartupTime, 0); + SetupPrediction(confidence, uri, runner); + +nextrow: + rv = stmt->ExecuteStep(&hasRows); + RETURN_IF_FAILED(rv); + } + + if (runner->HasWork()) { + NS_DispatchToMainThread(runner); + } else { + Telemetry::AccumulateTimeDelta(Telemetry::SEER_PREDICT_TIME_TO_INACTION, + predictStartTime); + } +} + +// All URIs we get passed *must* be http or https if they're not null. This +// helps ensure that. +static bool +IsNullOrHttp(nsIURI *uri) +{ + if (!uri) { + return true; + } + + bool isHTTP = false; + uri->SchemeIs("http", &isHTTP); + if (!isHTTP) { + uri->SchemeIs("https", &isHTTP); + } + + return isHTTP; +} + +nsresult +Seer::ReserveSpaceInQueue() +{ + MutexAutoLock lock(mQueueSizeLock); + + if (mQueueSize >= mMaxQueueSize) { + SEER_LOG(("Not enqueuing event - queue too large")); + return NS_ERROR_NOT_AVAILABLE; + } + + mQueueSize++; + return NS_OK; +} + +void +Seer::FreeSpaceInQueue() +{ + MutexAutoLock lock(mQueueSizeLock); + MOZ_ASSERT(mQueueSize > 0, "unexpected mQueueSize"); + mQueueSize--; +} + +// Called from the main thread to initiate predictive actions +NS_IMETHODIMP +Seer::Predict(nsIURI *targetURI, nsIURI *sourceURI, SeerPredictReason reason, + nsILoadContext *loadContext, nsINetworkSeerVerifier *verifier) +{ + MOZ_ASSERT(NS_IsMainThread(), + "Seer interface methods must be called on the main thread"); + + if (!mInitialized) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (!mEnabled) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (loadContext && loadContext->UsePrivateBrowsing()) { + // Don't want to do anything in PB mode + return NS_OK; + } + + if (!IsNullOrHttp(targetURI) || !IsNullOrHttp(sourceURI)) { + // Nothing we can do for non-HTTP[S] schemes + return NS_OK; + } + + // Ensure we've been given the appropriate arguments for the kind of + // prediction we're being asked to do + switch (reason) { + case nsINetworkSeer::PREDICT_LINK: + if (!targetURI || !sourceURI) { + return NS_ERROR_INVALID_ARG; + } + // Link hover is a special case where we can predict without hitting the + // db, so let's go ahead and fire off that prediction here. + PredictForLink(targetURI, sourceURI, verifier); + return NS_OK; + case nsINetworkSeer::PREDICT_LOAD: + if (!targetURI || sourceURI) { + return NS_ERROR_INVALID_ARG; + } + break; + case nsINetworkSeer::PREDICT_STARTUP: + if (targetURI || sourceURI) { + return NS_ERROR_INVALID_ARG; + } + break; + default: + return NS_ERROR_INVALID_ARG; + } + + ++mAccumulators->mPredictAttempts; + nsresult rv = ReserveSpaceInQueue(); + if (NS_FAILED(rv)) { + ++mAccumulators->mPredictFullQueue; + return NS_ERROR_NOT_AVAILABLE; + } + + nsRefPtr event = new SeerPredictionEvent(targetURI, + sourceURI, + reason, + verifier); + return mIOThread->Dispatch(event, NS_DISPATCH_NORMAL); +} + +// A runnable for updating our information in the database. This must always +// be dispatched to the seer thread. +class SeerLearnEvent : public nsRunnable +{ +public: + SeerLearnEvent(nsIURI *targetURI, nsIURI *sourceURI, SeerLearnReason reason) + :mReason(reason) + { + MOZ_ASSERT(NS_IsMainThread(), "Creating learn event off main thread"); + + mEnqueueTime = TimeStamp::Now(); + + targetURI->GetAsciiSpec(mTargetURI.spec); + ExtractOrigin(targetURI, mTargetURI.origin); + if (sourceURI) { + sourceURI->GetAsciiSpec(mSourceURI.spec); + ExtractOrigin(sourceURI, mSourceURI.origin); + } + } + + NS_IMETHOD Run() MOZ_OVERRIDE + { + MOZ_ASSERT(!NS_IsMainThread(), "Running learn off main thread"); + + nsresult rv = NS_OK; + + Telemetry::AccumulateTimeDelta(Telemetry::SEER_LEARN_WAIT_TIME, + mEnqueueTime); + + TimeStamp startTime = TimeStamp::Now(); + + switch (mReason) { + case nsINetworkSeer::LEARN_LOAD_TOPLEVEL: + gSeer->LearnForToplevel(mTargetURI); + break; + case nsINetworkSeer::LEARN_LOAD_REDIRECT: + gSeer->LearnForRedirect(mTargetURI, mSourceURI); + break; + case nsINetworkSeer::LEARN_LOAD_SUBRESOURCE: + gSeer->LearnForSubresource(mTargetURI, mSourceURI); + break; + case nsINetworkSeer::LEARN_STARTUP: + gSeer->LearnForStartup(mTargetURI); + break; + default: + MOZ_ASSERT(false, "Got unexpected value for learn reason"); + rv = NS_ERROR_UNEXPECTED; + } + + gSeer->FreeSpaceInQueue(); + + Telemetry::AccumulateTimeDelta(Telemetry::SEER_LEARN_WORK_TIME, startTime); + + return rv; + } +private: + Seer::UriInfo mTargetURI; + Seer::UriInfo mSourceURI; + SeerLearnReason mReason; + TimeStamp mEnqueueTime; +}; + +void +Seer::LearnForToplevel(const UriInfo &uri) +{ + MOZ_ASSERT(!NS_IsMainThread(), "LearnForToplevel called on main thread."); + + if (NS_FAILED(EnsureInitStorage())) { + return; + } + + PRTime now = PR_Now(); + + MaybeLearnForStartup(uri, now); + + TopLevelInfo pageInfo; + TopLevelInfo originInfo; + bool havePage = LookupTopLevel(QUERY_PAGE, uri.spec, pageInfo); + bool haveOrigin = LookupTopLevel(QUERY_ORIGIN, uri.origin, originInfo); + + if (!havePage) { + AddTopLevel(QUERY_PAGE, uri.spec, now); + } else { + UpdateTopLevel(QUERY_PAGE, pageInfo, now); + } + + if (!haveOrigin) { + AddTopLevel(QUERY_ORIGIN, uri.origin, now); + } else { + UpdateTopLevel(QUERY_ORIGIN, originInfo, now); + } +} + +// Queries to look up information about a *specific* subresource associated +// with a *specific* top-level load. +bool +Seer::LookupSubresource(QueryType queryType, const int32_t parentId, + const nsACString &key, SubresourceInfo &info) +{ + MOZ_ASSERT(!NS_IsMainThread(), "LookupSubresource called on main thread."); + + nsCOMPtr stmt; + if (queryType == QUERY_PAGE) { + stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("SELECT id, hits, last_hit FROM moz_subresources " + "WHERE pid = :parent_id AND uri = :key;")); + } else { + stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("SELECT id, hits, last_hit FROM moz_subhosts WHERE " + "hid = :parent_id AND origin = :key;")); + } + NS_ENSURE_TRUE(stmt, false); + mozStorageStatementScoper scope(stmt); + + nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("parent_id"), + parentId); + NS_ENSURE_SUCCESS(rv, false); + + rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("key"), key); + NS_ENSURE_SUCCESS(rv, false); + + bool hasRows; + rv = stmt->ExecuteStep(&hasRows); + NS_ENSURE_SUCCESS(rv, false); + if (!hasRows) { + return false; + } + + rv = stmt->GetInt32(0, &info.id); + NS_ENSURE_SUCCESS(rv, false); + + rv = stmt->GetInt32(1, &info.hitCount); + NS_ENSURE_SUCCESS(rv, false); + + rv = stmt->GetInt64(2, &info.lastHit); + NS_ENSURE_SUCCESS(rv, false); + + return true; +} + +// Add information about a new subresource associated with a top-level load. +void +Seer::AddSubresource(QueryType queryType, const int32_t parentId, + const nsACString &key, const PRTime now) +{ + MOZ_ASSERT(!NS_IsMainThread(), "AddSubresource called on main thread."); + + nsCOMPtr stmt; + if (queryType == QUERY_PAGE) { + stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("INSERT INTO moz_subresources " + "(pid, uri, hits, last_hit) VALUES " + "(:parent_id, :key, 1, :now);")); + } else { + stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("INSERT INTO moz_subhosts " + "(hid, origin, hits, last_hit) VALUES " + "(:parent_id, :key, 1, :now);")); + } + if (!stmt) { + return; + } + mozStorageStatementScoper scope(stmt); + + nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("parent_id"), + parentId); + RETURN_IF_FAILED(rv); + + rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("key"), key); + RETURN_IF_FAILED(rv); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("now"), now); + RETURN_IF_FAILED(rv); + + rv = stmt->Execute(); +} + +// Update the information about a particular subresource associated with a +// top-level load +void +Seer::UpdateSubresource(QueryType queryType, const SubresourceInfo &info, + const PRTime now) +{ + MOZ_ASSERT(!NS_IsMainThread(), "UpdateSubresource called on main thread."); + + nsCOMPtr stmt; + if (queryType == QUERY_PAGE) { + stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("UPDATE moz_subresources SET hits = :hit_count, " + "last_hit = :now WHERE id = :id;")); + } else { + stmt = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("UPDATE moz_subhosts SET hits = :hit_count, " + "last_hit = :now WHERE id = :id;")); + } + if (!stmt) { + return; + } + mozStorageStatementScoper scope(stmt); + + nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("hit_count"), + info.hitCount + 1); + RETURN_IF_FAILED(rv); + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("now"), now); + RETURN_IF_FAILED(rv); + + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("id"), info.id); + RETURN_IF_FAILED(rv); + + rv = stmt->Execute(); +} + +// Called when a subresource has been hit from a top-level load. Uses the two +// helper functions above to update the database appropriately. +void +Seer::LearnForSubresource(const UriInfo &targetURI, const UriInfo &sourceURI) +{ + MOZ_ASSERT(!NS_IsMainThread(), "LearnForSubresource called on main thread."); + + if (NS_FAILED(EnsureInitStorage())) { + return; + } + + TopLevelInfo pageInfo, originInfo; + bool havePage = LookupTopLevel(QUERY_PAGE, sourceURI.spec, pageInfo); + bool haveOrigin = LookupTopLevel(QUERY_ORIGIN, sourceURI.origin, + originInfo); + + if (!havePage && !haveOrigin) { + // Nothing to do, since we know nothing about the top level resource + return; + } + + SubresourceInfo resourceInfo; + bool haveResource = false; + if (havePage) { + haveResource = LookupSubresource(QUERY_PAGE, pageInfo.id, targetURI.spec, + resourceInfo); + } + + SubresourceInfo hostInfo; + bool haveHost = false; + if (haveOrigin) { + haveHost = LookupSubresource(QUERY_ORIGIN, originInfo.id, targetURI.origin, + hostInfo); + } + + PRTime now = PR_Now(); + + if (haveResource) { + UpdateSubresource(QUERY_PAGE, resourceInfo, now); + } else if (havePage) { + AddSubresource(QUERY_PAGE, pageInfo.id, targetURI.spec, now); + } + // Can't add a subresource to a page we don't have in our db. + + if (haveHost) { + UpdateSubresource(QUERY_ORIGIN, hostInfo, now); + } else if (haveOrigin) { + AddSubresource(QUERY_ORIGIN, originInfo.id, targetURI.origin, now); + } + // Can't add a subhost to a host we don't have in our db +} + +// This is called when a top-level loaded ended up redirecting to a different +// URI so we can keep track of that fact. +void +Seer::LearnForRedirect(const UriInfo &targetURI, const UriInfo &sourceURI) +{ + MOZ_ASSERT(!NS_IsMainThread(), "LearnForRedirect called on main thread."); + + if (NS_FAILED(EnsureInitStorage())) { + return; + } + + PRTime now = PR_Now(); + nsresult rv; + + nsCOMPtr getPage = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("SELECT id FROM moz_pages WHERE uri = :spec;")); + if (!getPage) { + return; + } + mozStorageStatementScoper scopedPage(getPage); + + // look up source in moz_pages + rv = getPage->BindUTF8StringByName(NS_LITERAL_CSTRING("spec"), + sourceURI.spec); + RETURN_IF_FAILED(rv); + + bool hasRows; + rv = getPage->ExecuteStep(&hasRows); + if (NS_FAILED(rv) || !hasRows) { + return; + } + + int32_t pageId; + rv = getPage->GetInt32(0, &pageId); + RETURN_IF_FAILED(rv); + + nsCOMPtr getRedirect = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("SELECT id, hits FROM moz_redirects WHERE " + "pid = :page_id AND uri = :spec;")); + if (!getRedirect) { + return; + } + mozStorageStatementScoper scopedRedirect(getRedirect); + + rv = getRedirect->BindInt32ByName(NS_LITERAL_CSTRING("page_id"), pageId); + RETURN_IF_FAILED(rv); + + rv = getRedirect->BindUTF8StringByName(NS_LITERAL_CSTRING("spec"), + targetURI.spec); + RETURN_IF_FAILED(rv); + + rv = getRedirect->ExecuteStep(&hasRows); + RETURN_IF_FAILED(rv); + + if (!hasRows) { + // This is the first time we've seen this top-level redirect to this URI + nsCOMPtr addRedirect = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("INSERT INTO moz_redirects " + "(pid, uri, origin, hits, last_hit) VALUES " + "(:page_id, :spec, :origin, 1, :now);")); + if (!addRedirect) { + return; + } + mozStorageStatementScoper scopedAdd(addRedirect); + + rv = addRedirect->BindInt32ByName(NS_LITERAL_CSTRING("page_id"), pageId); + RETURN_IF_FAILED(rv); + + rv = addRedirect->BindUTF8StringByName(NS_LITERAL_CSTRING("spec"), + targetURI.spec); + RETURN_IF_FAILED(rv); + + rv = addRedirect->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), + targetURI.origin); + RETURN_IF_FAILED(rv); + + rv = addRedirect->BindInt64ByName(NS_LITERAL_CSTRING("now"), now); + RETURN_IF_FAILED(rv); + + rv = addRedirect->Execute(); + } else { + // We've seen this redirect before + int32_t redirId, hits; + rv = getRedirect->GetInt32(0, &redirId); + RETURN_IF_FAILED(rv); + + rv = getRedirect->GetInt32(1, &hits); + RETURN_IF_FAILED(rv); + + nsCOMPtr updateRedirect = + mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("UPDATE moz_redirects SET hits = :hits, " + "last_hit = :now WHERE id = :redir;")); + if (!updateRedirect) { + return; + } + mozStorageStatementScoper scopedUpdate(updateRedirect); + + rv = updateRedirect->BindInt32ByName(NS_LITERAL_CSTRING("hits"), hits + 1); + RETURN_IF_FAILED(rv); + + rv = updateRedirect->BindInt64ByName(NS_LITERAL_CSTRING("now"), now); + RETURN_IF_FAILED(rv); + + rv = updateRedirect->BindInt32ByName(NS_LITERAL_CSTRING("redir"), redirId); + RETURN_IF_FAILED(rv); + + updateRedirect->Execute(); + } +} + +// Add information about a top-level load to our list of startup pages +void +Seer::LearnForStartup(const UriInfo &uri) +{ + MOZ_ASSERT(!NS_IsMainThread(), "LearnForStartup called on main thread."); + + if (NS_FAILED(EnsureInitStorage())) { + return; + } + + nsCOMPtr getPage = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("SELECT id, hits FROM moz_startup_pages WHERE " + "uri = :origin;")); + if (!getPage) { + return; + } + mozStorageStatementScoper scopedPage(getPage); + nsresult rv; + + rv = getPage->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), uri.origin); + RETURN_IF_FAILED(rv); + + bool hasRows; + rv = getPage->ExecuteStep(&hasRows); + RETURN_IF_FAILED(rv); + + if (hasRows) { + // We've loaded this page on startup before + int32_t pageId, hitCount; + + rv = getPage->GetInt32(0, &pageId); + RETURN_IF_FAILED(rv); + + rv = getPage->GetInt32(1, &hitCount); + RETURN_IF_FAILED(rv); + + nsCOMPtr updatePage = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("UPDATE moz_startup_pages SET hits = :hit_count, " + "last_hit = :startup_time WHERE id = :page_id;")); + if (!updatePage) { + return; + } + mozStorageStatementScoper scopedUpdate(updatePage); + + rv = updatePage->BindInt32ByName(NS_LITERAL_CSTRING("hit_count"), + hitCount + 1); + RETURN_IF_FAILED(rv); + + rv = updatePage->BindInt64ByName(NS_LITERAL_CSTRING("startup_time"), + mStartupTime); + RETURN_IF_FAILED(rv); + + rv = updatePage->BindInt32ByName(NS_LITERAL_CSTRING("page_id"), pageId); + RETURN_IF_FAILED(rv); + + updatePage->Execute(); + } else { + // New startup page + nsCOMPtr addPage = mStatements.GetCachedStatement( + NS_LITERAL_CSTRING("INSERT INTO moz_startup_pages (uri, hits, " + "last_hit) VALUES (:origin, 1, :startup_time);")); + if (!addPage) { + return; + } + mozStorageStatementScoper scopedAdd(addPage); + rv = addPage->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), + uri.origin); + RETURN_IF_FAILED(rv); + + rv = addPage->BindInt64ByName(NS_LITERAL_CSTRING("startup_time"), + mStartupTime); + RETURN_IF_FAILED(rv); + + addPage->Execute(); + } +} + +// Called from the main thread to update the database +NS_IMETHODIMP +Seer::Learn(nsIURI *targetURI, nsIURI *sourceURI, SeerLearnReason reason, + nsILoadContext *loadContext) +{ + MOZ_ASSERT(NS_IsMainThread(), + "Seer interface methods must be called on the main thread"); + + if (!mInitialized) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (!mEnabled) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (loadContext && loadContext->UsePrivateBrowsing()) { + // Don't want to do anything in PB mode + return NS_OK; + } + + if (!IsNullOrHttp(targetURI) || !IsNullOrHttp(sourceURI)) { + return NS_ERROR_INVALID_ARG; + } + + switch (reason) { + case nsINetworkSeer::LEARN_LOAD_TOPLEVEL: + case nsINetworkSeer::LEARN_STARTUP: + if (!targetURI || sourceURI) { + return NS_ERROR_INVALID_ARG; + } + break; + case nsINetworkSeer::LEARN_LOAD_REDIRECT: + case nsINetworkSeer::LEARN_LOAD_SUBRESOURCE: + if (!targetURI || !sourceURI) { + return NS_ERROR_INVALID_ARG; + } + break; + default: + return NS_ERROR_INVALID_ARG; + } + + ++mAccumulators->mLearnAttempts; + nsresult rv = ReserveSpaceInQueue(); + if (NS_FAILED(rv)) { + ++mAccumulators->mLearnFullQueue; + return NS_ERROR_NOT_AVAILABLE; + } + + nsRefPtr event = new SeerLearnEvent(targetURI, sourceURI, + reason); + return mIOThread->Dispatch(event, NS_DISPATCH_NORMAL); +} + +// Runnable to clear out the database. Dispatched from the main thread to the +// seer thread +class SeerResetEvent : public nsRunnable +{ +public: + SeerResetEvent() + { } + + NS_IMETHOD Run() MOZ_OVERRIDE + { + MOZ_ASSERT(!NS_IsMainThread(), "Running reset on main thread"); + + gSeer->ResetInternal(); + + return NS_OK; + } +}; + +// Helper that actually does the database wipe. +void +Seer::ResetInternal() +{ + MOZ_ASSERT(!NS_IsMainThread(), "Resetting db on main thread"); + + nsresult rv = EnsureInitStorage(); + RETURN_IF_FAILED(rv); + + mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_redirects")); + mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_startup_pages")); + mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_startups")); + + // These cascade to moz_subresources and moz_subhosts, respectively. + mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_pages")); + mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_hosts")); +} + +// Called on the main thread to clear out all our knowledge. Tabula Rasa FTW! +NS_IMETHODIMP +Seer::Reset() +{ + MOZ_ASSERT(NS_IsMainThread(), + "Seer interface methods must be called on the main thread"); + + if (!mInitialized) { + return NS_ERROR_NOT_AVAILABLE; + } + + nsRefPtr event = new SeerResetEvent(); + return mIOThread->Dispatch(event, NS_DISPATCH_NORMAL); +} + +// Helper functions to make using the seer easier from native code + +static nsresult +EnsureGlobalSeer(nsINetworkSeer **aSeer) +{ + nsresult rv; + nsCOMPtr seer = do_GetService("@mozilla.org/network/seer;1", + &rv); + NS_ENSURE_SUCCESS(rv, rv); + + NS_IF_ADDREF(*aSeer = seer); + return NS_OK; +} + +nsresult +SeerPredict(nsIURI *targetURI, nsIURI *sourceURI, SeerPredictReason reason, + nsILoadContext *loadContext, nsINetworkSeerVerifier *verifier) +{ + nsCOMPtr seer; + nsresult rv = EnsureGlobalSeer(getter_AddRefs(seer)); + NS_ENSURE_SUCCESS(rv, rv); + + return seer->Predict(targetURI, sourceURI, reason, loadContext, verifier); +} + +nsresult +SeerLearn(nsIURI *targetURI, nsIURI *sourceURI, SeerLearnReason reason, + nsILoadContext *loadContext) +{ + nsCOMPtr seer; + nsresult rv = EnsureGlobalSeer(getter_AddRefs(seer)); + NS_ENSURE_SUCCESS(rv, rv); + + return seer->Learn(targetURI, sourceURI, reason, loadContext); +} + +nsresult +SeerLearn(nsIURI *targetURI, nsIURI *sourceURI, SeerLearnReason reason, + nsILoadGroup *loadGroup) +{ + nsCOMPtr seer; + nsresult rv = EnsureGlobalSeer(getter_AddRefs(seer)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr loadContext; + + if (loadGroup) { + nsCOMPtr callbacks; + loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks)); + if (callbacks) { + loadContext = do_GetInterface(callbacks); + } + } + + return seer->Learn(targetURI, sourceURI, reason, loadContext); +} + +nsresult +SeerLearn(nsIURI *targetURI, nsIURI *sourceURI, SeerLearnReason reason, + nsIDocument *document) +{ + nsCOMPtr seer; + nsresult rv = EnsureGlobalSeer(getter_AddRefs(seer)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr loadContext; + + if (document) { + loadContext = document->GetLoadContext(); + } + + return seer->Learn(targetURI, sourceURI, reason, loadContext); +} + +nsresult +SeerLearnRedirect(nsIURI *targetURI, nsIChannel *channel, + nsILoadContext *loadContext) +{ + nsCOMPtr seer; + nsresult rv = EnsureGlobalSeer(getter_AddRefs(seer)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr sourceURI; + rv = channel->GetOriginalURI(getter_AddRefs(sourceURI)); + NS_ENSURE_SUCCESS(rv, rv); + + bool sameUri; + rv = targetURI->Equals(sourceURI, &sameUri); + NS_ENSURE_SUCCESS(rv, rv); + + if (sameUri) { + return NS_OK; + } + + return seer->Learn(targetURI, sourceURI, + nsINetworkSeer::LEARN_LOAD_REDIRECT, loadContext); +} + +} // ::mozilla::net +} // ::mozilla diff --git a/netwerk/base/src/Seer.h b/netwerk/base/src/Seer.h new file mode 100644 index 000000000000..73e831ab5b8f --- /dev/null +++ b/netwerk/base/src/Seer.h @@ -0,0 +1,206 @@ +/* vim: set ts=2 sts=2 et sw=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/. */ + +#ifndef mozilla_net_Seer_h +#define mozilla_net_Seer_h + +#include "nsINetworkSeer.h" + +#include "nsCOMPtr.h" +#include "nsIDNSListener.h" +#include "nsIInterfaceRequestor.h" +#include "nsIObserver.h" +#include "nsISpeculativeConnect.h" +#include "nsProxyRelease.h" + +#include "mozilla/Mutex.h" +#include "mozilla/storage/StatementCache.h" +#include "mozilla/TimeStamp.h" + +class nsIDNSService; +class nsINetworkSeerVerifier; +class nsIThread; + +class mozIStorageConnection; +class mozIStorageService; +class mozIStorageStatement; + +namespace mozilla { +namespace net { + +typedef nsMainThreadPtrHandle SeerVerifierHandle; + +class SeerPredictionRunner; +struct SeerTelemetryAccumulators; + +class Seer : public nsINetworkSeer + , public nsIObserver + , public nsIDNSListener + , public nsISpeculativeConnectionOverrider + , public nsIInterfaceRequestor +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSINETWORKSEER + NS_DECL_NSIOBSERVER + NS_DECL_NSIDNSLISTENER + NS_DECL_NSISPECULATIVECONNECTIONOVERRIDER + NS_DECL_NSIINTERFACEREQUESTOR + + Seer(); + virtual ~Seer(); + + nsresult Init(); + void Shutdown(); + static nsresult Create(nsISupports *outer, const nsIID& iid, void **result); + +private: + friend class SeerPredictionEvent; + friend class SeerLearnEvent; + friend class SeerResetEvent; + friend class SeerPredictionRunner; + friend class SeerDBShutdownRunner; + + nsresult EnsureInitStorage(); + + // This is a proxy for the information we need from an nsIURI + struct UriInfo { + nsAutoCString spec; + nsAutoCString origin; + }; + + void PredictForLink(nsIURI *targetURI, + nsIURI *sourceURI, + nsINetworkSeerVerifier *verifier); + void PredictForPageload(const UriInfo &dest, + SeerVerifierHandle &verifier, + int stackCount, + TimeStamp &predictStartTime); + void PredictForStartup(SeerVerifierHandle &verifier, + TimeStamp &predictStartTime); + + // Whether we're working on a page or an origin + enum QueryType { + QUERY_PAGE = 0, + QUERY_ORIGIN + }; + + // Holds info from the db about a top-level page or origin + struct TopLevelInfo { + int32_t id; + int32_t loadCount; + PRTime lastLoad; + }; + + // Holds info from the db about a subresource + struct SubresourceInfo { + int32_t id; + int32_t hitCount; + PRTime lastHit; + }; + + nsresult ReserveSpaceInQueue(); + void FreeSpaceInQueue(); + + int CalculateGlobalDegradation(PRTime now, + PRTime lastLoad); + int CalculateConfidence(int baseConfidence, + PRTime lastHit, + PRTime lastPossible, + int globalDegradation); + void SetupPrediction(int confidence, + const nsACString &uri, + SeerPredictionRunner *runner); + + bool LookupTopLevel(QueryType queryType, + const nsACString &key, + TopLevelInfo &info); + void AddTopLevel(QueryType queryType, + const nsACString &key, + PRTime now); + void UpdateTopLevel(QueryType queryType, + const TopLevelInfo &info, + PRTime now); + bool TryPredict(QueryType queryType, + const TopLevelInfo &info, + PRTime now, + SeerVerifierHandle &verifier, + TimeStamp &predictStartTime); + bool WouldRedirect(const TopLevelInfo &info, + PRTime now, + UriInfo &newUri); + + bool LookupSubresource(QueryType queryType, + const int32_t parentId, + const nsACString &key, + SubresourceInfo &info); + void AddSubresource(QueryType queryType, + const int32_t parentId, + const nsACString &key, PRTime now); + void UpdateSubresource(QueryType queryType, + const SubresourceInfo &info, + PRTime now); + + void MaybeLearnForStartup(const UriInfo &uri, const PRTime now); + + void LearnForToplevel(const UriInfo &uri); + void LearnForSubresource(const UriInfo &targetURI, const UriInfo &sourceURI); + void LearnForRedirect(const UriInfo &targetURI, const UriInfo &sourceURI); + void LearnForStartup(const UriInfo &uri); + + void ResetInternal(); + + // Observer-related stuff + nsresult InstallObserver(); + void RemoveObserver(); + + bool mInitialized; + + bool mEnabled; + bool mEnableHoverOnSSL; + + int mPageDegradationDay; + int mPageDegradationWeek; + int mPageDegradationMonth; + int mPageDegradationYear; + int mPageDegradationMax; + + int mSubresourceDegradationDay; + int mSubresourceDegradationWeek; + int mSubresourceDegradationMonth; + int mSubresourceDegradationYear; + int mSubresourceDegradationMax; + + int mPreconnectMinConfidence; + int mPreresolveMinConfidence; + int mRedirectLikelyConfidence; + + int32_t mMaxQueueSize; + + nsCOMPtr mIOThread; + + nsCOMPtr mSpeculativeService; + + nsCOMPtr mDBFile; + nsCOMPtr mStorageService; + nsCOMPtr mDB; + mozilla::storage::StatementCache mStatements; + + PRTime mStartupTime; + PRTime mLastStartupTime; + int32_t mStartupCount; + + nsCOMPtr mDnsService; + + int32_t mQueueSize; + mozilla::Mutex mQueueSizeLock; + + nsAutoPtr mAccumulators; +}; + +} // ::mozilla::net +} // ::mozilla + +#endif // mozilla_net_Seer_h diff --git a/netwerk/base/src/moz.build b/netwerk/base/src/moz.build index 1b4e57cff867..914db2018b04 100644 --- a/netwerk/base/src/moz.build +++ b/netwerk/base/src/moz.build @@ -74,6 +74,7 @@ CPP_SOURCES += [ 'nsURLHelper.cpp', 'nsURLParsers.cpp', 'nsUnicharStreamLoader.cpp', + 'Seer.cpp', ] if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'os2': diff --git a/netwerk/build/nsNetCID.h b/netwerk/build/nsNetCID.h index cc5a4caed96e..544bdc74150f 100644 --- a/netwerk/build/nsNetCID.h +++ b/netwerk/build/nsNetCID.h @@ -436,6 +436,17 @@ { 0x8d, 0x17, 0xa2, 0x7e, 0x44, 0xa8, 0x39, 0x3e } \ } +// service implementing nsINetworkSeer +#define NS_NETWORKSEER_CONTRACTID \ + "@mozilla.org/network/seer;1" +#define NS_NETWORKSEER_CID \ +{ /* {1C218009-A531-46AD-8351-1E7F45D5A3C4} */ \ + 0x1C218009, \ + 0xA531, \ + 0x46AD, \ + { 0x83, 0x51, 0x1E, 0x7F, 0x45, 0xD5, 0xA3, 0xC4 } \ +} + /****************************************************************************** * netwerk/cache/ classes */ diff --git a/netwerk/build/nsNetModule.cpp b/netwerk/build/nsNetModule.cpp index 3f7577314715..45641ff8bd4f 100644 --- a/netwerk/build/nsNetModule.cpp +++ b/netwerk/build/nsNetModule.cpp @@ -36,6 +36,7 @@ #include "nsXULAppAPI.h" #include "nsCategoryCache.h" #include "nsIContentSniffer.h" +#include "Seer.h" #include "nsNetUtil.h" #include "mozilla/net/NeckoChild.h" @@ -812,6 +813,7 @@ NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_SERIALIZATION_HELPER_CID); NS_DEFINE_NAMED_CID(NS_REDIRECTCHANNELREGISTRAR_CID); NS_DEFINE_NAMED_CID(NS_CACHE_STORAGE_SERVICE_CID); +NS_DEFINE_NAMED_CID(NS_NETWORKSEER_CID); static const mozilla::Module::CIDEntry kNeckoCIDs[] = { { &kNS_IOSERVICE_CID, false, nullptr, nsIOServiceConstructor }, @@ -952,6 +954,7 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = { { &kNS_SERIALIZATION_HELPER_CID, false, nullptr, nsSerializationHelperConstructor }, { &kNS_REDIRECTCHANNELREGISTRAR_CID, false, nullptr, RedirectChannelRegistrarConstructor }, { &kNS_CACHE_STORAGE_SERVICE_CID, false, nullptr, CacheStorageServiceConstructor }, + { &kNS_NETWORKSEER_CID, false, NULL, mozilla::net::Seer::Create }, { nullptr } }; @@ -1095,6 +1098,7 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = { { NS_SERIALIZATION_HELPER_CONTRACTID, &kNS_SERIALIZATION_HELPER_CID }, { NS_REDIRECTCHANNELREGISTRAR_CONTRACTID, &kNS_REDIRECTCHANNELREGISTRAR_CID }, { NS_CACHE_STORAGE_SERVICE_CONTRACTID, &kNS_CACHE_STORAGE_SERVICE_CID }, + { NS_NETWORKSEER_CONTRACTID, &kNS_NETWORKSEER_CID }, { nullptr } }; diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index fce775e1c4f2..61337f22541a 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -320,27 +320,70 @@ nsHttpConnectionMgr::DoShiftReloadConnectionCleanup(nsHttpConnectionInfo *aCI) return rv; } +class SpeculativeConnectArgs +{ +public: + SpeculativeConnectArgs() { mOverridesOK = false; } + virtual ~SpeculativeConnectArgs() {} + + // Added manually so we can use nsRefPtr without inheriting from + // nsISupports + NS_IMETHOD_(nsrefcnt) AddRef(void); + NS_IMETHOD_(nsrefcnt) Release(void); + +public: // intentional! + nsRefPtr mTrans; + + bool mOverridesOK; + uint32_t mParallelSpeculativeConnectLimit; + bool mIgnoreIdle; + bool mIgnorePossibleSpdyConnections; + + // As above, added manually so we can use nsRefPtr without inheriting from + // nsISupports +protected: + ::mozilla::ThreadSafeAutoRefCnt mRefCnt; + NS_DECL_OWNINGTHREAD +}; + +NS_IMPL_ADDREF(SpeculativeConnectArgs) +NS_IMPL_RELEASE(SpeculativeConnectArgs) + nsresult nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci, nsIInterfaceRequestor *callbacks, uint32_t caps) { + MOZ_ASSERT(NS_IsMainThread(), "nsHttpConnectionMgr::SpeculativeConnect called off main thread!"); + LOG(("nsHttpConnectionMgr::SpeculativeConnect [ci=%s]\n", ci->HashKey().get())); + nsRefPtr args = new SpeculativeConnectArgs(); + // Wrap up the callbacks and the target to ensure they're released on the target // thread properly. nsCOMPtr wrappedCallbacks; NS_NewInterfaceRequestorAggregation(callbacks, nullptr, getter_AddRefs(wrappedCallbacks)); caps |= ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0; - nsRefPtr trans = - new NullHttpTransaction(ci, wrappedCallbacks, caps); + args->mTrans = new NullHttpTransaction(ci, wrappedCallbacks, caps); + + nsCOMPtr overrider = + do_GetInterface(callbacks); + if (overrider) { + args->mOverridesOK = true; + overrider->GetParallelSpeculativeConnectLimit( + &args->mParallelSpeculativeConnectLimit); + overrider->GetIgnoreIdle(&args->mIgnoreIdle); + overrider->GetIgnorePossibleSpdyConnections( + &args->mIgnorePossibleSpdyConnections); + } nsresult rv = - PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, trans); + PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, args); if (NS_SUCCEEDED(rv)) - trans.forget(); + args.forget(); return rv; } @@ -1229,7 +1272,8 @@ nsHttpConnectionMgr::ClosePersistentConnectionsCB(const nsACString &key, } bool -nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent) +nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent, + bool ignorePossibleSpdyConnections) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); @@ -1239,7 +1283,8 @@ nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent) bool doRestrict = ent->mConnInfo->UsingSSL() && gHttpHandler->IsSpdyEnabled() && - (!ent->mTestedSpdy || ent->mUsingSpdy) && + ((!ent->mTestedSpdy && !ignorePossibleSpdyConnections) || + ent->mUsingSpdy) && (ent->mHalfOpens.Length() || ent->mActiveConns.Length()); // If there are no restrictions, we are done @@ -1248,7 +1293,7 @@ nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent) // If the restriction is based on a tcp handshake in progress // let that connect and then see if it was SPDY or not - if (ent->UnconnectedHalfOpens()) + if (ent->UnconnectedHalfOpens() && !ignorePossibleSpdyConnections) return true; // There is a concern that a host is using a mix of HTTP/1 and SPDY. @@ -2520,14 +2565,14 @@ nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, void *param) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - nsRefPtr trans = - dont_AddRef(static_cast(param)); + nsRefPtr args = + dont_AddRef(static_cast(param)); LOG(("nsHttpConnectionMgr::OnMsgSpeculativeConnect [ci=%s]\n", - trans->ConnectionInfo()->HashKey().get())); + args->mTrans->ConnectionInfo()->HashKey().get())); nsConnectionEntry *ent = - GetOrCreateConnectionEntry(trans->ConnectionInfo()); + GetOrCreateConnectionEntry(args->mTrans->ConnectionInfo()); // If spdy has previously made a preferred entry for this host via // the ip pooling rules. If so, connect to the preferred host instead of @@ -2536,10 +2581,22 @@ nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, void *param) if (preferredEntry) ent = preferredEntry; - if (mNumHalfOpenConns < gHttpHandler->ParallelSpeculativeConnectLimit() && - !ent->mIdleConns.Length() && !RestrictConnections(ent) && - !AtActiveConnectionLimit(ent, trans->Caps())) { - CreateTransport(ent, trans, trans->Caps(), true); + uint32_t parallelSpeculativeConnectLimit = + gHttpHandler->ParallelSpeculativeConnectLimit(); + bool ignorePossibleSpdyConnections = false; + bool ignoreIdle = false; + + if (args->mOverridesOK) { + parallelSpeculativeConnectLimit = args->mParallelSpeculativeConnectLimit; + ignorePossibleSpdyConnections = args->mIgnorePossibleSpdyConnections; + ignoreIdle = args->mIgnoreIdle; + } + + if (mNumHalfOpenConns < parallelSpeculativeConnectLimit && + (ignoreIdle || !ent->mIdleConns.Length()) && + !RestrictConnections(ent, ignorePossibleSpdyConnections) && + !AtActiveConnectionLimit(ent, args->mTrans->Caps())) { + CreateTransport(ent, args->mTrans, args->mTrans->Caps(), true); } else { LOG((" Transport not created due to existing connection count\n")); diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.h b/netwerk/protocol/http/nsHttpConnectionMgr.h index 9e539286d414..35fec1d29ca1 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.h +++ b/netwerk/protocol/http/nsHttpConnectionMgr.h @@ -516,7 +516,7 @@ private: nsresult BuildPipeline(nsConnectionEntry *, nsAHttpTransaction *, nsHttpPipeline **); - bool RestrictConnections(nsConnectionEntry *); + bool RestrictConnections(nsConnectionEntry *, bool = false); nsresult ProcessNewTransaction(nsHttpTransaction *); nsresult EnsureSocketThreadTarget(); void ClosePersistentConnections(nsConnectionEntry *ent); diff --git a/netwerk/test/unit/test_seer.js b/netwerk/test/unit/test_seer.js new file mode 100644 index 000000000000..48c672865006 --- /dev/null +++ b/netwerk/test/unit/test_seer.js @@ -0,0 +1,295 @@ +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; +var Cr = Components.results; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/FileUtils.jsm"); + +var seer = null; +var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); +var profile = null; + +function extract_origin(uri) { + var o = uri.scheme + "://" + uri.asciiHost; + if (uri.port !== -1) { + o = o + ":" + uri.port; + } + return o; +} + +var LoadContext = function _loadContext() { +}; + +LoadContext.prototype = { + usePrivateBrowsing: false, + + getInterface: function loadContext_getInterface(iid) { + return this.QueryInterface(iid); + }, + + QueryInterface: function loadContext_QueryInterface(iid) { + if (iid.equals(Ci.nsINetworkSeerVerifier) || + iid.equals(Ci.nsILoadContext)) { + return this; + } + + throw Cr.NS_ERROR_NO_INTERFACE; + } +}; + +var load_context = new LoadContext(); + +var Verifier = function _verifier(testing, expected_preconnects, expected_preresolves) { + this.verifying = testing; + this.expected_preconnects = expected_preconnects; + this.expected_preresolves = expected_preresolves; +}; + +Verifier.prototype = { + verifying: null, + expected_preconnects: null, + expected_preresolves: null, + + getInterface: function verifier_getInterface(iid) { + return this.QueryInterface(iid); + }, + + QueryInterface: function verifier_QueryInterface(iid) { + if (iid.equals(Ci.nsINetworkSeerVerifier) || + iid.equals(Ci.nsISupports)) { + return this; + } + + throw Cr.NS_ERROR_NO_INTERFACE; + }, + + maybe_run_next_test: function verifier_maybe_run_next_test() { + if (this.expected_preconnects.length === 0 && + this.expected_preresolves.length === 0) { + do_check_true(true, "Well this is unexpected..."); + run_next_test(); + } + }, + + onPredictPreconnect: function verifier_onPredictPreconnect(uri) { + var origin = extract_origin(uri); + var index = this.expected_preconnects.indexOf(origin); + if (index == -1) { + do_check_true(false, "Got preconnect for unexpected uri " + origin); + } else { + this.expected_preconnects.splice(index, 1); + } + this.maybe_run_next_test(); + }, + + onPredictDNS: function verifier_onPredictDNS(uri) { + var origin = extract_origin(uri); + var index = this.expected_preresolves.indexOf(origin); + if (index == -1) { + do_check_true(false, "Got preresolve for unexpected uri " + origin); + } else { + this.expected_preresolves.splice(index, 1); + } + this.maybe_run_next_test(); + } +}; + +function reset_seer() { + seer.reset(); +} + +function newURI(s) { + return ios.newURI(s, null, null); +} + +function test_link_hover() { + reset_seer(); + var uri = newURI("http://localhost:4444/foo/bar"); + var referrer = newURI("http://localhost:4444/foo"); + var preconns = ["http://localhost:4444"]; + + var verifier = new Verifier("hover", preconns, []); + seer.predict(uri, referrer, seer.PREDICT_LINK, load_context, verifier); +} + +function test_pageload() { + reset_seer(); + var toplevel = "http://localhost:4444/index.html"; + var subresources = [ + "http://localhost:4444/style.css", + "http://localhost:4443/jquery.js", + "http://localhost:4444/image.png" + ]; + + var tluri = newURI(toplevel); + seer.learn(tluri, null, seer.LEARN_LOAD_TOPLEVEL, load_context); + var preconns = []; + for (var i = 0; i < subresources.length; i++) { + var sruri = newURI(subresources[i]); + seer.learn(sruri, tluri, seer.LEARN_LOAD_SUBRESOURCE, load_context); + preconns.push(extract_origin(sruri)); + } + + var verifier = new Verifier("pageload", preconns, []); + seer.predict(tluri, null, seer.PREDICT_LOAD, load_context, verifier); +} + +function test_redirect() { + reset_seer(); + var initial = "http://localhost:4443/redirect"; + var target = "http://localhost:4444/index.html"; + var subresources = [ + "http://localhost:4444/style.css", + "http://localhost:4443/jquery.js", + "http://localhost:4444/image.png" + ]; + + var inituri = newURI(initial); + var targeturi = newURI(target); + seer.learn(inituri, null, seer.LEARN_LOAD_TOPLEVEL, load_context); + seer.learn(targeturi, inituri, seer.LEARN_LOAD_REDIRECT, load_context); + seer.learn(targeturi, null, seer.LEARN_LOAD_TOPLEVEL, load_context); + + var preconns = []; + preconns.push(extract_origin(targeturi)); + for (var i = 0; i < subresources.length; i++) { + var sruri = newURI(subresources[i]); + seer.learn(sruri, targeturi, seer.LEARN_LOAD_SUBRESOURCE, load_context); + preconns.push(extract_origin(sruri)); + } + + var verifier = new Verifier("redirect", preconns, []); + seer.predict(inituri, null, seer.PREDICT_LOAD, load_context, verifier); +} + +function test_startup() { + reset_seer(); + var uris = [ + "http://localhost:4444/startup", + "http://localhost:4443/startup" + ]; + var preconns = []; + for (var i = 0; i < uris.length; i++) { + var uri = newURI(uris[i]); + seer.learn(uri, null, seer.LEARN_STARTUP, load_context); + preconns.push(extract_origin(uri)); + } + + var verifier = new Verifier("startup", preconns, []); + seer.predict(null, null, seer.PREDICT_STARTUP, load_context, verifier); +} + +// A class used to guarantee serialization of SQL queries so we can properly +// update last hit times on subresources to ensure the seer tries to do DNS +// preresolve on them instead of preconnecting +var DnsContinueVerifier = function _dnsContinueVerifier(subresource, tluri, preresolves) { + this.subresource = subresource; + this.tluri = tluri; + this.preresolves = preresolves; +}; + +DnsContinueVerifier.prototype = { + subresource: null, + tluri: null, + preresolves: null, + + getInterface: function _dnsContinueVerifier_getInterface(iid) { + return this.QueryInterface(iid); + }, + + QueryInterface: function _dnsContinueVerifier_QueryInterface(iid) { + if (iid.equals(Ci.nsISupports) || + iid.equals(Ci.nsINetworkSeerVerifier)) { + return this; + } + + throw Cr.NS_ERROR_NO_INTERFACE; + }, + + onPredictPreconnect: function _dnsContinueVerifier_onPredictPreconnect() { + // This means that the seer has learned and done our "checkpoint" prediction + // Now we can get on with the prediction we actually want to test + + // tstamp is 10 days older than now - just over 1 week, which will ensure we + // hit our cutoff for dns vs. preconnect. This is all in usec, hence the + // x1000 on the Date object value. + var tstamp = (new Date().valueOf() * 1000) - (10 * 86400 * 1000000); + + var dbfile = FileUtils.getFile("ProfD", ["seer.sqlite"]); + var dbconn = Services.storage.openDatabase(dbfile); + // We also need to update hits, since the toplevel has been "loaded" a + // second time (from the prediction that kicked off this callback) to ensure + // that the seer will try to do anything for this subresource. + var stmt = "UPDATE moz_subresources SET last_hit = " + tstamp + ", hits = 2 WHERE uri = '" + this.subresource + "';"; + dbconn.executeSimpleSQL(stmt); + dbconn.close(); + + var verifier = new Verifier("dns", [], this.preresolves); + seer.predict(this.tluri, null, seer.PREDICT_LOAD, load_context, verifier); + }, + + onPredictDNS: function _dnsContinueVerifier_onPredictDNS() { + do_check_true(false, "Shouldn't have gotten a preresolve prediction here!"); + } +}; + +function test_dns() { + reset_seer(); + var toplevel = "http://localhost:4444/index.html"; + var subresource = "http://localhost:4443/jquery.js"; + + var tluri = newURI(toplevel); + seer.learn(tluri, null, seer.LEARN_LOAD_TOPLEVEL, load_context); + var sruri = newURI(subresource); + seer.learn(sruri, tluri, seer.LEARN_LOAD_SUBRESOURCE, load_context); + + var preresolves = [extract_origin(sruri)]; + var continue_verifier = new DnsContinueVerifier(subresource, tluri, preresolves); + // Fire off a prediction that will do preconnects so we know when the seer + // thread has gotten to the point where we can update the database manually + seer.predict(tluri, null, seer.PREDICT_LOAD, load_context, continue_verifier); +} + +function test_origin() { + reset_seer(); + var toplevel = "http://localhost:4444/index.html"; + var subresources = [ + "http://localhost:4444/style.css", + "http://localhost:4443/jquery.js", + "http://localhost:4444/image.png" + ]; + + var tluri = newURI(toplevel); + seer.learn(tluri, null, seer.LEARN_LOAD_TOPLEVEL, load_context); + var preconns = []; + for (var i = 0; i < subresources.length; i++) { + var sruri = newURI(subresources[i]); + seer.learn(sruri, tluri, seer.LEARN_LOAD_SUBRESOURCE, load_context); + var origin = extract_origin(sruri); + if (preconns.indexOf(origin) === -1) { + preconns.push(origin); + } + } + + var loaduri = newURI("http://localhost:4444/anotherpage.html"); + var verifier = new Verifier("origin", preconns, []); + seer.predict(loaduri, null, seer.PREDICT_LOAD, load_context, verifier); +} + +var tests = [ + test_link_hover, + test_pageload, + test_redirect, + test_startup, + test_dns, + test_origin +]; + +function run_test() { + tests.forEach(add_test); + profile = do_get_profile(); + seer = Cc["@mozilla.org/network/seer;1"].getService(Ci.nsINetworkSeer); + do_register_cleanup(reset_seer); + run_next_test(); +} diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 8d5d5bcdc3de..2bc9922a5e65 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -281,3 +281,4 @@ skip-if = os == "android" [test_addr_in_use_error.js] [test_about_networking.js] [test_ping_aboutnetworking.js] +[test_seer.js] diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 5013ef7e0746..4fdcf89d720a 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -1643,6 +1643,126 @@ "extended_statistics_ok": true, "description": "Time for an unsuccessful DNS OS resolution (msec)" }, + "SEER_PREDICT_ATTEMPTS": { + "kind": "exponential", + "high": "1000 * 1000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Number of times nsINetworkSeer::Predict is called and attempts to predict" + }, + "SEER_LEARN_ATTEMPTS": { + "kind": "exponential", + "high": "1000 * 1000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Number of times nsINetworkSeer::Learn is called and attempts to learn" + }, + "SEER_PREDICT_FULL_QUEUE": { + "kind": "exponential", + "high": "60000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Number of times nsINetworkSeer::Predict doesn't continue because the queue is full" + }, + "SEER_LEARN_FULL_QUEUE": { + "kind": "exponential", + "high": "60000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "Number of times nsINetworkSeer::Learn doesn't continue because the queue is full" + }, + "SEER_PREDICT_WAIT_TIME": { + "kind": "exponential", + "high": "3000", + "n_buckets": 10, + "extended_statistics_ok": true, + "description": "Amount of time a predict event waits in the queue (ms)" + }, + "SEER_PREDICT_WORK_TIME": { + "kind": "exponential", + "high": "3000", + "n_buckets": 10, + "extended_statistics_ok": true, + "description": "Amount of time spent doing the work for predict (ms)" + }, + "SEER_LEARN_WAIT_TIME": { + "kind": "exponential", + "high": "3000", + "n_buckets": 10, + "extended_statistics_ok": true, + "description": "Amount of time a learn event waits in the queue (ms)" + }, + "SEER_LEARN_WORK_TIME": { + "kind": "exponential", + "high": "3000", + "n_buckets": 10, + "extended_statistics_ok": true, + "description": "Amount of time spent doing the work for learn (ms)" + }, + "SEER_TOTAL_PREDICTIONS": { + "kind": "exponential", + "high": "1000 * 1000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "How many actual predictions (preresolves, preconnects, ...) happen" + }, + "SEER_TOTAL_PRECONNECTS": { + "kind": "exponential", + "high": "1000 * 1000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "How many actual preconnects happen" + }, + "SEER_TOTAL_PRERESOLVES": { + "kind": "exponential", + "high": "1000 * 1000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "How many actual preresolves happen" + }, + "SEER_PREDICTIONS_CALCULATED": { + "kind": "exponential", + "high": "1000 * 1000", + "n_buckets": 50, + "extended_statistics_ok": true, + "description": "How many prediction calculations are performed" + }, + "SEER_GLOBAL_DEGRADATION": { + "kind": "linear", + "high": "100", + "n_buckets": 50, + "description": "The global degradation calculated" + }, + "SEER_SUBRESOURCE_DEGRADATION": { + "kind": "linear", + "high": "100", + "n_buckets": 50, + "description": "The degradation calculated for a subresource" + }, + "SEER_BASE_CONFIDENCE": { + "kind": "linear", + "high": "100", + "n_buckets": 50, + "description": "The base confidence calculated for a subresource" + }, + "SEER_CONFIDENCE": { + "kind": "linear", + "high": "100", + "n_buckets": 50, + "description": "The final confidence calculated for a subresource" + }, + "SEER_PREDICT_TIME_TO_ACTION": { + "kind": "exponential", + "high": "3000", + "n_buckets": 10, + "description": "How long it takes from the time Predict() is called to the time we take action" + }, + "SEER_PREDICT_TIME_TO_INACTION": { + "kind": "exponential", + "high": "3000", + "n_buckets": 10, + "description": "How long it takes from the time Predict() is called to the time we figure out there's nothing to do" + }, "FIND_PLUGINS": { "kind": "exponential", "high": "3000", From 26ad4af46daacb339b75b37516325772ac1e553a Mon Sep 17 00:00:00 2001 From: Nicholas Hurley Date: Fri, 20 Sep 2013 15:24:06 -0700 Subject: [PATCH 03/24] Bug 881804 (part 2) - Plumb docshell into predictive network actions. r=smaug --- docshell/base/nsDocShell.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index d2845d5bd06c..05cd159db5b5 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -117,6 +117,7 @@ #include "nsIFaviconService.h" #include "mozIAsyncFavicons.h" #endif +#include "nsINetworkSeer.h" // Editor-related #include "nsIEditingSession.h" @@ -6977,9 +6978,12 @@ nsDocShell::EndPageLoad(nsIWebProgress * aProgress, aStatus = NS_ERROR_OFFLINE; DisplayLoadError(aStatus, url, nullptr, aChannel); } - } // if we have a host + } // if we have a host + else if (url && NS_SUCCEEDED(aStatus)) { + mozilla::net::SeerLearnRedirect(url, aChannel, this); + } - return NS_OK; + return NS_OK; } @@ -9378,6 +9382,9 @@ nsDocShell::InternalLoad(nsIURI * aURI, else srcdoc = NullString(); + mozilla::net::SeerPredict(aURI, nullptr, nsINetworkSeer::PREDICT_LOAD, + this, nullptr); + nsCOMPtr req; rv = DoURILoad(aURI, aReferrer, !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER), @@ -12459,6 +12466,9 @@ nsDocShell::OnOverLink(nsIContent* aContent, rv = textToSubURI->UnEscapeURIForUI(charset, spec, uStr); NS_ENSURE_SUCCESS(rv, rv); + mozilla::net::SeerPredict(aURI, mCurrentURI, nsINetworkSeer::PREDICT_LINK, + this, nullptr); + if (browserChrome2) { nsCOMPtr element = do_QueryInterface(aContent); rv = browserChrome2->SetStatusWithContext(nsIWebBrowserChrome::STATUS_LINK, From d520df637d19d5503425964d828cd9be58d6ab7e Mon Sep 17 00:00:00 2001 From: Nicholas Hurley Date: Fri, 20 Sep 2013 15:24:07 -0700 Subject: [PATCH 04/24] Bug 881804 (part 3) - Plumb script loader into predictive network actions. r=jst --- content/base/src/nsScriptLoader.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/content/base/src/nsScriptLoader.cpp b/content/base/src/nsScriptLoader.cpp index 9f4434968f5b..3b26af68f12e 100644 --- a/content/base/src/nsScriptLoader.cpp +++ b/content/base/src/nsScriptLoader.cpp @@ -47,6 +47,7 @@ #include "nsCrossSiteListenerProxy.h" #include "nsSandboxFlags.h" #include "nsContentTypeParser.h" +#include "nsINetworkSeer.h" #include "mozilla/CORSMode.h" #include "mozilla/Attributes.h" @@ -329,6 +330,10 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, httpChannel->SetReferrer(mDocument->GetDocumentURI()); } + nsCOMPtr loadContext(do_QueryInterface(docshell)); + mozilla::net::SeerLearn(aRequest->mURI, mDocument->GetDocumentURI(), + nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, loadContext); + nsCOMPtr loader; rv = NS_NewStreamLoader(getter_AddRefs(loader), this); NS_ENSURE_SUCCESS(rv, rv); From 4820e263a775745362ec4cf3a7b58790ba418b38 Mon Sep 17 00:00:00 2001 From: Nicholas Hurley Date: Fri, 20 Sep 2013 15:24:07 -0700 Subject: [PATCH 05/24] Bug 881804 (part 4) - Plumb layout into predictive network actions. r=bz --- layout/style/Loader.cpp | 12 ++++++++++++ layout/style/nsFontFaceLoader.cpp | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index 9e9a232e4fd4..d386f4a25af0 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -47,6 +47,7 @@ #include "nsGkAtoms.h" #include "nsIThreadInternal.h" #include "nsCrossSiteListenerProxy.h" +#include "nsINetworkSeer.h" #ifdef MOZ_XUL #include "nsXULPrototypeCache.h" @@ -1433,6 +1434,12 @@ Loader::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState) return rv; } + if (mDocument) { + mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(), + nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, + mDocument); + } + // Just load it nsCOMPtr stream; nsCOMPtr channel; @@ -1606,6 +1613,11 @@ Loader::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState) channelListener = streamLoader; } + if (mDocument) { + mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(), + nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, mDocument); + } + rv = channel->AsyncOpen(channelListener, nullptr); #ifdef DEBUG diff --git a/layout/style/nsFontFaceLoader.cpp b/layout/style/nsFontFaceLoader.cpp index 06ffa5e3e6f3..6cb23717e9e8 100644 --- a/layout/style/nsFontFaceLoader.cpp +++ b/layout/style/nsFontFaceLoader.cpp @@ -29,6 +29,7 @@ #include "nsIContentSecurityPolicy.h" #include "nsIDocShell.h" #include "nsIWebNavigation.h" +#include "nsINetworkSeer.h" #include "nsIConsoleService.h" @@ -374,6 +375,10 @@ nsUserFontSet::StartLoad(gfxMixedFontFamily *aFamily, rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader); NS_ENSURE_SUCCESS(rv, rv); + nsIDocument *document = ps->GetDocument(); + mozilla::net::SeerLearn(aFontFaceSrc->mURI, document->GetDocumentURI(), + nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, loadGroup); + bool inherits = false; rv = NS_URIChainHasFlags(aFontFaceSrc->mURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, From 10d8f1725cf6f0656c7cffe86782fe1ef0856de7 Mon Sep 17 00:00:00 2001 From: Nicholas Hurley Date: Fri, 20 Sep 2013 15:24:07 -0700 Subject: [PATCH 06/24] Bug 881804 (part 5) - Plumb image loader into predictive network actions. r=seth --- image/src/imgLoader.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/image/src/imgLoader.cpp b/image/src/imgLoader.cpp index 8c3e3abb4025..5137a51dd2e2 100644 --- a/image/src/imgLoader.cpp +++ b/image/src/imgLoader.cpp @@ -28,6 +28,7 @@ #include "nsIFileURL.h" #include "nsCRT.h" #include "nsIDocument.h" +#include "nsINetworkSeer.h" #include "nsIApplicationCache.h" #include "nsIApplicationCacheContainer.h" @@ -1243,6 +1244,9 @@ bool imgLoader::ValidateRequestWithNewChannel(imgRequest *request, // Add the proxy without notifying hvc->AddProxy(proxy); + mozilla::net::SeerLearn(aURI, aInitialDocumentURI, + nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, aLoadGroup); + rv = newChannel->AsyncOpen(listener, nullptr); if (NS_SUCCEEDED(rv)) NS_ADDREF(*aProxyRequest = req.get()); @@ -1719,6 +1723,9 @@ nsresult imgLoader::LoadImage(nsIURI *aURI, PR_LOG(GetImgLog(), PR_LOG_DEBUG, ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n", this)); + mozilla::net::SeerLearn(aURI, aInitialDocumentURI, + nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, aLoadGroup); + nsresult openRes = newChannel->AsyncOpen(listener, nullptr); if (NS_FAILED(openRes)) { From e82afeaa6dce4e20a78742b220f4b6bcad30572a Mon Sep 17 00:00:00 2001 From: Nicholas Hurley Date: Fri, 20 Sep 2013 15:24:07 -0700 Subject: [PATCH 07/24] Bug 881804 (part 6) - Plumb browser UI into predictive network actions to allow clearing data. r=ttaubert r=mossop f=gavin --- browser/base/content/sanitize.js | 6 ++++++ toolkit/forgetaboutsite/ForgetAboutSite.jsm | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/browser/base/content/sanitize.js b/browser/base/content/sanitize.js index 9906b6285fc3..aeb6087cb071 100644 --- a/browser/base/content/sanitize.js +++ b/browser/base/content/sanitize.js @@ -227,6 +227,12 @@ Sanitizer.prototype = { prefs.clearUserPref("general.open_location.last_url"); } catch (e) { } + + try { + var seer = Components.classes["@mozilla.org/network/seer;1"] + .getService(Components.interfaces.nsINetworkSeer); + seer.reset(); + } catch (e) { } }, get canClear() diff --git a/toolkit/forgetaboutsite/ForgetAboutSite.jsm b/toolkit/forgetaboutsite/ForgetAboutSite.jsm index f4e14e09e225..1be2fe0fd56f 100644 --- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm +++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm @@ -216,5 +216,11 @@ this.ForgetAboutSite = { handleCompletion: function() onContentPrefsRemovalFinished(), handleError: function() {} }); + + // Predictive network data - like cache, no way to clear this per + // domain, so just trash it all + let ns = Cc["@mozilla.org/network/seer;1"]. + getService(Ci.nsINetworkSeer); + ns.reset(); } }; From 5916036948090f8ad1a3880e0ec01adbcf69e81e Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Fri, 20 Sep 2013 15:46:23 -0700 Subject: [PATCH 08/24] Bug 794506 - Part 1: Move virtualenv code under mozbuild; r=ted --HG-- rename : build/virtualenv/packages.txt => build/virtualenv_packages.txt rename : build/virtualenv/populate_virtualenv.py => python/mozbuild/mozbuild/virtualenv.py --- build/autoconf/python-virtualenv.m4 | 5 +++-- .../packages.txt => virtualenv_packages.txt} | 0 client.mk | 4 ++-- js/src/build/autoconf/python-virtualenv.m4 | 5 +++-- .../mozbuild/mozbuild/virtualenv.py | 17 +++++------------ 5 files changed, 13 insertions(+), 18 deletions(-) rename build/{virtualenv/packages.txt => virtualenv_packages.txt} (100%) rename build/virtualenv/populate_virtualenv.py => python/mozbuild/mozbuild/virtualenv.py (97%) diff --git a/build/autoconf/python-virtualenv.m4 b/build/autoconf/python-virtualenv.m4 index ef8e476a8071..dbc8a21cf0da 100644 --- a/build/autoconf/python-virtualenv.m4 +++ b/build/autoconf/python-virtualenv.m4 @@ -24,7 +24,7 @@ dnl If this is a mozilla-central, we'll find the virtualenv in the top dnl source directory. If this is a SpiderMonkey build, we assume we're at dnl js/src and try to find the virtualenv from the mozilla-central root. for base in $MOZILLA_CENTRAL_PATH $_topsrcdir $_topsrcdir/../..; do - possible=$base/build/virtualenv/populate_virtualenv.py + possible=$base/python/mozbuild/mozbuild/virtualenv.py if test -e $possible; then _virtualenv_topsrcdir=$base @@ -53,7 +53,8 @@ if test -z $DONT_POPULATE_VIRTUALENV; then dnl virtualenv is present and up to date. It sanitizes the environment dnl for us, so we don't need to clean anything out. $PYTHON $_virtualenv_populate_path \ - $_virtualenv_topsrcdir $MOZ_BUILD_ROOT $MOZ_BUILD_ROOT/_virtualenv || exit 1 + $_virtualenv_topsrcdir $MOZ_BUILD_ROOT $MOZ_BUILD_ROOT/_virtualenv \ + $_virtualenv_topsrcdir/build/virtualenv_packages.txt || exit 1 case "$host_os" in mingw*) diff --git a/build/virtualenv/packages.txt b/build/virtualenv_packages.txt similarity index 100% rename from build/virtualenv/packages.txt rename to build/virtualenv_packages.txt diff --git a/client.mk b/client.mk index 4fe5b61d3271..3723b2ee3e71 100644 --- a/client.mk +++ b/client.mk @@ -325,8 +325,8 @@ CONFIG_STATUS_DEPS := \ $(TOPSRCDIR)/config/milestone.txt \ $(TOPSRCDIR)/js/src/config/milestone.txt \ $(TOPSRCDIR)/browser/config/version.txt \ - $(TOPSRCDIR)/build/virtualenv/packages.txt \ - $(TOPSRCDIR)/build/virtualenv/populate_virtualenv.py \ + $(TOPSRCDIR)/build/virtualenv_packages.txt \ + $(TOPSRCDIR)/python/mozbuild/mozbuild/virtualenv.py \ $(TOPSRCDIR)/testing/mozbase/packages.txt \ $(NULL) diff --git a/js/src/build/autoconf/python-virtualenv.m4 b/js/src/build/autoconf/python-virtualenv.m4 index ef8e476a8071..dbc8a21cf0da 100644 --- a/js/src/build/autoconf/python-virtualenv.m4 +++ b/js/src/build/autoconf/python-virtualenv.m4 @@ -24,7 +24,7 @@ dnl If this is a mozilla-central, we'll find the virtualenv in the top dnl source directory. If this is a SpiderMonkey build, we assume we're at dnl js/src and try to find the virtualenv from the mozilla-central root. for base in $MOZILLA_CENTRAL_PATH $_topsrcdir $_topsrcdir/../..; do - possible=$base/build/virtualenv/populate_virtualenv.py + possible=$base/python/mozbuild/mozbuild/virtualenv.py if test -e $possible; then _virtualenv_topsrcdir=$base @@ -53,7 +53,8 @@ if test -z $DONT_POPULATE_VIRTUALENV; then dnl virtualenv is present and up to date. It sanitizes the environment dnl for us, so we don't need to clean anything out. $PYTHON $_virtualenv_populate_path \ - $_virtualenv_topsrcdir $MOZ_BUILD_ROOT $MOZ_BUILD_ROOT/_virtualenv || exit 1 + $_virtualenv_topsrcdir $MOZ_BUILD_ROOT $MOZ_BUILD_ROOT/_virtualenv \ + $_virtualenv_topsrcdir/build/virtualenv_packages.txt || exit 1 case "$host_os" in mingw*) diff --git a/build/virtualenv/populate_virtualenv.py b/python/mozbuild/mozbuild/virtualenv.py similarity index 97% rename from build/virtualenv/populate_virtualenv.py rename to python/mozbuild/mozbuild/virtualenv.py index 8b0b124cbab6..d9c2a2850e21 100644 --- a/build/virtualenv/populate_virtualenv.py +++ b/python/mozbuild/mozbuild/virtualenv.py @@ -366,7 +366,7 @@ class VirtualenvManager(object): # the virtualenv for paths to be proper. args = [self.python_path, __file__, 'populate', self.topsrcdir, - self.topobjdir, self.virtualenv_root] + self.topobjdir, self.virtualenv_root, self.manifest_path] result = subprocess.call(args, stdout=self.log_handle, stderr=subprocess.STDOUT, cwd=self.topsrcdir) @@ -409,26 +409,19 @@ def verify_python_version(log_handle): if __name__ == '__main__': - if len(sys.argv) < 4: - print('Usage: populate_virtualenv.py /path/to/topsrcdir /path/to/topobjdir /path/to/virtualenv') + if len(sys.argv) < 5: + print('Usage: populate_virtualenv.py /path/to/topsrcdir /path/to/topobjdir /path/to/virtualenv /path/to/virtualenv_manifest') sys.exit(1) verify_python_version(sys.stdout) - topsrcdir = sys.argv[1] - topobjdir = sys.argv[2] - virtualenv_path = sys.argv[3] + topsrcdir, topobjdir, virtualenv_path, manifest_path = sys.argv[1:5] populate = False # This should only be called internally. if sys.argv[1] == 'populate': populate = True - topsrcdir = sys.argv[2] - topobjdir = sys.argv[3] - virtualenv_path = sys.argv[4] - - # path to default packages.txt - manifest_path = os.path.join(topsrcdir, 'build', 'virtualenv', 'packages.txt') + topsrcdir, topobjdir, virtualenv_path, manifest_path = sys.argv[2:] manager = VirtualenvManager(topsrcdir, topobjdir, virtualenv_path, sys.stdout, manifest_path) From 062461bdeef3e2e9995d7211b7cf25a4795fc5dc Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Fri, 20 Sep 2013 15:46:25 -0700 Subject: [PATCH 09/24] Bug 794506 - Part 2: Add virtualenv APIs; r=ted --- python/mozbuild/mozbuild/base.py | 17 ++++++++ python/mozbuild/mozbuild/virtualenv.py | 57 ++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/python/mozbuild/mozbuild/base.py b/python/mozbuild/mozbuild/base.py index 6047cdfe98be..44c182472c2b 100644 --- a/python/mozbuild/mozbuild/base.py +++ b/python/mozbuild/mozbuild/base.py @@ -25,6 +25,8 @@ from .mozconfig import ( MozconfigLoadException, MozconfigLoader, ) +from .virtualenv import VirtualenvManager + def ancestors(path): """Emit the parent directories of a path.""" @@ -86,6 +88,7 @@ class MozbuildObject(ProcessExecutionMixin): self._mozconfig = None self._config_guess_output = None self._config_environment = None + self._virtualenv_manager = None @classmethod def from_environment(cls, cwd=None, detect_virtualenv_mozinfo=True): @@ -206,6 +209,16 @@ class MozbuildObject(ProcessExecutionMixin): return self._topobjdir + @property + def virtualenv_manager(self): + if self._virtualenv_manager is None: + self._virtualenv_manager = VirtualenvManager(self.topsrcdir, + self.topobjdir, os.path.join(self.topobjdir, '_virtualenv'), + sys.stdout, os.path.join(self.topsrcdir, 'build', + 'virtualenv_packages.txt')) + + return self._virtualenv_manager + @property def mozconfig(self): """Returns information about the current mozconfig file. @@ -475,6 +488,10 @@ class MozbuildObject(ProcessExecutionMixin): return cls(self.topsrcdir, self.settings, self.log_manager, topobjdir=self.topobjdir) + def _activate_virtualenv(self): + self.virtualenv_manager.ensure() + self.virtualenv_manager.activate() + class MachCommandBase(MozbuildObject): """Base class for mach command providers that wish to be MozbuildObjects. diff --git a/python/mozbuild/mozbuild/virtualenv.py b/python/mozbuild/mozbuild/virtualenv.py index d9c2a2850e21..1cc0280ed15d 100644 --- a/python/mozbuild/mozbuild/virtualenv.py +++ b/python/mozbuild/mozbuild/virtualenv.py @@ -60,19 +60,27 @@ class VirtualenvManager(object): 'virtualenv.py') @property - def python_path(self): + def bin_path(self): + # virtualenv.py provides a similar API via path_locations(). However, + # we have a bit of a chicken-and-egg problem and can't reliably + # import virtualenv. The functionality is trivial, so just implement + # it here. if sys.platform in ('win32', 'cygwin'): - return os.path.join(self.virtualenv_root, 'Scripts', 'python.exe') + return os.path.join(self.virtualenv_root, 'Scripts') - return os.path.join(self.virtualenv_root, 'bin', 'python') + return os.path.join(self.virtualenv_root, 'bin') + + @property + def python_path(self): + binary = 'python' + if sys.platform in ('win32', 'cygwin'): + binary += '.exe' + + return os.path.join(self.bin_path, binary) @property def activate_path(self): - if sys.platform in ('win32', 'cygwin'): - return os.path.join(self.virtualenv_root, 'Scripts', - 'activate_this.py') - - return os.path.join(self.virtualenv_root, 'bin', 'activate_this.py') + return os.path.join(self.bin_path, 'activate_this.py') def up_to_date(self): """Returns whether the virtualenv is present and up to date.""" @@ -388,6 +396,39 @@ class VirtualenvManager(object): execfile(self.activate_path, dict(__file__=self.activate_path)) + def install_pip_package(self, package): + """Install a package via pip. + + The supplied package is specified using a pip requirement specifier. + e.g. 'foo' or 'foo==1.0'. + + If the package is already installed, this is a no-op. + """ + from pip.req import InstallRequirement + + req = InstallRequirement.from_line(package) + if req.check_if_exists(): + return + + args = [ + 'install', + '--use-wheel', + package, + ] + + return self._run_pip(args) + + def _run_pip(self, args): + # It's tempting to call pip natively via pip.main(). However, + # the current Python interpreter may not be the virtualenv python. + # This will confuse pip and cause the package to attempt to install + # against the executing interpreter. By creating a new process, we + # force the virtualenv's interpreter to be used and all is well. + # It /might/ be possible to cheat and set sys.executable to + # self.python_path. However, this seems more risk than it's worth. + subprocess.check_call([os.path.join(self.bin_path, 'pip')] + args, + stderr=subprocess.STDOUT) + def verify_python_version(log_handle): """Ensure the current version of Python is sufficient.""" From 60010fa7c300d608fccf672020ec5a0e3bc9add8 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Fri, 20 Sep 2013 15:46:43 -0700 Subject: [PATCH 10/24] Bug 917988 - Sphinx managed build system documentation; add mach build-docs; r=bsmedberg --HG-- rename : python/mozbuild/dumbmake/README.rst => build/docs/mozbuild/dumbmake.rst rename : python/mozbuild/mozbuild/frontend/README.rst => build/docs/mozbuild/frontend.rst rename : python/mozbuild/README.rst => build/docs/mozbuild/index.rst --- build/docs/conf.py | 49 +++++++++++++ build/docs/glossary.rst | 28 ++++++++ build/docs/index.rst | 41 +++++++++++ .../docs/mozbuild/dumbmake.rst | 0 .../docs/mozbuild/frontend.rst | 0 .../docs/mozbuild/index.rst | 0 build/docs/mozconfigs.rst | 68 +++++++++++++++++++ build/docs/pgo.rst | 40 +++++++++++ python/mozbuild/mozbuild/mach_commands.py | 34 ++++++++++ 9 files changed, 260 insertions(+) create mode 100644 build/docs/conf.py create mode 100644 build/docs/glossary.rst create mode 100644 build/docs/index.rst rename python/mozbuild/dumbmake/README.rst => build/docs/mozbuild/dumbmake.rst (100%) rename python/mozbuild/mozbuild/frontend/README.rst => build/docs/mozbuild/frontend.rst (100%) rename python/mozbuild/README.rst => build/docs/mozbuild/index.rst (100%) create mode 100644 build/docs/mozconfigs.rst create mode 100644 build/docs/pgo.rst diff --git a/build/docs/conf.py b/build/docs/conf.py new file mode 100644 index 000000000000..c33b8c5a798c --- /dev/null +++ b/build/docs/conf.py @@ -0,0 +1,49 @@ +# 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/. + +from __future__ import unicode_literals + +import os +import re + +from datetime import datetime + + +here = os.path.abspath(os.path.dirname(__file__)) +mozilla_dir = os.path.normpath(os.path.join(here, '..', '..')) + +import mdn_theme + +extensions = [ + 'sphinx.ext.autodoc', +] + +templates_path = ['_templates'] +source_suffix = '.rst' +master_doc = 'index' +project = u'Mozilla Build System' +year = datetime.now().year +copyright = u'%s, Mozilla Foundation, CC BY-SA 3.0' % year + +# Grab the version from the source tree's milestone. +with open(os.path.join(mozilla_dir, 'config', 'milestone.txt'), 'rt') as fh: + for line in fh: + line = line.strip() + + if not line or line.startswith('#'): + continue + + release = line + break + +version = re.sub(r'[ab]\d+$', '', release) + +exclude_patterns = ['_build'] +pygments_style = 'sphinx' + +html_theme_path = [mdn_theme.get_theme_dir()] +html_theme = 'mdn' + +html_static_path = ['_static'] +htmlhelp_basename = 'MozillaBuildSystemdoc' diff --git a/build/docs/glossary.rst b/build/docs/glossary.rst new file mode 100644 index 000000000000..91534e653238 --- /dev/null +++ b/build/docs/glossary.rst @@ -0,0 +1,28 @@ +======== +Glossary +======== + +.. glossary:: + :sorted: + + object directory + A directory holding the output of the build system. The build + system attempts to isolate all file modifications to this + directory. By convention, object directories are commonly + directories under the source directory prefixed with **obj-**. + e.g. **obj-firefox**. + + mozconfig + A shell script used to configure the build system. + + configure + A generated shell script which detects the current system + environment, applies a requested set of build configuration + options, and writes out metadata to be consumed by the build + system. + + config.status + An executable file produced by **configure** that takes the + generated build config and writes out files used to build the + tree. Traditionally, config.status writes out a bunch of + Makefiles. diff --git a/build/docs/index.rst b/build/docs/index.rst new file mode 100644 index 000000000000..06c8fc0d5c70 --- /dev/null +++ b/build/docs/index.rst @@ -0,0 +1,41 @@ +================================== +Mozilla Build System Documentation +================================== + +Overview +======== + +.. toctree:: + :maxdepth: 1 + + glossary + +Important Concepts +================== +.. toctree:: + :maxdepth: 1 + + Mozconfig Files + Profile Guided Optimization + +mozbuild +======== + +mozbuild is a Python package containing a lot of the code for the +Mozilla build system. + +.. toctree:: + :maxdepth: 1 + + mozbuild/index + mozbuild/frontend + mozbuild/dumbmake + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/python/mozbuild/dumbmake/README.rst b/build/docs/mozbuild/dumbmake.rst similarity index 100% rename from python/mozbuild/dumbmake/README.rst rename to build/docs/mozbuild/dumbmake.rst diff --git a/python/mozbuild/mozbuild/frontend/README.rst b/build/docs/mozbuild/frontend.rst similarity index 100% rename from python/mozbuild/mozbuild/frontend/README.rst rename to build/docs/mozbuild/frontend.rst diff --git a/python/mozbuild/README.rst b/build/docs/mozbuild/index.rst similarity index 100% rename from python/mozbuild/README.rst rename to build/docs/mozbuild/index.rst diff --git a/build/docs/mozconfigs.rst b/build/docs/mozconfigs.rst new file mode 100644 index 000000000000..cb473c532c98 --- /dev/null +++ b/build/docs/mozconfigs.rst @@ -0,0 +1,68 @@ +=============== +mozconfig Files +=============== + +mozconfig files are used to configure how a build works. + +mozconfig files are actually shell scripts. They are executed in a +special context with specific variables and functions exposed to them. + +API +=== + +Functions +--------- + +The following special functions are available to a mozconfig script. + +ac_add_options +^^^^^^^^^^^^^^ + +This function is used to declare extra options/arguments to pass into +configure. + +e.g.:: + + ac_add_options --disable-tests + ac_add_options --enable-optimize + +mk_add_options +^^^^^^^^^^^^^^ + +This function is used to inject statements into client.mk for execution. +It is typically used to define variables, notably the object directory. + +e.g.:: + + mk_add_options AUTOCLOBBER=1 + +ac_add_options +^^^^^^^^^^^^^^ + +This is a variant of ac_add_options() which only adds configure options +for a specified application. This is only used when building multiple +applications through client.mk. This function is typically not needed. + +Special mk_add_options Variables +-------------------------------- + +For historical reasons, the method for communicating certain +well-defined variables is via mk_add_options(). In this section, we +document what those special variables are. + +MOZ_OBJDIR +^^^^^^^^^^ + +This variable is used to define the :term:`object directory` for the current +build. + +Finding the active mozconfig +============================ + +Multiple mozconfig files can exist to provide different configuration +options for different tasks. The rules for finding the active mozconfig +are defined in the +:py:func:`mozbuild.mozconfig.MozconfigLoader.find_mozconfig` method: + +.. autoclass:: mozbuild.mozconfig.MozconfigLoader + :members: find_mozconfig diff --git a/build/docs/pgo.rst b/build/docs/pgo.rst new file mode 100644 index 000000000000..51d03c625411 --- /dev/null +++ b/build/docs/pgo.rst @@ -0,0 +1,40 @@ +.. _pgo: + +=========================== +Profile Guided Optimization +=========================== + +:abbr:`PGO (Profile Guided Optimization)` is the process of adding +probes to a compiled binary, running said binary, then using the +run-time information to *recompile* the binary to (hopefully) make it +faster. + +How PGO Builds Work +=================== + +The supported interface for invoking a PGO build is to evaluate the +*build* target of client.mk with *MOZ_PGO* defined. e.g.:: + + $ make -f client.mk MOZ_PGO=1 + +This is equivalent to:: + + $ make -f client.mk profiledbuild + +Which is roughly equivalent to: + +#. Perform a build with *MOZ_PROFILE_GENERATE=1* and *MOZ_PGO_INSTRUMENTED=1* +#. Package with *MOZ_PGO_INSTRUMENTED=1* +#. Performing a run of the instrumented binaries +#. $ make maybe_clobber_profiledbuild +#. Perform a build with *MOZ_PROFILE_USE=1* + +Differences between toolchains +============================== + +There are some implementation differences depending on the compiler +toolchain being used. + +The *maybe_clobber_profiledbuild* step gets its name because of a +difference. On Windows, this step merely moves some *.pgc* files around. +Using GCC or Clang, it is equivalent to a *make clean*. diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py index 7db779763b00..028494e8088f 100644 --- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -913,3 +913,37 @@ class MachDebug(object): print('config defines:') for k in sorted(config.defines): print('\t%s' % k) + + +@CommandProvider +class Documentation(MachCommandBase): + """Helps manage in-tree documentation.""" + + @Command('build-docs', category='build-dev', + description='Generate documentation for the tree.') + @CommandArgument('--format', default='html', + help='Documentation format to write.') + @CommandArgument('outdir', default='', nargs='?', + help='Where to write output.') + def build_docs(self, format=None, outdir=None): + self._activate_virtualenv() + + self.virtualenv_manager.install_pip_package('mdn-sphinx-theme==0.3') + + import sphinx + + if outdir == '': + outdir = os.path.join(self.topobjdir, 'docs', format) + + args = [ + sys.argv[0], + '-b', format, + os.path.join(self.topsrcdir, 'build', 'docs'), + outdir, + ] + + result = sphinx.main(args) + + print('Docs written to %s.' % outdir) + + return result From 02cf71a3b2e718547c66c7139cb3a948dff07c6d Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Fri, 20 Sep 2013 16:07:13 -0700 Subject: [PATCH 11/24] Backed out changeset 76f9cced8105 (bug 902715) for build bustage on at least Windows on a CLOSED TREE --- dom/base/nsGlobalWindow.cpp | 4 ---- dom/ipc/PBrowser.ipdl | 6 ------ dom/ipc/TabParent.cpp | 22 ---------------------- dom/ipc/TabParent.h | 1 - 4 files changed, 33 deletions(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 326a8551340f..8e70a8fdaf14 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -34,7 +34,6 @@ #include "nsIPermissionManager.h" #include "nsIScriptContext.h" #include "nsIScriptTimeoutHandler.h" -#include "mozilla/dom/TabChild.h" #ifdef XP_WIN // Thanks so much, Microsoft! :( @@ -5712,9 +5711,6 @@ nsGlobalWindow::Focus() return fm->SetFocus(frameElement, flags); } } - else if (TabChild *child = TabChild::GetFrom(this)) { - child->SendRequestFocus(canFocus); - } else if (canFocus) { // if there is no parent, this must be a toplevel window, so raise the // window if canFocus is true diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 266b0543db57..9fde7924c99e 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -163,12 +163,6 @@ parent: */ sync EndIMEComposition(bool cancel) returns (nsString composition); - /** - * Request that the parent process move focus to the browser's frame. If - * canRaise is true, the window can be raised if it is inactive. - */ - RequestFocus(bool canRaise); - sync GetInputContext() returns (int32_t IMEEnabled, int32_t IMEOpen, intptr_t NativeIMEContext); diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index d59f241a2a1e..a26fd31754c9 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -897,28 +897,6 @@ TabParent::RecvNotifyIMETextHint(const nsString& aText) return true; } -bool -TabParent::RecvRequestFocus(const bool& aCanFocus) -{ - nsCOMPtr fm = nsFocusManager::GetFocusManager(); - if (!fm) { - return true; - } - - nsCOMPtr content = do_QueryInterface(mFrameElement); - if (!content || !content->OwnerDoc()) { - return true; - } - - uint32_t flags = nsIFocusManager::FLAG_NOSCROLL; - if (aCanFocus) - flags |= nsIFocusManager::FLAG_RAISE; - - nsCOMPtr node = do_QueryInterface(mFrameElement); - fm->SetFocus(node, flags); - return true; -} - /** * Try to answer query event using cached text. * diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index d8efbc679daa..bc8f8cd11d11 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -149,7 +149,6 @@ public: const nsString& aActionHint, const int32_t& aCause, const int32_t& aFocusChange); - virtual bool RecvRequestFocus(const bool& aCanFocus); virtual bool RecvSetCursor(const uint32_t& aValue); virtual bool RecvSetBackgroundColor(const nscolor& aValue); virtual bool RecvSetStatus(const uint32_t& aType, const nsString& aStatus); From 2048df0a7fcbf94134d3d62046231756aba7556b Mon Sep 17 00:00:00 2001 From: Steven Michaud Date: Fri, 20 Sep 2013 23:01:12 -0500 Subject: [PATCH 12/24] Bug 917526 - Various duplicate symbol errors building tree with the 10.9 SDK. r=glandium --- configure.in | 7 ++++++- js/src/configure.in | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 81b764200214..f982cd73f40a 100644 --- a/configure.in +++ b/configure.in @@ -1235,7 +1235,12 @@ dnl ======================================================== if test "$GNU_CC"; then # Per bug 719659 comment 2, some of the headers on ancient build machines # may require gnu89 inline semantics. But otherwise, we use C99. - CFLAGS="$CFLAGS -std=gnu99 -fgnu89-inline" + # But on OS X we just use C99 plus GNU extensions, in order to fix + # bug 917526. + CFLAGS="$CFLAGS -std=gnu99" + if test "${OS_ARCH}" != Darwin; then + CFLAGS="$CFLAGS -fgnu89-inline" + fi # FIXME: Let us build with strict aliasing. bug 414641. CFLAGS="$CFLAGS -fno-strict-aliasing" MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$(notdir $@) -o $@' diff --git a/js/src/configure.in b/js/src/configure.in index eaa58c2e25d6..b67be2d0e849 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -1061,7 +1061,12 @@ dnl ======================================================== if test "$GNU_CC"; then # Per bug 719659 comment 2, some of the headers on ancient build machines # may require gnu89 inline semantics. But otherwise, we use C99. - CFLAGS="$CFLAGS -std=gnu99 -fgnu89-inline" + # But on OS X we just use C99 plus GNU extensions, in order to fix + # bug 917526. + CFLAGS="$CFLAGS -std=gnu99" + if test "${OS_ARCH}" != Darwin; then + CFLAGS="$CFLAGS -fgnu89-inline" + fi MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$(notdir $@) -o $@' MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$(notdir $@) -o $@' DSO_LDOPTS='-shared' From 624ad009ad95211b64f4d80b669622618c950135 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Fri, 20 Sep 2013 21:44:46 -0700 Subject: [PATCH 13/24] Bug 916845: Use size_t to describe length of source code in SpiderMonkey SourceHook lazy source hook. r=jorendorff --- js/src/jsfriendapi.h | 2 +- js/src/jsscript.cpp | 4 ++-- js/src/jsscript.h | 2 +- js/xpconnect/src/XPCJSRuntime.cpp | 8 ++++++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index d2f86285d71a..cec59b225969 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -204,7 +204,7 @@ extern JS_FRIEND_API(bool) JS_DefineFunctionsWithHelp(JSContext *cx, JSObject *obj, const JSFunctionSpecWithHelp *fs); typedef bool (* JS_SourceHook)(JSContext *cx, const char *filename, - jschar **src, uint32_t *length); + jschar **src, size_t *length); extern JS_FRIEND_API(void) JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook); diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index fffcf0d2f838..a74d609a24f1 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -998,7 +998,7 @@ JSScript::loadSource(JSContext *cx, ScriptSource *ss, bool *worked) if (!cx->runtime()->sourceHook || !ss->sourceRetrievable()) return true; jschar *src = NULL; - uint32_t length; + size_t length; if (!cx->runtime()->sourceHook(cx, ss->filename(), &src, &length)) return false; if (!src) @@ -1124,7 +1124,7 @@ ScriptSource::setSourceCopy(ExclusiveContext *cx, const jschar *src, uint32_t le } void -ScriptSource::setSource(const jschar *src, uint32_t length) +ScriptSource::setSource(const jschar *src, size_t length) { JS_ASSERT(!hasSourceData()); length_ = length; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 6c98e4685316..c93c14f8e867 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -341,7 +341,7 @@ class ScriptSource uint32_t length, bool argumentsNotIncluded, SourceCompressionTask *tok); - void setSource(const jschar *src, uint32_t length); + void setSource(const jschar *src, size_t length); bool ready() const { return ready_; } void setSourceRetrievable() { sourceRetrievable_ = true; } bool sourceRetrievable() const { return sourceRetrievable_; } diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index f6d0575577c3..e098bab3576f 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2774,7 +2774,7 @@ PreserveWrapper(JSContext *cx, JSObject *obj) } static nsresult -ReadSourceFromFilename(JSContext *cx, const char *filename, jschar **src, uint32_t *len) +ReadSourceFromFilename(JSContext *cx, const char *filename, jschar **src, size_t *len) { nsresult rv; @@ -2812,6 +2812,10 @@ ReadSourceFromFilename(JSContext *cx, const char *filename, jschar **src, uint32 NS_ENSURE_SUCCESS(rv, rv); if (!rawLen) return NS_ERROR_FAILURE; + + // Technically, this should be SIZE_MAX, but we don't run on machines + // where that would be less than UINT32_MAX, and the latter is already + // well beyond a reasonable limit. if (rawLen > UINT32_MAX) return NS_ERROR_FILE_TOO_BIG; @@ -2849,7 +2853,7 @@ ReadSourceFromFilename(JSContext *cx, const char *filename, jschar **src, uint32 function. See the comment in the XPCJSRuntime constructor. */ static bool -SourceHook(JSContext *cx, const char *filename, jschar **src, uint32_t *length) +SourceHook(JSContext *cx, const char *filename, jschar **src, size_t *length) { *src = NULL; *length = 0; From d61e731b853986274037ac126ce1f43848ee5253 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Fri, 20 Sep 2013 21:44:46 -0700 Subject: [PATCH 14/24] Bug 916845: Change sourceHook to a nice C++ object with a destructor. r=jorendorff --- js/src/jsfriendapi.cpp | 2 +- js/src/jsfriendapi.h | 32 +++++++++++++++++++----- js/src/jsscript.cpp | 2 +- js/src/vm/Runtime.cpp | 4 ++- js/src/vm/Runtime.h | 3 ++- js/xpconnect/src/XPCJSRuntime.cpp | 41 +++++++++++++++---------------- 6 files changed, 53 insertions(+), 31 deletions(-) diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 88a050a1cb85..43c26cdef648 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -46,7 +46,7 @@ PerThreadDataFriendFields::PerThreadDataFriendFields() } JS_FRIEND_API(void) -JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook) +js::SetSourceHook(JSRuntime *rt, SourceHook *hook) { rt->sourceHook = hook; } diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index cec59b225969..d9367a72319b 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -203,14 +203,34 @@ struct JSFunctionSpecWithHelp { extern JS_FRIEND_API(bool) JS_DefineFunctionsWithHelp(JSContext *cx, JSObject *obj, const JSFunctionSpecWithHelp *fs); -typedef bool (* JS_SourceHook)(JSContext *cx, const char *filename, - jschar **src, size_t *length); - -extern JS_FRIEND_API(void) -JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook); - namespace js { +/* + * A class of objects that return source code on demand. + * + * When code is compiled with CompileOptions::LAZY_SOURCE, SpiderMonkey + * doesn't retain the source code (and doesn't do lazy bytecode + * generation). If we ever need the source code, say, in response to a call + * to Function.prototype.toSource or Debugger.Source.prototype.text, then + * we call the 'load' member function of the instance of this class that + * has hopefully been registered with the runtime, passing the code's URL, + * and hope that it will be able to find the source. + */ +class SourceHook { + public: + virtual ~SourceHook() { } + + /* Set |*src| and |*length| to refer to the source code for |filename|. */ + virtual bool load(JSContext *cx, const char *filename, jschar **src, size_t *length) = 0; +}; + +/* + * Have |rt| use |hook| to retrieve LAZY_SOURCE source code. + * See the comments for SourceHook. + */ +extern JS_FRIEND_API(void) +SetSourceHook(JSRuntime *rt, SourceHook *hook); + inline JSRuntime * GetRuntime(const JSContext *cx) { diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index a74d609a24f1..150bc782078d 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -999,7 +999,7 @@ JSScript::loadSource(JSContext *cx, ScriptSource *ss, bool *worked) return true; jschar *src = NULL; size_t length; - if (!cx->runtime()->sourceHook(cx, ss->filename(), &src, &length)) + if (!cx->runtime()->sourceHook->load(cx, ss->filename(), &src, &length)) return false; if (!src) return true; diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 19cf9c9fcd54..898900f34cb3 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -234,7 +234,6 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads) negativeInfinityValue(DoubleValue(NegativeInfinity())), positiveInfinityValue(DoubleValue(PositiveInfinity())), emptyString(NULL), - sourceHook(NULL), debugMode(false), spsProfiler(thisFromCtor()), profilingScripts(false), @@ -399,6 +398,9 @@ JSRuntime::~JSRuntime() { JS_ASSERT(!isHeapBusy()); + /* Free source hook early, as its destructor may want to delete roots. */ + sourceHook = NULL; + /* Off thread compilation and parsing depend on atoms still existing. */ for (CompartmentsIter comp(this); !comp.done(); comp.next()) CancelOffThreadIonCompile(comp, NULL); diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index bacb4525cf80..d9721fd259ed 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -11,6 +11,7 @@ #include "mozilla/LinkedList.h" #include "mozilla/MemoryReporting.h" #include "mozilla/PodOperations.h" +#include "mozilla/Scoped.h" #include "mozilla/ThreadLocal.h" #include @@ -1275,7 +1276,7 @@ struct JSRuntime : public JS::shadow::Runtime, return !contextList.isEmpty(); } - JS_SourceHook sourceHook; + mozilla::ScopedDeletePtr sourceHook; /* Per runtime debug hooks -- see js/OldDebugAPI.h. */ JSDebugHooks debugHooks; diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index e098bab3576f..cc5c5892861d 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2848,30 +2848,29 @@ ReadSourceFromFilename(JSContext *cx, const char *filename, jschar **src, size_t return NS_OK; } -/* - The JS engine calls this function when it needs the source for a chrome JS - function. See the comment in the XPCJSRuntime constructor. -*/ -static bool -SourceHook(JSContext *cx, const char *filename, jschar **src, size_t *length) -{ - *src = NULL; - *length = 0; +// The JS engine calls this object's 'load' member function when it needs +// the source for a chrome JS function. See the comment in the XPCJSRuntime +// constructor. +class XPCJSSourceHook: public js::SourceHook { + bool load(JSContext *cx, const char *filename, jschar **src, size_t *length) { + *src = NULL; + *length = 0; - if (!nsContentUtils::IsCallerChrome()) - return true; + if (!nsContentUtils::IsCallerChrome()) + return true; - if (!filename) - return true; + if (!filename) + return true; - nsresult rv = ReadSourceFromFilename(cx, filename, src, length); - if (NS_FAILED(rv)) { - xpc::Throw(cx, rv); - return false; - } + nsresult rv = ReadSourceFromFilename(cx, filename, src, length); + if (NS_FAILED(rv)) { + xpc::Throw(cx, rv); + return false; + } - return true; -} + return true; + } +}; XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) : CycleCollectedJSRuntime(32L * 1024L * 1024L, JS_USE_HELPER_THREADS), @@ -3040,7 +3039,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) // compileAndGo mode and compiled function bodies (from // JS_CompileFunction*). In practice, this means content scripts and event // handlers. - JS_SetSourceHook(runtime, SourceHook); + js::SetSourceHook(runtime, new XPCJSSourceHook); // Set up locale information and callbacks for the newly-created runtime so // that the various toLocaleString() methods, localeCompare(), and other From f9b19015e2ec6838a9bf047cc18e82336a983280 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Fri, 20 Sep 2013 21:49:42 -0700 Subject: [PATCH 15/24] Bug 916845: Reindent ReadSourceFromFilename to match the surrounding style. r=bz --- js/xpconnect/src/XPCJSRuntime.cpp | 130 +++++++++++++++--------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index cc5c5892861d..967af11362ee 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2776,76 +2776,76 @@ PreserveWrapper(JSContext *cx, JSObject *obj) static nsresult ReadSourceFromFilename(JSContext *cx, const char *filename, jschar **src, size_t *len) { - nsresult rv; + nsresult rv; - // mozJSSubScriptLoader prefixes the filenames of the scripts it loads with - // the filename of its caller. Axe that if present. - const char *arrow; - while ((arrow = strstr(filename, " -> "))) - filename = arrow + strlen(" -> "); + // mozJSSubScriptLoader prefixes the filenames of the scripts it loads with + // the filename of its caller. Axe that if present. + const char *arrow; + while ((arrow = strstr(filename, " -> "))) + filename = arrow + strlen(" -> "); - // Get the URI. - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), filename); - NS_ENSURE_SUCCESS(rv, rv); + // Get the URI. + nsCOMPtr uri; + rv = NS_NewURI(getter_AddRefs(uri), filename); + NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr scriptChannel; - rv = NS_NewChannel(getter_AddRefs(scriptChannel), uri); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr scriptChannel; + rv = NS_NewChannel(getter_AddRefs(scriptChannel), uri); + NS_ENSURE_SUCCESS(rv, rv); + + // Only allow local reading. + nsCOMPtr actualUri; + rv = scriptChannel->GetURI(getter_AddRefs(actualUri)); + NS_ENSURE_SUCCESS(rv, rv); + nsCString scheme; + rv = actualUri->GetScheme(scheme); + NS_ENSURE_SUCCESS(rv, rv); + if (!scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar")) + return NS_OK; + + nsCOMPtr scriptStream; + rv = scriptChannel->Open(getter_AddRefs(scriptStream)); + NS_ENSURE_SUCCESS(rv, rv); + + uint64_t rawLen; + rv = scriptStream->Available(&rawLen); + NS_ENSURE_SUCCESS(rv, rv); + if (!rawLen) + return NS_ERROR_FAILURE; + + // Technically, this should be SIZE_MAX, but we don't run on machines + // where that would be less than UINT32_MAX, and the latter is already + // well beyond a reasonable limit. + if (rawLen > UINT32_MAX) + return NS_ERROR_FILE_TOO_BIG; + + // Allocate an internal buf the size of the file. + nsAutoArrayPtr buf(new unsigned char[rawLen]); + if (!buf) + return NS_ERROR_OUT_OF_MEMORY; + + unsigned char *ptr = buf, *end = ptr + rawLen; + while (ptr < end) { + uint32_t bytesRead; + rv = scriptStream->Read(reinterpret_cast(ptr), end - ptr, &bytesRead); + if (NS_FAILED(rv)) + return rv; + MOZ_ASSERT(bytesRead > 0, "stream promised more bytes before EOF"); + ptr += bytesRead; + } + + nsString decoded; + rv = nsScriptLoader::ConvertToUTF16(scriptChannel, buf, rawLen, EmptyString(), NULL, decoded); + NS_ENSURE_SUCCESS(rv, rv); + + // Copy to JS engine. + *len = decoded.Length(); + *src = static_cast(JS_malloc(cx, decoded.Length()*sizeof(jschar))); + if (!*src) + return NS_ERROR_FAILURE; + memcpy(*src, decoded.get(), decoded.Length()*sizeof(jschar)); - // Only allow local reading. - nsCOMPtr actualUri; - rv = scriptChannel->GetURI(getter_AddRefs(actualUri)); - NS_ENSURE_SUCCESS(rv, rv); - nsCString scheme; - rv = actualUri->GetScheme(scheme); - NS_ENSURE_SUCCESS(rv, rv); - if (!scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar")) return NS_OK; - - nsCOMPtr scriptStream; - rv = scriptChannel->Open(getter_AddRefs(scriptStream)); - NS_ENSURE_SUCCESS(rv, rv); - - uint64_t rawLen; - rv = scriptStream->Available(&rawLen); - NS_ENSURE_SUCCESS(rv, rv); - if (!rawLen) - return NS_ERROR_FAILURE; - - // Technically, this should be SIZE_MAX, but we don't run on machines - // where that would be less than UINT32_MAX, and the latter is already - // well beyond a reasonable limit. - if (rawLen > UINT32_MAX) - return NS_ERROR_FILE_TOO_BIG; - - // Allocate an internal buf the size of the file. - nsAutoArrayPtr buf(new unsigned char[rawLen]); - if (!buf) - return NS_ERROR_OUT_OF_MEMORY; - - unsigned char *ptr = buf, *end = ptr + rawLen; - while (ptr < end) { - uint32_t bytesRead; - rv = scriptStream->Read(reinterpret_cast(ptr), end - ptr, &bytesRead); - if (NS_FAILED(rv)) - return rv; - MOZ_ASSERT(bytesRead > 0, "stream promised more bytes before EOF"); - ptr += bytesRead; - } - - nsString decoded; - rv = nsScriptLoader::ConvertToUTF16(scriptChannel, buf, rawLen, EmptyString(), NULL, decoded); - NS_ENSURE_SUCCESS(rv, rv); - - // Copy to JS engine. - *len = decoded.Length(); - *src = static_cast(JS_malloc(cx, decoded.Length()*sizeof(jschar))); - if (!*src) - return NS_ERROR_FAILURE; - memcpy(*src, decoded.get(), decoded.Length()*sizeof(jschar)); - - return NS_OK; } // The JS engine calls this object's 'load' member function when it needs From 7e552c692667f5c122a8d270bdfba031a1d7b2ca Mon Sep 17 00:00:00 2001 From: "Nicholas D. Matsakis" Date: Wed, 21 Aug 2013 13:35:30 -0400 Subject: [PATCH 16/24] Bug 898349 - JIT support for getting and setting scalar properties and for optimizing away intermediate typed objects r=jandem --- js/src/builtin/TypeRepresentation.cpp | 12 +- js/src/builtin/TypeRepresentation.h | 6 +- js/src/builtin/TypedObject.cpp | 7 + js/src/builtin/TypedObject.h | 4 + .../jit-test/tests/TypedObject/jit-complex.js | 30 ++ .../jit-test/tests/TypedObject/jit-prefix.js | 17 +- js/src/jit/CodeGenerator.cpp | 29 ++ js/src/jit/CodeGenerator.h | 2 + js/src/jit/IonBuilder.cpp | 412 +++++++++++++++++- js/src/jit/IonBuilder.h | 33 ++ js/src/jit/LIR-Common.h | 40 ++ js/src/jit/LOpcodes.h | 2 + js/src/jit/Lowering.cpp | 17 + js/src/jit/Lowering.h | 2 + js/src/jit/MIR.cpp | 7 + js/src/jit/MIR.h | 99 +++++ js/src/jit/MOpcodes.h | 2 + js/src/jit/ParallelSafetyAnalysis.cpp | 2 + js/src/jit/TypePolicy.cpp | 1 + js/src/jit/TypeRepresentationSet.cpp | 278 ++++++++++++ js/src/jit/TypeRepresentationSet.h | 146 +++++++ js/src/jit/VMFunctions.cpp | 8 + js/src/jit/VMFunctions.h | 3 + js/src/jscompartment.h | 2 +- js/src/moz.build | 1 + 25 files changed, 1125 insertions(+), 37 deletions(-) create mode 100644 js/src/jit-test/tests/TypedObject/jit-complex.js create mode 100644 js/src/jit/TypeRepresentationSet.cpp create mode 100644 js/src/jit/TypeRepresentationSet.h diff --git a/js/src/builtin/TypeRepresentation.cpp b/js/src/builtin/TypeRepresentation.cpp index 5402efa841e6..7c64b211b598 100644 --- a/js/src/builtin/TypeRepresentation.cpp +++ b/js/src/builtin/TypeRepresentation.cpp @@ -260,7 +260,7 @@ StructTypeRepresentation::init(JSContext *cx, JSObject * TypeRepresentation::addToTableOrFree(JSContext *cx, - TypeRepresentationSet::AddPtr &p) + TypeRepresentationHash::AddPtr &p) { JS_ASSERT(!ownerObject_); @@ -298,7 +298,7 @@ ScalarTypeRepresentation::Create(JSContext *cx, JSCompartment *comp = cx->compartment(); ScalarTypeRepresentation sample(type); - TypeRepresentationSet::AddPtr p = comp->typeReprs.lookupForAdd(&sample); + TypeRepresentationHash::AddPtr p = comp->typeReprs.lookupForAdd(&sample); if (p) return (*p)->ownerObject(); @@ -332,7 +332,7 @@ ArrayTypeRepresentation::Create(JSContext *cx, } ArrayTypeRepresentation sample(element, length); - TypeRepresentationSet::AddPtr p = comp->typeReprs.lookupForAdd(&sample); + TypeRepresentationHash::AddPtr p = comp->typeReprs.lookupForAdd(&sample); if (p) return (*p)->ownerObject(); @@ -364,7 +364,7 @@ StructTypeRepresentation::Create(JSContext *cx, if (!ptr->init(cx, ids, typeReprOwners)) return NULL; - TypeRepresentationSet::AddPtr p = comp->typeReprs.lookupForAdd(ptr); + TypeRepresentationHash::AddPtr p = comp->typeReprs.lookupForAdd(ptr); if (p) { js_free(ptr); // do not finalize, not present in the table return (*p)->ownerObject(); @@ -540,10 +540,10 @@ StructTypeRepresentation::appendStringStruct(JSContext *cx, StringBuffer &conten // Misc const StructField * -StructTypeRepresentation::fieldNamed(HandleId id) const +StructTypeRepresentation::fieldNamed(jsid id) const { for (size_t i = 0; i < fieldCount(); i++) { - if (field(i).id.get() == id.get()) + if (field(i).id.get() == id) return &field(i); } return NULL; diff --git a/js/src/builtin/TypeRepresentation.h b/js/src/builtin/TypeRepresentation.h index 4c67ebf479ac..16c637bd216b 100644 --- a/js/src/builtin/TypeRepresentation.h +++ b/js/src/builtin/TypeRepresentation.h @@ -85,7 +85,7 @@ struct TypeRepresentationHasher typedef js::HashSet TypeRepresentationSet; + RuntimeAllocPolicy> TypeRepresentationHash; class TypeRepresentation { public: @@ -102,7 +102,7 @@ class TypeRepresentation { size_t alignment_; Kind kind_; - JSObject *addToTableOrFree(JSContext *cx, TypeRepresentationSet::AddPtr &p); + JSObject *addToTableOrFree(JSContext *cx, TypeRepresentationHash::AddPtr &p); private: static const Class class_; @@ -290,7 +290,7 @@ class StructTypeRepresentation : public TypeRepresentation { return fields()[i]; } - const StructField *fieldNamed(HandleId id) const; + const StructField *fieldNamed(jsid id) const; static JSObject *Create(JSContext *cx, AutoIdVector &ids, diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 6cbf1123d135..4294f6c3420a 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -2366,3 +2366,10 @@ BinaryBlock::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op, return true; } + +/* static */ size_t +BinaryBlock::dataOffset() +{ + return JSObject::getPrivateDataOffset(BLOCK_RESERVED_SLOTS + 1); +} + diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h index 840c4037c8b2..2852b89ee9fd 100644 --- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -197,6 +197,10 @@ class BinaryBlock public: static const Class class_; + // Returns the offset in bytes within the object where the `void*` + // pointer can be found. + static size_t dataOffset(); + static bool isBlock(HandleObject val); static uint8_t *mem(HandleObject val); diff --git a/js/src/jit-test/tests/TypedObject/jit-complex.js b/js/src/jit-test/tests/TypedObject/jit-complex.js new file mode 100644 index 000000000000..a80f14c870e0 --- /dev/null +++ b/js/src/jit-test/tests/TypedObject/jit-complex.js @@ -0,0 +1,30 @@ +// Test that we can optimize stuff like line.target.x without +// creating an intermediate object. + +if (!this.hasOwnProperty("Type")) + quit(); + +var PointType = new StructType({x: float64, + y: float64}); +var LineType = new StructType({source: PointType, + target: PointType}); + +function manhattenDistance(line) { + return (Math.abs(line.target.x - line.source.x) + + Math.abs(line.target.y - line.source.y)); +} + +function foo() { + var N = 30000; + var points = []; + var obj; + var s; + + var fromAToB = new LineType({source: {x: 22, y: 44}, + target: {x: 66, y: 88}}); + + for (var i = 0; i < N; i++) + assertEq(manhattenDistance(fromAToB), 88); +} + +foo(); diff --git a/js/src/jit-test/tests/TypedObject/jit-prefix.js b/js/src/jit-test/tests/TypedObject/jit-prefix.js index d108711eac70..290f3425c62f 100644 --- a/js/src/jit-test/tests/TypedObject/jit-prefix.js +++ b/js/src/jit-test/tests/TypedObject/jit-prefix.js @@ -12,16 +12,25 @@ function xPlusY(p) { return p.x + p.y; } +function xPlusYTweak(p) { + p.x = 22; + return xPlusY(p); +} + function foo() { var N = 30000; var points = []; + var obj; + var s; + for (var i = 0; i < N; i++) { - var s; if ((i % 2) == 0 || true) - s = xPlusY(new PointType2({x: i, y: i+1})); + obj = new PointType2({x: i, y: i+1}); else - s = xPlusY(new PointType3({x: i, y: i+1, z: i+2})); - assertEq(s, i + i + 1); + obj = new PointType3({x: i, y: i+1, z: i+2}); + + assertEq(xPlusY(obj), i + i + 1); + assertEq(xPlusYTweak(obj), 22 + i + 1); } } diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 7d5abb5e16f5..2cd207667d22 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -16,6 +16,7 @@ #include "jsnum.h" #include "builtin/Eval.h" +#include "builtin/TypedObject.h" #include "gc/Nursery.h" #include "jit/ExecutionModeInlines.h" #include "jit/IonLinker.h" @@ -2861,6 +2862,25 @@ CodeGenerator::visitNewArrayCallVM(LNewArray *lir) return true; } +typedef JSObject *(*NewDerivedTypedObjectFn)(JSContext *, + HandleObject type, + HandleObject owner, + int32_t offset); +static const VMFunction CreateDerivedTypedObjInfo = + FunctionInfo(CreateDerivedTypedObj); + +bool +CodeGenerator::visitNewDerivedTypedObject(LNewDerivedTypedObject *lir) +{ + // Not yet made safe for par exec: + JS_ASSERT(gen->info().executionMode() == SequentialExecution); + + pushArg(ToRegister(lir->offset())); + pushArg(ToRegister(lir->owner())); + pushArg(ToRegister(lir->type())); + return callVM(CreateDerivedTypedObjInfo, lir); +} + bool CodeGenerator::visitNewSlots(LNewSlots *lir) { @@ -3577,6 +3597,15 @@ CodeGenerator::visitTypedArrayElements(LTypedArrayElements *lir) return true; } +bool +CodeGenerator::visitTypedObjectElements(LTypedObjectElements *lir) +{ + Register obj = ToRegister(lir->object()); + Register out = ToRegister(lir->output()); + masm.loadPtr(Address(obj, BinaryBlock::dataOffset()), out); + return true; +} + bool CodeGenerator::visitStringLength(LStringLength *lir) { diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 81cccae27c45..69148786560a 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -132,6 +132,7 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitNewStringObject(LNewStringObject *lir); bool visitNewPar(LNewPar *lir); bool visitNewDenseArrayPar(LNewDenseArrayPar *lir); + bool visitNewDerivedTypedObject(LNewDerivedTypedObject *lir); bool visitAbortPar(LAbortPar *lir); bool visitInitElem(LInitElem *lir); bool visitInitElemGetterSetter(LInitElemGetterSetter *lir); @@ -148,6 +149,7 @@ class CodeGenerator : public CodeGeneratorSpecific bool visitArrayLength(LArrayLength *lir); bool visitTypedArrayLength(LTypedArrayLength *lir); bool visitTypedArrayElements(LTypedArrayElements *lir); + bool visitTypedObjectElements(LTypedObjectElements *lir); bool visitStringLength(LStringLength *lir); bool visitInitializedLength(LInitializedLength *lir); bool visitSetInitializedLength(LSetInitializedLength *lir); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index bd4fcb7ed194..4249403e41fe 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -11,6 +11,7 @@ #include "jsautooplen.h" #include "builtin/Eval.h" +#include "builtin/TypedObject.h" #include "builtin/TypeRepresentation.h" #include "frontend/SourceNotes.h" #include "jit/BaselineFrame.h" @@ -1186,7 +1187,15 @@ IonBuilder::traverseBytecode() // FALL THROUGH default: - JS_ASSERT(popped[i]->isFolded() || popped[i]->defUseCount() > poppedUses[i]); + JS_ASSERT(popped[i]->isFolded() || + + // MNewDerivedTypedObject instances are + // often dead unless they escape from the + // fn. See IonBuilder::loadTypedObjectData() + // for more details. + popped[i]->isNewDerivedTypedObject() || + + popped[i]->defUseCount() > poppedUses[i]); break; } } @@ -7001,6 +7010,28 @@ IonBuilder::convertShiftToMaskForStaticTypedArray(MDefinition *id, return ptr; } +static MIRType +MIRTypeForTypedArrayRead(ScalarTypeRepresentation::Type arrayType, + bool observedDouble) +{ + switch (arrayType) { + case ScalarTypeRepresentation::TYPE_INT8: + case ScalarTypeRepresentation::TYPE_UINT8: + case ScalarTypeRepresentation::TYPE_UINT8_CLAMPED: + case ScalarTypeRepresentation::TYPE_INT16: + case ScalarTypeRepresentation::TYPE_UINT16: + case ScalarTypeRepresentation::TYPE_INT32: + return MIRType_Int32; + case ScalarTypeRepresentation::TYPE_UINT32: + return observedDouble ? MIRType_Double : MIRType_Int32; + case ScalarTypeRepresentation::TYPE_FLOAT32: + return (LIRGenerator::allowFloat32Optimizations()) ? MIRType_Float32 : MIRType_Double; + case ScalarTypeRepresentation::TYPE_FLOAT64: + return MIRType_Double; + } + MOZ_ASSUME_UNREACHABLE("Unknown typed array type"); +} + bool IonBuilder::jsop_getelem_typed(MDefinition *obj, MDefinition *index, ScalarTypeRepresentation::Type arrayType) @@ -7027,28 +7058,7 @@ IonBuilder::jsop_getelem_typed(MDefinition *obj, MDefinition *index, // the array type to determine the result type, even if the opcode has // never executed. The known pushed type is only used to distinguish // uint32 reads that may produce either doubles or integers. - MIRType knownType; - switch (arrayType) { - case ScalarTypeRepresentation::TYPE_INT8: - case ScalarTypeRepresentation::TYPE_UINT8: - case ScalarTypeRepresentation::TYPE_UINT8_CLAMPED: - case ScalarTypeRepresentation::TYPE_INT16: - case ScalarTypeRepresentation::TYPE_UINT16: - case ScalarTypeRepresentation::TYPE_INT32: - knownType = MIRType_Int32; - break; - case ScalarTypeRepresentation::TYPE_UINT32: - knownType = allowDouble ? MIRType_Double : MIRType_Int32; - break; - case ScalarTypeRepresentation::TYPE_FLOAT32: - knownType = (LIRGenerator::allowFloat32Optimizations()) ? MIRType_Float32 : MIRType_Double; - break; - case ScalarTypeRepresentation::TYPE_FLOAT64: - knownType = MIRType_Double; - break; - default: - MOZ_ASSUME_UNREACHABLE("Unknown typed array type"); - } + MIRType knownType = MIRTypeForTypedArrayRead(arrayType, allowDouble); // Get the length. MInstruction *length = getTypedArrayLength(obj); @@ -8150,6 +8160,10 @@ IonBuilder::jsop_getprop(PropertyName *name) return resumeAfter(call); } + // Try to emit loads from known binary data blocks + if (!getPropTryTypedObject(&emitted, id, types) || emitted) + return emitted; + // Try to emit loads from definite slots. if (!getPropTryDefiniteSlot(&emitted, name, barrier, types) || emitted) return emitted; @@ -8231,6 +8245,125 @@ IonBuilder::getPropTryConstant(bool *emitted, jsid id, types::TemporaryTypeSet * return true; } +bool +IonBuilder::getPropTryTypedObject(bool *emitted, + jsid id, + types::TemporaryTypeSet *resultTypes) +{ + TypeRepresentationSet fieldTypeReprs; + int32_t fieldOffset; + size_t fieldIndex; + if (!lookupTypedObjectField(current->peek(-1), id, &fieldOffset, + &fieldTypeReprs, &fieldIndex)) + return false; + if (fieldTypeReprs.empty()) + return true; + + switch (fieldTypeReprs.kind()) { + case TypeRepresentation::Struct: + case TypeRepresentation::Array: + return getPropTryComplexPropOfTypedObject(emitted, + fieldOffset, + fieldTypeReprs, + fieldIndex, + resultTypes); + + case TypeRepresentation::Scalar: + return getPropTryScalarPropOfTypedObject(emitted, + fieldOffset, + fieldTypeReprs, + resultTypes); + } + + MOZ_ASSUME_UNREACHABLE("Bad kind"); +} + +bool +IonBuilder::getPropTryScalarPropOfTypedObject(bool *emitted, + int32_t fieldOffset, + TypeRepresentationSet fieldTypeReprs, + types::TemporaryTypeSet *resultTypes) +{ + // Must always be loading the same scalar type + if (fieldTypeReprs.length() != 1) + return true; + ScalarTypeRepresentation *fieldTypeRepr = fieldTypeReprs.get(0)->asScalar(); + + // OK! + *emitted = true; + + MDefinition *typedObj = current->pop(); + + // Find location within the owner object. + MDefinition *owner, *ownerOffset; + loadTypedObjectData(typedObj, fieldOffset, &owner, &ownerOffset); + + // Load the element data. + MTypedObjectElements *elements = MTypedObjectElements::New(owner); + current->add(elements); + + // Reading from an Uint32Array will result in a double for values + // that don't fit in an int32. We have to bailout if this happens + // and the instruction is not known to return a double. + bool allowDouble = + resultTypes->hasType(types::Type::DoubleType()); + MIRType knownType = + MIRTypeForTypedArrayRead(fieldTypeRepr->type(), allowDouble); + + // Typed array offsets are expressed in units of the alignment, + // and the binary data API guarantees all offsets are properly + // aligned. So just do the divide. + MConstant *alignment = + MConstant::New(Int32Value(fieldTypeRepr->alignment())); + current->add(alignment); + MDiv *scaledOffset = + MDiv::NewAsmJS(ownerOffset, alignment, MIRType_Int32); + current->add(scaledOffset); + + MLoadTypedArrayElement *load = + MLoadTypedArrayElement::New(elements, scaledOffset, + fieldTypeRepr->type()); + load->setResultType(knownType); + load->setResultTypeSet(resultTypes); + current->add(load); + current->push(load); + return true; +} + +bool +IonBuilder::getPropTryComplexPropOfTypedObject(bool *emitted, + int32_t fieldOffset, + TypeRepresentationSet fieldTypeReprs, + size_t fieldIndex, + types::TemporaryTypeSet *resultTypes) +{ + // Must know the field index so that we can load the new type + // object for the derived value + if (fieldIndex == SIZE_MAX) + return true; + + *emitted = true; + MDefinition *typedObj = current->pop(); + + // Identify the type object for the field. + MDefinition *type = loadTypedObjectType(typedObj); + MDefinition *fieldType = typeObjectForFieldFromStructType(type, fieldIndex); + + // Find location within the owner object. + MDefinition *owner, *ownerOffset; + loadTypedObjectData(typedObj, fieldOffset, &owner, &ownerOffset); + + // Create the derived type object. + MInstruction *derived = new MNewDerivedTypedObject(fieldTypeReprs, + fieldType, + owner, + ownerOffset); + derived->setResultTypeSet(resultTypes); + current->add(derived); + current->push(derived); + return true; +} + bool IonBuilder::getPropTryDefiniteSlot(bool *emitted, PropertyName *name, bool barrier, types::TemporaryTypeSet *types) @@ -8521,6 +8654,10 @@ IonBuilder::jsop_setprop(PropertyName *name) return false; } + // Try to emit stores to known binary data blocks + if (!setPropTryTypedObject(&emitted, obj, id, value) || emitted) + return emitted; + // Try to emit store from definite slots. if (!setPropTryDefiniteSlot(&emitted, obj, name, value, barrier, objTypes) || emitted) return emitted; @@ -8653,6 +8790,66 @@ IonBuilder::setPropTryCommonDOMSetter(bool *emitted, MDefinition *obj, return true; } +bool +IonBuilder::setPropTryTypedObject(bool *emitted, MDefinition *obj, + jsid id, MDefinition *value) +{ + TypeRepresentationSet fieldTypeReprs; + int32_t fieldOffset; + size_t fieldIndex; + if (!lookupTypedObjectField(obj, id, &fieldOffset, &fieldTypeReprs, + &fieldIndex)) + return false; + if (fieldTypeReprs.empty()) + return true; + + switch (fieldTypeReprs.kind()) { + case TypeRepresentation::Struct: + case TypeRepresentation::Array: + // For now, only optimize storing scalars. + return true; + + case TypeRepresentation::Scalar: + break; + } + + // Must always be storing the same scalar type + if (fieldTypeReprs.length() != 1) + return true; + ScalarTypeRepresentation *fieldTypeRepr = fieldTypeReprs.get(0)->asScalar(); + + // OK! + *emitted = true; + + MTypedObjectElements *elements = MTypedObjectElements::New(obj); + current->add(elements); + + // Typed array offsets are expressed in units of the alignment, + // and the binary data API guarantees all offsets are properly + // aligned. + JS_ASSERT(fieldOffset % fieldTypeRepr->alignment() == 0); + int32_t scaledFieldOffset = fieldOffset / fieldTypeRepr->alignment(); + + MConstant *offset = MConstant::New(Int32Value(scaledFieldOffset)); + current->add(offset); + + // Clamp value to [0, 255] for Uint8ClampedArray. + MDefinition *toWrite = value; + if (fieldTypeRepr->type() == ScalarTypeRepresentation::TYPE_UINT8_CLAMPED) { + toWrite = MClampToUint8::New(value); + current->add(toWrite->toInstruction()); + } + + MStoreTypedArrayElement *store = + MStoreTypedArrayElement::New(elements, offset, toWrite, + fieldTypeRepr->type()); + current->add(store); + + current->push(value); + + return true; +} + bool IonBuilder::setPropTryDefiniteSlot(bool *emitted, MDefinition *obj, PropertyName *name, MDefinition *value, @@ -9325,3 +9522,172 @@ IonBuilder::cloneTypeSet(types::StackTypeSet *types) // after bug 804676 this code can be removed. return types->clone(GetIonContext()->temp->lifoAlloc()); } + +TypeRepresentationSetHash * +IonBuilder::getOrCreateReprSetHash() +{ + if (!reprSetHash_) { + TypeRepresentationSetHash* hash = + cx->new_(); + if (!hash || !hash->init()) { + js_delete(hash); + return NULL; + } + + reprSetHash_ = hash; + } + return reprSetHash_.get(); +} + +bool +IonBuilder::lookupTypeRepresentationSet(MDefinition *typedObj, + TypeRepresentationSet *out) +{ + *out = TypeRepresentationSet(); // default to unknown + + // Extract TypeRepresentationSet directly if we can + if (typedObj->isNewDerivedTypedObject()) { + *out = typedObj->toNewDerivedTypedObject()->set(); + return true; + } + + // Extract TypeRepresentationSet directly if we can + types::TemporaryTypeSet *types = typedObj->resultTypeSet(); + if (!types || types->getKnownTypeTag() != JSVAL_TYPE_OBJECT) + return true; + + // And only known objects. + if (types->unknownObject()) + return true; + + TypeRepresentationSetBuilder set; + for (uint32_t i = 0; i < types->getObjectCount(); i++) { + types::TypeObject *type = types->getTypeObject(0); + if (!type || type->unknownProperties()) + return true; + + if (!type->hasTypedObject()) + return true; + + TypeRepresentation *typeRepr = type->typedObject()->typeRepr; + if (!set.insert(typeRepr)) + return false; + } + + return set.build(*this, out); +} + +MDefinition * +IonBuilder::loadTypedObjectType(MDefinition *typedObj) +{ + // Shortcircuit derived type objects, meaning the intermediate + // objects created to represent `a.b` in an expression like + // `a.b.c`. In that case, the type object can be simply pulled + // from the operands of that instruction. + if (typedObj->isNewDerivedTypedObject()) + return typedObj->toNewDerivedTypedObject()->type(); + + MInstruction *load = MLoadFixedSlot::New(typedObj, js::SLOT_DATATYPE); + current->add(load); + return load; +} + +// Given a typed object `typedObj` and an offset `offset` into that +// object's data, returns another typed object and adusted offset +// where the data can be found. Often, these returned values are the +// same as the inputs, but in cases where intermediate derived type +// objects have been created, the return values will remove +// intermediate layers (often rendering those derived type objects +// into dead code). +void +IonBuilder::loadTypedObjectData(MDefinition *typedObj, + int32_t offset, + MDefinition **owner, + MDefinition **ownerOffset) +{ + MConstant *offsetDef = MConstant::New(Int32Value(offset)); + current->add(offsetDef); + + // Shortcircuit derived type objects, meaning the intermediate + // objects created to represent `a.b` in an expression like + // `a.b.c`. In that case, the owned and a base offset can be + // pulled from the operands of the instruction and combined with + // `offset`. + if (typedObj->isNewDerivedTypedObject()) { + // If we see that the + MNewDerivedTypedObject *ins = typedObj->toNewDerivedTypedObject(); + + MAdd *offsetAdd = MAdd::NewAsmJS(ins->offset(), offsetDef, + MIRType_Int32); + current->add(offsetAdd); + + *owner = ins->owner(); + *ownerOffset = offsetAdd; + return; + } + + *owner = typedObj; + *ownerOffset = offsetDef; +} + +// Looks up the offset/type-repr-set of the field `id`, given the type +// set `objTypes` of the field owner. Note that even when true is +// returned, `*fieldTypeReprs` might be empty if no useful type/offset +// pair could be determined. +bool +IonBuilder::lookupTypedObjectField(MDefinition *typedObj, + jsid id, + int32_t *fieldOffset, + TypeRepresentationSet *fieldTypeReprs, + size_t *fieldIndex) +{ + TypeRepresentationSet objTypeReprs; + if (!lookupTypeRepresentationSet(typedObj, &objTypeReprs)) + return false; + + // Must be accessing a struct. + if (!objTypeReprs.allOfKind(TypeRepresentation::Struct)) + return true; + + // Determine the type/offset of the field `id`, if any. + size_t offset; + if (!objTypeReprs.fieldNamed(*this, id, &offset, + fieldTypeReprs, fieldIndex)) + return false; + if (fieldTypeReprs->empty()) + return false; + + // Field offset must be representable as signed integer. + if (offset >= size_t(INT_MAX)) { + *fieldTypeReprs = TypeRepresentationSet(); + return true; + } + + *fieldOffset = int32_t(offset); + JS_ASSERT(*fieldOffset >= 0); + + return true; +} + +MDefinition * +IonBuilder::typeObjectForFieldFromStructType(MDefinition *typeObj, + size_t fieldIndex) +{ + // Load list of field type objects. + + MInstruction *fieldTypes = MLoadFixedSlot::New(typeObj, SLOT_STRUCT_FIELD_TYPES); + current->add(fieldTypes); + + // Index into list with index of field. + + MInstruction *fieldTypesElements = MElements::New(fieldTypes); + current->add(fieldTypesElements); + + MConstant *fieldIndexDef = MConstant::New(Int32Value(fieldIndex)); + current->add(fieldIndexDef); + + MInstruction *fieldType = MLoadElement::New(fieldTypesElements, fieldIndexDef, false, false); + current->add(fieldType); + + return fieldType; +} diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 7422fb293e44..fcbb061edb82 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -15,6 +15,7 @@ #include "jit/BytecodeAnalysis.h" #include "jit/MIR.h" #include "jit/MIRGraph.h" +#include "jit/TypeRepresentationSet.h" namespace js { namespace jit { @@ -363,6 +364,17 @@ class IonBuilder : public MIRGenerator bool barrier, types::TemporaryTypeSet *types); bool getPropTryInlineAccess(bool *emitted, PropertyName *name, jsid id, bool barrier, types::TemporaryTypeSet *types); + bool getPropTryTypedObject(bool *emitted, jsid id, + types::TemporaryTypeSet *resultTypes); + bool getPropTryScalarPropOfTypedObject(bool *emitted, + int32_t fieldOffset, + TypeRepresentationSet fieldTypeReprs, + types::TemporaryTypeSet *resultTypes); + bool getPropTryComplexPropOfTypedObject(bool *emitted, + int32_t fieldOffset, + TypeRepresentationSet fieldTypeReprs, + size_t fieldIndex, + types::TemporaryTypeSet *resultTypes); bool getPropTryCache(bool *emitted, PropertyName *name, jsid id, bool barrier, types::TemporaryTypeSet *types); bool needsToMonitorMissingProperties(types::TemporaryTypeSet *types); @@ -381,10 +393,28 @@ class IonBuilder : public MIRGenerator PropertyName *name, jsid id, MDefinition *value, bool barrier, types::TemporaryTypeSet *objTypes); + bool setPropTryTypedObject(bool *emitted, MDefinition *obj, + jsid id, MDefinition *value); bool setPropTryCache(bool *emitted, MDefinition *obj, PropertyName *name, MDefinition *value, bool barrier, types::TemporaryTypeSet *objTypes); + // binary data lookup helpers. + bool lookupTypeRepresentationSet(MDefinition *typedObj, + TypeRepresentationSet *out); + bool lookupTypedObjectField(MDefinition *typedObj, + jsid id, + int32_t *fieldOffset, + TypeRepresentationSet *fieldTypeReprs, + size_t *fieldIndex); + MDefinition *loadTypedObjectType(MDefinition *value); + void loadTypedObjectData(MDefinition *inOwner, + int32_t inOffset, + MDefinition **outOwner, + MDefinition **outOffset); + MDefinition *typeObjectForFieldFromStructType(MDefinition *type, + size_t fieldIndex); + // jsop_setelem() helpers. bool setElemTryTyped(bool *emitted, MDefinition *object, MDefinition *index, MDefinition *value); @@ -641,12 +671,15 @@ class IonBuilder : public MIRGenerator AbortReason abortReason() { return abortReason_; } + TypeRepresentationSetHash *getOrCreateReprSetHash(); // fallible + private: bool init(); JSContext *cx; BaselineFrame *baselineFrame_; AbortReason abortReason_; + ScopedJSDeletePtr reprSetHash_; // Basic analysis information about the script. BytecodeAnalysis analysis_; diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 714e7f94bcb3..88e065e9b6c4 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -501,6 +501,32 @@ public: } }; +class LNewDerivedTypedObject : public LCallInstructionHelper<1, 3, 0> +{ + public: + LIR_HEADER(NewDerivedTypedObject); + + LNewDerivedTypedObject(const LAllocation &type, + const LAllocation &owner, + const LAllocation &offset) { + setOperand(0, type); + setOperand(1, owner); + setOperand(2, offset); + } + + const LAllocation *type() { + return getOperand(0); + } + + const LAllocation *owner() { + return getOperand(1); + } + + const LAllocation *offset() { + return getOperand(2); + } +}; + class LNewStringObject : public LInstructionHelper<1, 1, 1> { public: @@ -3042,6 +3068,20 @@ class LTypedArrayElements : public LInstructionHelper<1, 1, 0> } }; +// Load a typed array's elements vector. +class LTypedObjectElements : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(TypedObjectElements) + + LTypedObjectElements(const LAllocation &object) { + setOperand(0, object); + } + const LAllocation *object() { + return getOperand(0); + } +}; + // Bailout if index >= length. class LBoundsCheck : public LInstructionHelper<0, 2, 0> { diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index ad406e328b71..8906ae0e8545 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -32,6 +32,7 @@ _(NewPar) \ _(NewDenseArrayPar) \ _(NewCallObjectPar) \ + _(NewDerivedTypedObject) \ _(AbortPar) \ _(InitElem) \ _(InitElemGetterSetter) \ @@ -221,6 +222,7 @@ _(ArrayLength) \ _(TypedArrayLength) \ _(TypedArrayElements) \ + _(TypedObjectElements) \ _(StringLength) \ _(ArgumentsLength) \ _(GetArgument) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 3fd63c929e30..3d3964fae04f 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -207,6 +207,16 @@ LIRGenerator::visitNewCallObject(MNewCallObject *ins) return true; } +bool +LIRGenerator::visitNewDerivedTypedObject(MNewDerivedTypedObject *ins) +{ + LNewDerivedTypedObject *lir = + new LNewDerivedTypedObject(useRegisterAtStart(ins->type()), + useRegisterAtStart(ins->owner()), + useRegisterAtStart(ins->offset())); + return defineReturn(lir, ins) && assignSafepoint(lir, ins); +} + bool LIRGenerator::visitNewCallObjectPar(MNewCallObjectPar *ins) { @@ -2068,6 +2078,13 @@ LIRGenerator::visitTypedArrayElements(MTypedArrayElements *ins) return define(new LTypedArrayElements(useRegisterAtStart(ins->object())), ins); } +bool +LIRGenerator::visitTypedObjectElements(MTypedObjectElements *ins) +{ + JS_ASSERT(ins->type() == MIRType_Elements); + return define(new LTypedObjectElements(useRegisterAtStart(ins->object())), ins); +} + bool LIRGenerator::visitInitializedLength(MInitializedLength *ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 5b18dfe03060..87e270d5ace8 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -89,6 +89,7 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitNewDeclEnvObject(MNewDeclEnvObject *ins); bool visitNewCallObject(MNewCallObject *ins); bool visitNewStringObject(MNewStringObject *ins); + bool visitNewDerivedTypedObject(MNewDerivedTypedObject *ins); bool visitNewPar(MNewPar *ins); bool visitNewCallObjectPar(MNewCallObjectPar *ins); bool visitNewDenseArrayPar(MNewDenseArrayPar *ins); @@ -182,6 +183,7 @@ class LIRGenerator : public LIRGeneratorSpecific bool visitArrayLength(MArrayLength *ins); bool visitTypedArrayLength(MTypedArrayLength *ins); bool visitTypedArrayElements(MTypedArrayElements *ins); + bool visitTypedObjectElements(MTypedObjectElements *ins); bool visitInitializedLength(MInitializedLength *ins); bool visitSetInitializedLength(MSetInitializedLength *ins); bool visitNot(MNot *ins); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 4b20274084fe..a0258248c2f8 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -539,6 +539,13 @@ MConstantElements::printOpcode(FILE *fp) const fprintf(fp, " %p", value()); } +void +MLoadTypedArrayElement::printOpcode(FILE *fp) const +{ + MDefinition::printOpcode(fp); + fprintf(fp, " %s", ScalarTypeRepresentation::typeName(arrayType())); +} + MParameter * MParameter::New(int32_t index, types::TemporaryTypeSet *types) { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 09924a170ce3..e3853afa8575 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -24,6 +24,7 @@ #include "jit/IonMacroAssembler.h" #include "jit/MOpcodes.h" #include "jit/TypePolicy.h" +#include "jit/TypeRepresentationSet.h" #include "vm/ScopeObject.h" namespace js { @@ -1487,6 +1488,66 @@ class MNewPar : public MUnaryInstruction } }; +// Creates a new derived type object. At runtime, this is just a call +// to `BinaryBlock::createDerived()`. That is, the MIR itself does not +// compile to particularly optimized code. However, using a distinct +// MIR for creating derived type objects allows the compiler to +// optimize ephemeral typed objects as would be created for a +// reference like `a.b.c` -- here, the `a.b` will create an ephemeral +// derived type object that aliases the memory of `a` itself. The +// specific nature of `a.b` is revealed by using +// `MNewDerivedTypedObject` rather than `MGetProperty` or what have +// you. Moreover, the compiler knows that there are no side-effects, +// so `MNewDerivedTypedObject` instructions can be reordered or pruned +// as dead code. +class MNewDerivedTypedObject + : public MTernaryInstruction, + public Mix3Policy, + ObjectPolicy<1>, + IntPolicy<2> > +{ + private: + TypeRepresentationSet set_; + + public: + INSTRUCTION_HEADER(NewDerivedTypedObject); + + MNewDerivedTypedObject(TypeRepresentationSet set, + MDefinition *type, + MDefinition *owner, + MDefinition *offset) + : MTernaryInstruction(type, owner, offset), + set_(set) + { + setMovable(); + setResultType(MIRType_Object); + } + + TypeRepresentationSet set() const { + return set_; + } + + MDefinition *type() const { + return getOperand(0); + } + + MDefinition *owner() const { + return getOperand(1); + } + + MDefinition *offset() const { + return getOperand(2); + } + + TypePolicy *typePolicy() { + return this; + } + + virtual AliasSet getAliasSet() const { + return AliasSet::None(); + } +}; + // Abort parallel execution. class MAbortPar : public MAryControlInstruction<0, 0> { @@ -4882,6 +4943,42 @@ class MTypedArrayElements } }; +// Load a binary data object's "elements", which is just its opaque +// binary data space. Eventually this should probably be +// unified with `MTypedArrayElements`. +class MTypedObjectElements + : public MUnaryInstruction, + public SingleObjectPolicy +{ + private: + MTypedObjectElements(MDefinition *object) + : MUnaryInstruction(object) + { + setResultType(MIRType_Elements); + setMovable(); + } + + public: + INSTRUCTION_HEADER(TypedObjectElements) + + static MTypedObjectElements *New(MDefinition *object) { + return new MTypedObjectElements(object); + } + + TypePolicy *typePolicy() { + return this; + } + MDefinition *object() const { + return getOperand(0); + } + bool congruentTo(MDefinition *ins) const { + return congruentIfOperandsEqual(ins); + } + AliasSet getAliasSet() const { + return AliasSet::Load(AliasSet::ObjectFields); + } +}; + // Perform !-operation class MNot : public MUnaryInstruction, @@ -5423,6 +5520,8 @@ class MLoadTypedArrayElement return AliasSet::Load(AliasSet::TypedArrayElement); } + void printOpcode(FILE *fp) const; + void computeRange(); bool canProduceFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; } diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 54211aa21048..8c8bda18490d 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -123,6 +123,7 @@ namespace jit { _(ArrayLength) \ _(TypedArrayLength) \ _(TypedArrayElements) \ + _(TypedObjectElements) \ _(InitializedLength) \ _(SetInitializedLength) \ _(Not) \ @@ -197,6 +198,7 @@ namespace jit { _(NewCallObjectPar) \ _(NewPar) \ _(NewDenseArrayPar) \ + _(NewDerivedTypedObject) \ _(AbortPar) \ _(LambdaPar) \ _(RestPar) \ diff --git a/js/src/jit/ParallelSafetyAnalysis.cpp b/js/src/jit/ParallelSafetyAnalysis.cpp index d5cea9a52df3..d578663c7a9c 100644 --- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -175,6 +175,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor CUSTOM_OP(NewObject) CUSTOM_OP(NewCallObject) CUSTOM_OP(NewParallelArray) + UNSAFE_OP(NewDerivedTypedObject) UNSAFE_OP(InitElem) UNSAFE_OP(InitElemGetterSetter) UNSAFE_OP(InitProp) @@ -207,6 +208,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor SAFE_OP(ArrayLength) SAFE_OP(TypedArrayLength) SAFE_OP(TypedArrayElements) + SAFE_OP(TypedObjectElements) SAFE_OP(InitializedLength) WRITE_GUARDED_OP(SetInitializedLength, elements) SAFE_OP(Not) diff --git a/js/src/jit/TypePolicy.cpp b/js/src/jit/TypePolicy.cpp index f8071f713a52..c850505233d2 100644 --- a/js/src/jit/TypePolicy.cpp +++ b/js/src/jit/TypePolicy.cpp @@ -416,6 +416,7 @@ IntPolicy::staticAdjustInputs(MInstruction *def) template bool IntPolicy<0>::staticAdjustInputs(MInstruction *def); template bool IntPolicy<1>::staticAdjustInputs(MInstruction *def); +template bool IntPolicy<2>::staticAdjustInputs(MInstruction *def); template bool diff --git a/js/src/jit/TypeRepresentationSet.cpp b/js/src/jit/TypeRepresentationSet.cpp new file mode 100644 index 000000000000..458dc1c4775c --- /dev/null +++ b/js/src/jit/TypeRepresentationSet.cpp @@ -0,0 +1,278 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 "jit/TypeRepresentationSet.h" + +#include "mozilla/HashFunctions.h" + +#include "jit/IonBuilder.h" + +#include "jsinferinlines.h" + +using namespace js; +using namespace jit; + +/////////////////////////////////////////////////////////////////////////// +// TypeRepresentationSet hasher + +HashNumber +TypeRepresentationSetHasher::hash(TypeRepresentationSet key) +{ + HashNumber hn = mozilla::HashGeneric(key.length()); + for (size_t i = 0; i < key.length(); i++) + hn = mozilla::AddToHash(hn, uintptr_t(key.get(i))); + return hn; +} + +bool +TypeRepresentationSetHasher::match(TypeRepresentationSet key1, + TypeRepresentationSet key2) +{ + if (key1.length() != key2.length()) + return false; + + // Note: entries are always sorted + for (size_t i = 0; i < key1.length(); i++) { + if (key1.get(i) != key2.get(i)) + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////// +// TypeRepresentationSetBuilder + +TypeRepresentationSetBuilder::TypeRepresentationSetBuilder() + : invalid_(false) +{} + +bool +TypeRepresentationSetBuilder::insert(TypeRepresentation *typeRepr) +{ + if (invalid_) + return true; + + if (entries_.length() == 0) + return entries_.append(typeRepr); + + // Check that this new type repr is of the same basic kind as the + // ones we have seen thus far. If not, for example if we have an + // `int` and a `struct`, then convert this set to the invalid set. + TypeRepresentation *entry0 = entries_[0]; + if (typeRepr->kind() != entry0->kind()) { + invalid_ = true; + entries_.clear(); + return true; + } + + // Otherwise, use binary search to find the right place to insert + // the type descriptor. We keep list sorted by the *address* of + // the type representations within. + uintptr_t typeReprAddr = (uintptr_t) typeRepr; + size_t min = 0; + size_t max = entries_.length(); + while (min != max) { + size_t i = min + ((max - min) >> 1); // average w/o fear of overflow + + uintptr_t entryiaddr = (uintptr_t) entries_[i]; + if (entryiaddr == typeReprAddr) + return true; // typeRepr already present in the set + + if (entryiaddr < typeReprAddr) { + // typeRepr lies to the right of entry i + min = i; + } else { + // typeRepr lies to the left of entry i + max = i; + } + } + + // As a sanity check, give up if the TypeRepresentationSet grows too large. + if (entries_.length() >= 512) { + invalid_ = true; + entries_.clear(); + return true; + } + + // Not present. Insert at position `min`. + if (min == entries_.length()) + return entries_.append(typeRepr); + TypeRepresentation **insertLoc = &entries_[min]; + return entries_.insert(insertLoc, typeRepr) != NULL; +} + +bool +TypeRepresentationSetBuilder::build(IonBuilder &builder, + TypeRepresentationSet *out) +{ + if (invalid_) { + *out = TypeRepresentationSet(); + return true; + } + + TypeRepresentationSetHash *table = builder.getOrCreateReprSetHash(); + if (!table) + return false; + + // Check if there is already a copy in the hashtable. + size_t length = entries_.length(); + TypeRepresentationSet tempSet(length, entries_.begin()); + TypeRepresentationSetHash::AddPtr p = table->lookupForAdd(tempSet); + if (p) { + *out = *p; + return true; + } + + // If not, allocate a permanent copy in Ion temp memory and add it. + size_t space = sizeof(TypeRepresentation*) * length; + TypeRepresentation **array = (TypeRepresentation**) + GetIonContext()->temp->allocate(space); + if (!array) + return false; + memcpy(array, entries_.begin(), space); + TypeRepresentationSet permSet(length, array); + if (!table->add(p, permSet)) + return false; + + *out = permSet; + return true; +} + +/////////////////////////////////////////////////////////////////////////// +// TypeRepresentationSet + +TypeRepresentationSet::TypeRepresentationSet(const TypeRepresentationSet &c) + : length_(c.length_), + entries_(c.entries_) +{} + +TypeRepresentationSet::TypeRepresentationSet(size_t length, + TypeRepresentation **entries) + : length_(length), + entries_(entries) +{} + +TypeRepresentationSet::TypeRepresentationSet() + : length_(0), + entries_(NULL) +{} + +bool +TypeRepresentationSet::empty() +{ + return length() == 0; +} + +size_t +TypeRepresentationSet::length() +{ + return length_; +} + +TypeRepresentation * +TypeRepresentationSet::get(size_t i) +{ + JS_ASSERT(i < length()); + return entries_[i]; +} + +bool +TypeRepresentationSet::allOfKind(TypeRepresentation::Kind aKind) +{ + if (empty()) + return false; + + return kind() == aKind; +} + +TypeRepresentation::Kind +TypeRepresentationSet::kind() +{ + JS_ASSERT(!empty()); + return get(0)->kind(); +} + +size_t +TypeRepresentationSet::arrayLength() +{ + JS_ASSERT(kind() == TypeRepresentation::Array); + const size_t result = get(0)->asArray()->length(); + for (size_t i = 1; i < length(); i++) { + if (get(i)->asArray()->length() != result) + return SIZE_MAX; + } + return result; +} + +bool +TypeRepresentationSet::arrayElementType(IonBuilder &builder, + TypeRepresentationSet *out) +{ + JS_ASSERT(kind() == TypeRepresentation::Array); + + TypeRepresentationSetBuilder elementTypes; + for (size_t i = 0; i < length(); i++) { + if (!elementTypes.insert(get(i)->asArray()->element())) + return false; + } + return elementTypes.build(builder, out); +} + +bool +TypeRepresentationSet::fieldNamed(IonBuilder &builder, + jsid id, + size_t *offset, + TypeRepresentationSet *out, + size_t *index) +{ + JS_ASSERT(kind() == TypeRepresentation::Struct); + + // Initialize `*offset` and `*out` for the case where incompatible + // or absent fields are found. + *offset = SIZE_MAX; + *index = SIZE_MAX; + *out = TypeRepresentationSet(); + + // Remember offset of the first field. + size_t offset0; + size_t index0; + TypeRepresentationSetBuilder fieldTypes; + { + const StructField *field = get(0)->asStruct()->fieldNamed(id); + if (!field) + return true; + + offset0 = field->offset; + index0 = field->index; + if (!fieldTypes.insert(field->typeRepr)) + return false; + } + + // Check that all subsequent fields are at the same offset + // and compute the union of their types. + for (size_t i = 1; i < length(); i++) { + const StructField *field = get(i)->asStruct()->fieldNamed(id); + if (!field) + return true; + + if (field->offset != offset0) + return true; + + if (field->index != index0) + index0 = SIZE_MAX; + + if (!fieldTypes.insert(field->typeRepr)) + return false; + } + + // All struct types had a field named `id` at the same offset + // (though it's still possible that the types are incompatible and + // that the indices disagree). + *offset = offset0; + *index = index0; + return fieldTypes.build(builder, out); +} diff --git a/js/src/jit/TypeRepresentationSet.h b/js/src/jit/TypeRepresentationSet.h new file mode 100644 index 000000000000..5ec40fc0db9d --- /dev/null +++ b/js/src/jit/TypeRepresentationSet.h @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 jit_TypeRepresentationSet_h +#define jit_TypeRepresentationSet_h + +#include "builtin/TypeRepresentation.h" +#include "jit/IonAllocPolicy.h" +#include "js/HashTable.h" + +// TypeRepresentationSet stores a set of TypeRepresentation* objects, +// representing the possible types of the binary data associated with +// a typed object value. Often TypeRepresentationSets will be +// singleton sets, but it is also possible to have cases where many +// type representations flow into a single point. In such cases, the +// various type representations may differ in their details but often +// have a common prefix. We try to optimize this case as well. +// +// So, for example, consider some code like: +// +// var Point2Type = new StructType({x: uint8, y: uint8}); +// var Point3Type = new StructType({x: uint8, y: uint8, z: uint8}); +// +// function distance2d(pnt) { +// return Math.sqrt(pnt.x * pnt.x + pnt.y * pnt.y); +// } +// +// Even if the function `distance2d()` were used with instances of +// both Point2Type and Point3Type, we can still generate optimal code, +// because both of those types contain fields named `x` and `y` with +// the same types at the same offset. + +namespace js { +namespace jit { + +class IonBuilder; +class TypeRepresentationSet; + +class TypeRepresentationSetBuilder { + private: + Vector entries_; + bool invalid_; + + bool overlaps(TypeRepresentation *a, TypeRepresentation *b); + + public: + TypeRepresentationSetBuilder(); + + bool insert(TypeRepresentation *typeRepr); + bool build(IonBuilder &builder, TypeRepresentationSet *out); +}; + +class TypeRepresentationSet { + private: + friend class TypeRepresentationSetBuilder; + + size_t length_; + TypeRepresentation **entries_; // Allocated using temp policy + + TypeRepresentationSet(size_t length, TypeRepresentation **entries); + + public: + ////////////////////////////////////////////////////////////////////// + // Constructors + // + // For more flexible constructors, see + // TypeRepresentationSetBuilder above. + + TypeRepresentationSet(const TypeRepresentationSet &c); + TypeRepresentationSet(); // empty set + + ////////////////////////////////////////////////////////////////////// + // Query the set + + bool empty(); + size_t length(); + TypeRepresentation *get(size_t i); + bool allOfKind(TypeRepresentation::Kind kind); + + ////////////////////////////////////////////////////////////////////// + // The following operations are only valid on a non-empty set: + + TypeRepresentation::Kind kind(); + + ////////////////////////////////////////////////////////////////////// + // Array operations + // + // Only valid when `kind() == TypeRepresentation::Array` + + // Returns the length of the arrays in this set, or SIZE_MAX + // if they are not all the same. + size_t arrayLength(); + + // Returns a `TypeRepresentationSet` representing the element + // types of the various array types in this set. The returned set + // may be the empty set. + bool arrayElementType(IonBuilder &builder, TypeRepresentationSet *out); + + ////////////////////////////////////////////////////////////////////// + // Struct operations + // + // Only valid when `kind() == TypeRepresentation::Struct` + + // Searches the type in the set for a field named `id`. All + // possible types must agree on the offset of the field within the + // structure and the possible types of the field must be + // compatible. If any pair of types disagree on the offset or have + // incompatible types for the field, then `*out` will be set to + // the empty set. + // + // Upon success, `out` will be set to the set of possible types of + // the field and `offset` will be set to the field's offset within + // the struct (measured in bytes). + // + // The parameter `*index` is special. If all types agree on the + // index of the field, then `*index` is set to the field index. + // Otherwise, it is set to SIZE_MAX. Note that two types may agree + // on the type and offset of a field but disagree about its index, + // e.g. the field `c` in `new StructType({a: uint8, b: uint8, c: + // uint16})` and `new StructType({a: uint16, c: uint16})`. + bool fieldNamed(IonBuilder &builder, + jsid id, + size_t *offset, + TypeRepresentationSet *out, + size_t *index); +}; + +struct TypeRepresentationSetHasher +{ + typedef TypeRepresentationSet Lookup; + static HashNumber hash(TypeRepresentationSet key); + static bool match(TypeRepresentationSet key1, + TypeRepresentationSet key2); +}; + +typedef js::HashSet TypeRepresentationSetHash; + +} // namespace jit +} // namespace js + +#endif diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index b8a0e0657565..3a6a10585106 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -7,6 +7,7 @@ #include "jit/VMFunctions.h" #include "builtin/ParallelArray.h" +#include "builtin/TypedObject.h" #include "frontend/BytecodeCompiler.h" #include "jit/BaselineIC.h" #include "jit/Ion.h" @@ -872,5 +873,12 @@ InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame, uint32_t return frame->initForOsr(interpFrame, numStackValues); } +JSObject *CreateDerivedTypedObj(JSContext *cx, HandleObject type, + HandleObject owner, int32_t offset) +{ + return BinaryBlock::createDerived(cx, type, owner, offset); +} + + } // namespace jit } // namespace js diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index dfd20bdbe061..d50c2cc9b872 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -665,6 +665,9 @@ bool LeaveBlock(JSContext *cx, BaselineFrame *frame); bool InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame, uint32_t numStackValues); +JSObject *CreateDerivedTypedObj(JSContext *cx, HandleObject type, + HandleObject owner, int32_t offset); + } // namespace jit } // namespace js diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index c45d83cb0238..dc00edff58cd 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -204,7 +204,7 @@ struct JSCompartment js::RegExpCompartment regExps; /* Set of all currently living type representations. */ - js::TypeRepresentationSet typeReprs; + js::TypeRepresentationHash typeReprs; private: void sizeOfTypeInferenceData(JS::TypeInferenceSizes *stats, mozilla::MallocSizeOf mallocSizeOf); diff --git a/js/src/moz.build b/js/src/moz.build index 6008e8a55269..66c46490727d 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -248,6 +248,7 @@ if CONFIG['ENABLE_ION']: 'Snapshots.cpp', 'StupidAllocator.cpp', 'TypePolicy.cpp', + 'TypeRepresentationSet.cpp', 'UnreachableCodeElimination.cpp', 'VMFunctions.cpp', 'ValueNumbering.cpp', From 99c1606d76488e4a638b98f0307ee85226d707fd Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Sat, 21 Sep 2013 11:34:37 +0200 Subject: [PATCH 17/24] Bug 918808 part 1 - Don't call strlen for int32 values in NumberValueToStringBuffer. r=njn --- js/src/jsnum.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 201d289513db..38c1d1fb2f40 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -650,11 +650,12 @@ js::Int32ToAtom(ExclusiveContext *cx, int32_t si); /* Returns a non-NULL pointer to inside cbuf. */ static char * -IntToCString(ToCStringBuf *cbuf, int i, int base = 10) +IntToCString(ToCStringBuf *cbuf, int i, size_t *len, int base = 10) { unsigned u = (i < 0) ? -i : i; - RangedPtr cp(cbuf->sbuf + cbuf->sbufSize - 1, cbuf->sbuf, cbuf->sbufSize); + RangedPtr cp(cbuf->sbuf + ToCStringBuf::sbufSize - 1, cbuf->sbuf, ToCStringBuf::sbufSize); + char *end = cp.get(); *cp = '\0'; /* Build the string from behind. */ @@ -681,6 +682,7 @@ IntToCString(ToCStringBuf *cbuf, int i, int base = 10) if (i < 0) *--cp = '-'; + *len = end - cp.get(); return cp.get(); } @@ -1309,8 +1311,9 @@ char * js::NumberToCString(JSContext *cx, ToCStringBuf *cbuf, double d, int base/* = 10*/) { int32_t i; + size_t len; return mozilla::DoubleIsInt32(d, &i) - ? IntToCString(cbuf, i, base) + ? IntToCString(cbuf, i, &len, base) : FracNumberToCString(cx, cbuf, d, base); } @@ -1350,7 +1353,8 @@ js_NumberToStringWithBase(ThreadSafeContext *cx, double d, int base) return str; } - numStr = IntToCString(&cbuf, i, base); + size_t len; + numStr = IntToCString(&cbuf, i, &len, base); JS_ASSERT(!cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize); } else { if (comp) { @@ -1466,21 +1470,23 @@ js::NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb) /* Convert to C-string. */ ToCStringBuf cbuf; const char *cstr; + size_t cstrlen; if (v.isInt32()) { - cstr = IntToCString(&cbuf, v.toInt32()); + cstr = IntToCString(&cbuf, v.toInt32(), &cstrlen); + JS_ASSERT(cstrlen == strlen(cstr)); } else { cstr = NumberToCString(cx, &cbuf, v.toDouble()); if (!cstr) { JS_ReportOutOfMemory(cx); return false; } + cstrlen = strlen(cstr); } /* * Inflate to jschar string. The input C-string characters are < 127, so * even if jschars are UTF-8, all chars should map to one jschar. */ - size_t cstrlen = strlen(cstr); JS_ASSERT(!cbuf.dbuf && cstrlen < cbuf.sbufSize); return sb.appendInflated(cstr, cstrlen); } From bb563a7928dbec330a5025112f34d80d882cf885 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Sat, 21 Sep 2013 11:36:33 +0200 Subject: [PATCH 18/24] Bug 918808 part 2 - Remove cruft from InflateStringToBuffer. r=luke --- js/src/gc/Statistics.cpp | 10 ++-------- js/src/jsapi.cpp | 20 +++++++++++++++++++- js/src/jsatom.cpp | 9 ++------- js/src/jsstr.cpp | 23 ----------------------- js/src/jsstr.h | 16 ++++++++-------- js/src/vm/StringBuffer.h | 5 +---- 6 files changed, 32 insertions(+), 51 deletions(-) diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index 365ae6fe3bb5..32eaca8aca2d 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -134,16 +134,10 @@ class gcstats::StatisticsSerializer return NULL; } - size_t outlen = nchars; - bool ok = InflateStringToBuffer(NULL, buf, nchars, out, &outlen); + InflateStringToBuffer(buf, nchars, out); js_free(buf); - if (!ok) { - oom_ = true; - js_free(out); - return NULL; - } - out[nchars] = 0; + out[nchars] = 0; return out; } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 76bd10b88b64..311ec8513177 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -5463,7 +5463,25 @@ JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, size_ { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); - return InflateStringToBuffer(cx, src, srclen, dst, dstlenp); + + if (!dst) { + *dstlenp = srclen; + return true; + } + + size_t dstlen = *dstlenp; + + if (srclen > dstlen) { + InflateStringToBuffer(src, dstlen, dst); + + AutoSuppressGC suppress(cx); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BUFFER_TOO_SMALL); + return false; + } + + InflateStringToBuffer(src, srclen, dst); + *dstlenp = srclen; + return true; } JS_PUBLIC_API(char *) diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index a90af045b0c0..23f45fda1ec3 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -388,13 +388,8 @@ js::AtomizeMaybeGC(ExclusiveContext *cx, const char *bytes, size_t length, Inter * js::AtomizeString rarely has to copy the temp string we make. */ jschar inflated[ATOMIZE_BUF_MAX]; - size_t inflatedLength = ATOMIZE_BUF_MAX - 1; - if (!InflateStringToBuffer(cx->maybeJSContext(), - bytes, length, inflated, &inflatedLength)) - { - return NULL; - } - return AtomizeAndCopyChars(cx, inflated, inflatedLength, ib); + InflateStringToBuffer(bytes, length, inflated); + return AtomizeAndCopyChars(cx, inflated, length, ib); } jschar *tbcharsZ = InflateString(cx, bytes, &length); diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index ebf91263222e..8f53a93e31e2 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -4164,29 +4164,6 @@ js::DeflateStringToBuffer(JSContext *maybecx, const jschar *src, size_t srclen, return true; } -bool -js::InflateStringToBuffer(JSContext *maybecx, const char *src, size_t srclen, - jschar *dst, size_t *dstlenp) -{ - if (dst) { - size_t dstlen = *dstlenp; - if (srclen > dstlen) { - for (size_t i = 0; i < dstlen; i++) - dst[i] = (unsigned char) src[i]; - if (maybecx) { - AutoSuppressGC suppress(maybecx); - JS_ReportErrorNumber(maybecx, js_GetErrorMessage, NULL, - JSMSG_BUFFER_TOO_SMALL); - } - return false; - } - for (size_t i = 0; i < srclen; i++) - dst[i] = (unsigned char) src[i]; - } - *dstlenp = srclen; - return true; -} - #define ____ false /* diff --git a/js/src/jsstr.h b/js/src/jsstr.h index c97e9e03060a..0a8cd225e00e 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -249,15 +249,15 @@ extern jschar * InflateString(ThreadSafeContext *cx, const char *bytes, size_t *length); /* - * Inflate bytes to JS chars in an existing buffer. 'chars' must be large - * enough for 'length' jschars. The buffer is NOT null-terminated. - * - * charsLength must be be initialized with the destination buffer size and, on - * return, will contain on return the number of copied chars. + * Inflate bytes to JS chars in an existing buffer. 'dst' must be large + * enough for 'srclen' jschars. The buffer is NOT null-terminated. */ -extern bool -InflateStringToBuffer(JSContext *maybecx, const char *bytes, size_t length, - jschar *chars, size_t *charsLength); +inline void +InflateStringToBuffer(const char *src, size_t srclen, jschar *dst) +{ + for (size_t i = 0; i < srclen; i++) + dst[i] = (unsigned char) src[i]; +} /* * Deflate JS chars to bytes into a buffer. 'bytes' must be large enough for diff --git a/js/src/vm/StringBuffer.h b/js/src/vm/StringBuffer.h index 84d339d39c8f..a37a513e3294 100644 --- a/js/src/vm/StringBuffer.h +++ b/js/src/vm/StringBuffer.h @@ -122,10 +122,7 @@ StringBuffer::appendInflated(const char *cstr, size_t cstrlen) size_t lengthBefore = length(); if (!cb.growByUninitialized(cstrlen)) return false; - mozilla::DebugOnly oldcstrlen = cstrlen; - mozilla::DebugOnly ok = InflateStringToBuffer(NULL, cstr, cstrlen, - begin() + lengthBefore, &cstrlen); - JS_ASSERT(ok && oldcstrlen == cstrlen); + InflateStringToBuffer(cstr, cstrlen, begin() + lengthBefore); return true; } From 01aa013cea7320a3b38a20b5eb89f817f1f14da4 Mon Sep 17 00:00:00 2001 From: Christian Holler Date: Fri, 20 Sep 2013 13:37:53 +0200 Subject: [PATCH 19/24] Bug 917242 - Symbolize ASan traces in automation. r=ted --- browser/installer/package-manifest.in | 4 ++++ build/automation.py.in | 6 ++++++ build/unix/Makefile.in | 8 ++++++++ build/unix/mozconfig.asan | 1 + configure.in | 2 ++ js/src/Makefile.in | 9 ++++++++- testing/runcppunittests.py | 6 ++++++ 7 files changed, 35 insertions(+), 1 deletion(-) diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 1e56158ac320..684351a11118 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -798,3 +798,7 @@ bin/libfreebl_32int64_3.so @BINPATH@/metro/defaults @BINPATH@/metro/modules #endif + +#ifdef MOZ_ASAN +@BINPATH@/llvm-symbolizer +#endif diff --git a/build/automation.py.in b/build/automation.py.in index 8f3984dd0de1..08763895331e 100644 --- a/build/automation.py.in +++ b/build/automation.py.in @@ -512,6 +512,12 @@ class Automation(object): # ASan specific environment stuff if self.IS_ASAN and (self.IS_LINUX or self.IS_MAC): + # Symbolizer support + llvmsym = os.path.join(xrePath, "llvm-symbolizer") + if os.path.isfile(llvmsym): + env["ASAN_SYMBOLIZER_PATH"] = llvmsym + self.log.info("INFO | automation.py | ASan using symbolizer at %s", llvmsym) + try: totalMemory = int(os.popen("free").readlines()[1].split()[1]) diff --git a/build/unix/Makefile.in b/build/unix/Makefile.in index 38cf1aedb979..1f1386f60539 100644 --- a/build/unix/Makefile.in +++ b/build/unix/Makefile.in @@ -6,6 +6,14 @@ SDK_BINARY = run-mozilla.sh +ifneq ($(LLVM_SYMBOLIZER),) +# Install a copy of the llvm-symbolizer binary to dist/bin, so it can +# be used for symbolizing traces for e.g. AddressSanitizer +LLVMSYM_EXECUTABLES=$(LLVM_SYMBOLIZER) +LLVMSYM_DEST=$(FINAL_TARGET) +INSTALL_TARGETS += LLVMSYM +endif + include $(topsrcdir)/config/rules.mk libs:: $(srcdir)/run-mozilla.sh diff --git a/build/unix/mozconfig.asan b/build/unix/mozconfig.asan index 02f01d9f7e17..8bd9e2d73136 100644 --- a/build/unix/mozconfig.asan +++ b/build/unix/mozconfig.asan @@ -3,6 +3,7 @@ # Use Clang as specified in manifest export CC="$topsrcdir/clang/bin/clang -fgnu89-inline" export CXX="$topsrcdir/clang/bin/clang++" +export LLVM_SYMBOLIZER="$topsrcdir/clang/bin/llvm-symbolizer" # Mandatory flags for ASan export ASANFLAGS="-fsanitize=address -Dxmalloc=myxmalloc -fPIC" diff --git a/configure.in b/configure.in index f982cd73f40a..0642272757d4 100644 --- a/configure.in +++ b/configure.in @@ -1212,8 +1212,10 @@ MOZ_ARG_ENABLE_BOOL(address-sanitizer, if test -n "$MOZ_ASAN"; then MOZ_LLVM_HACKS=1 AC_DEFINE(MOZ_ASAN) + MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer) fi AC_SUBST(MOZ_ASAN) +AC_SUBST(LLVM_SYMBOLIZER) dnl ======================================================== dnl = Enable hacks required for LLVM instrumentations diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 7f39b5d6b8c2..5bdfa5ffdd05 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -298,11 +298,18 @@ JITTEST_VALGRIND_FLAG = --valgrind endif endif +ifdef MOZ_ASAN +ifneq ($(LLVM_SYMBOLIZER),) +# Use the LLVM symbolizer when running jit-tests under ASan, if available +JITTEST_ASAN_ENV=ASAN_SYMBOLIZER_PATH='$(LLVM_SYMBOLIZER)' +endif +endif + check-style:: (cd $(srcdir) && $(PYTHON) config/check_spidermonkey_style.py); check-jit-test:: - $(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/jit-test/jit_test.py \ + $(JITTEST_ASAN_ENV) $(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/jit-test/jit_test.py \ --no-slow --no-progress --tinderbox --tbpl $(JITTEST_VALGRIND_FLAG) \ $(DIST)/bin/$(JS_SHELL_NAME)$(BIN_SUFFIX) diff --git a/testing/runcppunittests.py b/testing/runcppunittests.py index 66ad958f8e0e..3e8cd9b672d9 100644 --- a/testing/runcppunittests.py +++ b/testing/runcppunittests.py @@ -89,6 +89,12 @@ class CPPUnitTests(object): env[pathvar] = "%s%s%s" % (self.xre_path, os.pathsep, env[pathvar]) else: env[pathvar] = self.xre_path + + # Use llvm-symbolizer for ASan if available/required + llvmsym = os.path.join(self.xre_path, "llvm-symbolizer") + if os.path.isfile(llvmsym): + env["ASAN_SYMBOLIZER_PATH"] = llvmsym + return env def run_tests(self, programs, xre_path, symbols_path=None): From 80b1269ef5d994da511352dde32530a1d7c82308 Mon Sep 17 00:00:00 2001 From: "Nicholas D. Matsakis" Date: Sat, 21 Sep 2013 05:34:30 -0400 Subject: [PATCH 20/24] Bug 898349 - Correct indentation nits r=jandem --- js/src/jit/IonBuilder.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 4249403e41fe..0e10c2bb86e8 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -8305,19 +8305,15 @@ IonBuilder::getPropTryScalarPropOfTypedObject(bool *emitted, // Reading from an Uint32Array will result in a double for values // that don't fit in an int32. We have to bailout if this happens // and the instruction is not known to return a double. - bool allowDouble = - resultTypes->hasType(types::Type::DoubleType()); - MIRType knownType = - MIRTypeForTypedArrayRead(fieldTypeRepr->type(), allowDouble); + bool allowDouble = resultTypes->hasType(types::Type::DoubleType()); + MIRType knownType = MIRTypeForTypedArrayRead(fieldTypeRepr->type(), allowDouble); // Typed array offsets are expressed in units of the alignment, // and the binary data API guarantees all offsets are properly // aligned. So just do the divide. - MConstant *alignment = - MConstant::New(Int32Value(fieldTypeRepr->alignment())); + MConstant *alignment = MConstant::New(Int32Value(fieldTypeRepr->alignment())); current->add(alignment); - MDiv *scaledOffset = - MDiv::NewAsmJS(ownerOffset, alignment, MIRType_Int32); + MDiv *scaledOffset = MDiv::NewAsmJS(ownerOffset, alignment, MIRType_Int32); current->add(scaledOffset); MLoadTypedArrayElement *load = From a75f2ee68314813b11f7e7e64096624d817ac8b0 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sat, 21 Sep 2013 16:20:22 -0500 Subject: [PATCH 21/24] Bug 919129. Fix nsLayoutUtils::UpdateImageVisibilityForFrame so that it doesn't expand the image rect so much by replacing it with the scroll port rect. r=matspal --- layout/base/nsLayoutUtils.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index b6c7cb6c45a7..4b4abbaaaa96 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -5494,7 +5494,24 @@ nsLayoutUtils::UpdateImageVisibilityForFrame(nsIFrame* aImageFrame) visible = false; break; } - rect = sf->GetScrollPortRect(); + // Move transformedRect to be contained in the scrollport as best we can + // (it might not fit) to pretend that it was scrolled into view. + nsRect scrollPort = sf->GetScrollPortRect(); + if (transformedRect.XMost() > scrollPort.XMost()) { + transformedRect.x -= transformedRect.XMost() - scrollPort.XMost(); + } + if (transformedRect.x < scrollPort.x) { + transformedRect.x = scrollPort.x; + } + if (transformedRect.YMost() > scrollPort.YMost()) { + transformedRect.y -= transformedRect.YMost() - scrollPort.YMost(); + } + if (transformedRect.y < scrollPort.y) { + transformedRect.y = scrollPort.y; + } + transformedRect.width = std::min(transformedRect.width, scrollPort.width); + transformedRect.height = std::min(transformedRect.height, scrollPort.height); + rect = transformedRect; rectFrame = f; } nsIFrame* parent = f->GetParent(); From 14aab8b51f64fe8c970ce435c1797e2092cee0f2 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Sat, 21 Sep 2013 17:59:14 -0700 Subject: [PATCH 22/24] Backed out 6 changesets (bug 881804) for frequent Android reftest and jsreftest timeouts Backed out changeset 61972f55702b (bug 881804) Backed out changeset 0a24b77d4ffb (bug 881804) Backed out changeset 9fbcf4c3ab2b (bug 881804) Backed out changeset 2d35068ec2f5 (bug 881804) Backed out changeset 757790e5cd9f (bug 881804) Backed out changeset d8a172fa4b20 (bug 881804) --- browser/base/content/sanitize.js | 6 - content/base/src/nsScriptLoader.cpp | 5 - docshell/base/nsDocShell.cpp | 14 +- image/src/imgLoader.cpp | 7 - layout/style/Loader.cpp | 12 - layout/style/nsFontFaceLoader.cpp | 5 - modules/libpref/src/init/all.js | 18 - netwerk/base/public/moz.build | 2 - netwerk/base/public/nsINetworkSeer.idl | 164 -- .../base/public/nsINetworkSeerVerifier.idl | 31 - netwerk/base/public/nsISpeculativeConnect.idl | 28 - netwerk/base/src/Seer.cpp | 2146 ----------------- netwerk/base/src/Seer.h | 206 -- netwerk/base/src/moz.build | 1 - netwerk/build/nsNetCID.h | 11 - netwerk/build/nsNetModule.cpp | 4 - netwerk/protocol/http/nsHttpConnectionMgr.cpp | 87 +- netwerk/protocol/http/nsHttpConnectionMgr.h | 2 +- netwerk/test/unit/test_seer.js | 295 --- netwerk/test/unit/xpcshell.ini | 1 - toolkit/components/telemetry/Histograms.json | 120 - toolkit/forgetaboutsite/ForgetAboutSite.jsm | 6 - 22 files changed, 18 insertions(+), 3153 deletions(-) delete mode 100644 netwerk/base/public/nsINetworkSeer.idl delete mode 100644 netwerk/base/public/nsINetworkSeerVerifier.idl delete mode 100644 netwerk/base/src/Seer.cpp delete mode 100644 netwerk/base/src/Seer.h delete mode 100644 netwerk/test/unit/test_seer.js diff --git a/browser/base/content/sanitize.js b/browser/base/content/sanitize.js index aeb6087cb071..9906b6285fc3 100644 --- a/browser/base/content/sanitize.js +++ b/browser/base/content/sanitize.js @@ -227,12 +227,6 @@ Sanitizer.prototype = { prefs.clearUserPref("general.open_location.last_url"); } catch (e) { } - - try { - var seer = Components.classes["@mozilla.org/network/seer;1"] - .getService(Components.interfaces.nsINetworkSeer); - seer.reset(); - } catch (e) { } }, get canClear() diff --git a/content/base/src/nsScriptLoader.cpp b/content/base/src/nsScriptLoader.cpp index 3b26af68f12e..9f4434968f5b 100644 --- a/content/base/src/nsScriptLoader.cpp +++ b/content/base/src/nsScriptLoader.cpp @@ -47,7 +47,6 @@ #include "nsCrossSiteListenerProxy.h" #include "nsSandboxFlags.h" #include "nsContentTypeParser.h" -#include "nsINetworkSeer.h" #include "mozilla/CORSMode.h" #include "mozilla/Attributes.h" @@ -330,10 +329,6 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, httpChannel->SetReferrer(mDocument->GetDocumentURI()); } - nsCOMPtr loadContext(do_QueryInterface(docshell)); - mozilla::net::SeerLearn(aRequest->mURI, mDocument->GetDocumentURI(), - nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, loadContext); - nsCOMPtr loader; rv = NS_NewStreamLoader(getter_AddRefs(loader), this); NS_ENSURE_SUCCESS(rv, rv); diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 05cd159db5b5..d2845d5bd06c 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -117,7 +117,6 @@ #include "nsIFaviconService.h" #include "mozIAsyncFavicons.h" #endif -#include "nsINetworkSeer.h" // Editor-related #include "nsIEditingSession.h" @@ -6978,12 +6977,9 @@ nsDocShell::EndPageLoad(nsIWebProgress * aProgress, aStatus = NS_ERROR_OFFLINE; DisplayLoadError(aStatus, url, nullptr, aChannel); } - } // if we have a host - else if (url && NS_SUCCEEDED(aStatus)) { - mozilla::net::SeerLearnRedirect(url, aChannel, this); - } + } // if we have a host - return NS_OK; + return NS_OK; } @@ -9382,9 +9378,6 @@ nsDocShell::InternalLoad(nsIURI * aURI, else srcdoc = NullString(); - mozilla::net::SeerPredict(aURI, nullptr, nsINetworkSeer::PREDICT_LOAD, - this, nullptr); - nsCOMPtr req; rv = DoURILoad(aURI, aReferrer, !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER), @@ -12466,9 +12459,6 @@ nsDocShell::OnOverLink(nsIContent* aContent, rv = textToSubURI->UnEscapeURIForUI(charset, spec, uStr); NS_ENSURE_SUCCESS(rv, rv); - mozilla::net::SeerPredict(aURI, mCurrentURI, nsINetworkSeer::PREDICT_LINK, - this, nullptr); - if (browserChrome2) { nsCOMPtr element = do_QueryInterface(aContent); rv = browserChrome2->SetStatusWithContext(nsIWebBrowserChrome::STATUS_LINK, diff --git a/image/src/imgLoader.cpp b/image/src/imgLoader.cpp index 5137a51dd2e2..8c3e3abb4025 100644 --- a/image/src/imgLoader.cpp +++ b/image/src/imgLoader.cpp @@ -28,7 +28,6 @@ #include "nsIFileURL.h" #include "nsCRT.h" #include "nsIDocument.h" -#include "nsINetworkSeer.h" #include "nsIApplicationCache.h" #include "nsIApplicationCacheContainer.h" @@ -1244,9 +1243,6 @@ bool imgLoader::ValidateRequestWithNewChannel(imgRequest *request, // Add the proxy without notifying hvc->AddProxy(proxy); - mozilla::net::SeerLearn(aURI, aInitialDocumentURI, - nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, aLoadGroup); - rv = newChannel->AsyncOpen(listener, nullptr); if (NS_SUCCEEDED(rv)) NS_ADDREF(*aProxyRequest = req.get()); @@ -1723,9 +1719,6 @@ nsresult imgLoader::LoadImage(nsIURI *aURI, PR_LOG(GetImgLog(), PR_LOG_DEBUG, ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n", this)); - mozilla::net::SeerLearn(aURI, aInitialDocumentURI, - nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, aLoadGroup); - nsresult openRes = newChannel->AsyncOpen(listener, nullptr); if (NS_FAILED(openRes)) { diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index d386f4a25af0..9e9a232e4fd4 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -47,7 +47,6 @@ #include "nsGkAtoms.h" #include "nsIThreadInternal.h" #include "nsCrossSiteListenerProxy.h" -#include "nsINetworkSeer.h" #ifdef MOZ_XUL #include "nsXULPrototypeCache.h" @@ -1434,12 +1433,6 @@ Loader::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState) return rv; } - if (mDocument) { - mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(), - nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, - mDocument); - } - // Just load it nsCOMPtr stream; nsCOMPtr channel; @@ -1613,11 +1606,6 @@ Loader::LoadSheet(SheetLoadData* aLoadData, StyleSheetState aSheetState) channelListener = streamLoader; } - if (mDocument) { - mozilla::net::SeerLearn(aLoadData->mURI, mDocument->GetDocumentURI(), - nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, mDocument); - } - rv = channel->AsyncOpen(channelListener, nullptr); #ifdef DEBUG diff --git a/layout/style/nsFontFaceLoader.cpp b/layout/style/nsFontFaceLoader.cpp index 6cb23717e9e8..06ffa5e3e6f3 100644 --- a/layout/style/nsFontFaceLoader.cpp +++ b/layout/style/nsFontFaceLoader.cpp @@ -29,7 +29,6 @@ #include "nsIContentSecurityPolicy.h" #include "nsIDocShell.h" #include "nsIWebNavigation.h" -#include "nsINetworkSeer.h" #include "nsIConsoleService.h" @@ -375,10 +374,6 @@ nsUserFontSet::StartLoad(gfxMixedFontFamily *aFamily, rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader); NS_ENSURE_SUCCESS(rv, rv); - nsIDocument *document = ps->GetDocument(); - mozilla::net::SeerLearn(aFontFaceSrc->mURI, document->GetDocumentURI(), - nsINetworkSeer::LEARN_LOAD_SUBRESOURCE, loadGroup); - bool inherits = false; rv = NS_URIChainHasFlags(aFontFaceSrc->mURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 79ae4ef07220..a6abc7a7b786 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1348,24 +1348,6 @@ pref("network.dir.format", 2); // enables the prefetch service (i.e., prefetching of URLs). pref("network.prefetch-next", true); -// enables the predictive service -pref("network.seer.enabled", true); -pref("network.seer.enable-hover-on-ssl", false); -pref("network.seer.page-degradation.day", 0); -pref("network.seer.page-degradation.week", 5); -pref("network.seer.page-degradation.month", 10); -pref("network.seer.page-degradation.year", 25); -pref("network.seer.page-degradation.max", 50); -pref("network.seer.subresource-degradation.day", 1); -pref("network.seer.subresource-degradation.week", 10); -pref("network.seer.subresource-degradation.month", 25); -pref("network.seer.subresource-degradation.year", 50); -pref("network.seer.subresource-degradation.max", 100); -pref("network.seer.preconnect-min-confidence", 90); -pref("network.seer.preresolve-min-confidence", 60); -pref("network.seer.redirect-likely-confidence", 75); -pref("network.seer.max-queue-size", 50); - // The following prefs pertain to the negotiate-auth extension (see bug 17578), // which provides transparent Kerberos or NTLM authentication using the SPNEGO diff --git a/netwerk/base/public/moz.build b/netwerk/base/public/moz.build index 84f5b7127dbd..dc7467efc96a 100644 --- a/netwerk/base/public/moz.build +++ b/netwerk/base/public/moz.build @@ -57,8 +57,6 @@ XPIDL_SOURCES += [ 'nsINetUtil.idl', 'nsINetworkLinkService.idl', 'nsINetworkProperties.idl', - 'nsINetworkSeer.idl', - 'nsINetworkSeerVerifier.idl', 'nsIParentChannel.idl', 'nsIParentRedirectingChannel.idl', 'nsIPermission.idl', diff --git a/netwerk/base/public/nsINetworkSeer.idl b/netwerk/base/public/nsINetworkSeer.idl deleted file mode 100644 index 5f7f09605398..000000000000 --- a/netwerk/base/public/nsINetworkSeer.idl +++ /dev/null @@ -1,164 +0,0 @@ -/* vim: set ts=2 sts=2 et sw=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/. */ - -#include "nsISupports.idl" - -interface nsIURI; -interface nsILoadContext; -interface nsINetworkSeerVerifier; - -typedef unsigned long SeerPredictReason; -typedef unsigned long SeerLearnReason; - -/** - * nsINetworkSeer - learn about pages users visit, and allow us to take - * predictive actions upon future visits. - * NOTE: nsINetworkSeer should only be used on the main thread - */ -[scriptable, uuid(884a39a0-a3ed-4855-826a-fabb73ae878d)] -interface nsINetworkSeer : nsISupports -{ - /** - * Prediction reasons - * - * PREDICT_LINK - we are being asked to take predictive action because - * the user is hovering over a link. - * - * PREDICT_LOAD - we are being asked to take predictive action because - * the user has initiated a pageload. - * - * PREDICT_STARTUP - we are being asked to take predictive action - * because the browser is starting up. - */ - const SeerPredictReason PREDICT_LINK = 0; - const SeerPredictReason PREDICT_LOAD = 1; - const SeerPredictReason PREDICT_STARTUP = 2; - - /** - * Start taking predictive actions - * - * Calling this will cause the seer to (possibly) start - * taking actions such as DNS prefetch and/or TCP preconnect based on - * (1) the host name that we are given, and (2) the reason we are being - * asked to take actions. - * - * @param targetURI - The URI we are being asked to take actions based on. - * @param sourceURI - The URI that is currently loaded. This is so we can - * avoid doing predictive actions for link hover on an HTTPS page (for - * example). - * @param reason - The reason we are being asked to take actions. Can be - * any of the PREDICT_* values above. - * In the case of PREDICT_LINK, targetURI should be the URI of the link - * that is being hovered over, and sourceURI should be the URI of the page - * on which the link appears. - * In the case of PREDICT_LOAD, targetURI should be the URI of the page that - * is being loaded and sourceURI should be null. - * In the case of PREDICT_STARTUP, both targetURI and sourceURI should be - * null. - * @param loadContext - The nsILoadContext of the page load we are predicting - * about. - * @param verifier - An nsINetworkSeerVerifier used in testing to ensure we're - * predicting the way we expect to. Not necessary (or desired) for normal - * operation. - */ - void predict(in nsIURI targetURI, - in nsIURI sourceURI, - in SeerPredictReason reason, - in nsILoadContext loadContext, - in nsINetworkSeerVerifier verifier); - - - /* - * Reasons we are learning something - * - * LEARN_LOAD_TOPLEVEL - we are learning about the toplevel resource of a - * pageload (NOTE: this should ONLY be used by tests) - * - * LEARN_LOAD_SUBRESOURCE - we are learning a subresource from a pageload - * - * LEARN_LOAD_REDIRECT - we are learning about the re-direct of a URI - * - * LEARN_STARTUP - we are learning about a page loaded during startup - */ - const SeerLearnReason LEARN_LOAD_TOPLEVEL = 0; - const SeerLearnReason LEARN_LOAD_SUBRESOURCE = 1; - const SeerLearnReason LEARN_LOAD_REDIRECT = 2; - const SeerLearnReason LEARN_STARTUP = 3; - - /** - * Add to our compendium of knowledge - * - * This adds to our prediction database to make things (hopefully) - * smarter next time we predict something. - * - * @param targetURI - The URI that was loaded that we are keeping track of. - * @param sourceURI - The URI that caused targetURI to be loaded (for page - * loads). This means the DOCUMENT URI. - * @param reason - The reason we are learning this bit of knowledge. - * Reason can be any of the LEARN_* values. - * In the case of LEARN_LOAD_SUBRESOURCE, targetURI should be the URI of a - * subresource of a page, and sourceURI should be the top-level URI. - * In the case of LEARN_LOAD_REDIRECT, targetURI is the NEW URI of a - * top-level resource that was redirected to, and sourceURI is the - * ORIGINAL URI of said top-level resource. - * In the case of LEARN_STARTUP, targetURI should be the URI of a page - * that was loaded immediately after browser startup, and sourceURI should - * be null. - * @param loadContext - The nsILoadContext for the page load that we are - * learning about. - */ - void learn(in nsIURI targetURI, - in nsIURI sourceURI, - in SeerLearnReason reason, - in nsILoadContext loadContext); - - /** - * Clear out all our learned knowledge - * - * This removes everything from our database so that any predictions begun - * after this completes will start from a blank slate. - */ - void reset(); -}; - -%{C++ -// Wrapper functions to make use of the seer easier and less invasive -class nsIChannel; -class nsIDocument; -class nsILoadContext; -class nsILoadGroup; -class nsINetworkSeerVerifier; - -namespace mozilla { -namespace net { - -nsresult SeerPredict(nsIURI *targetURI, - nsIURI *sourceURI, - SeerPredictReason reason, - nsILoadContext *loadContext, - nsINetworkSeerVerifier *verifier); - -nsresult SeerLearn(nsIURI *targetURI, - nsIURI *sourceURI, - SeerLearnReason reason, - nsILoadContext *loadContext); - -nsresult SeerLearn(nsIURI *targetURI, - nsIURI *sourceURI, - SeerLearnReason reason, - nsILoadGroup *loadGroup); - -nsresult SeerLearn(nsIURI *targetURI, - nsIURI *sourceURI, - SeerLearnReason reason, - nsIDocument *document); - -nsresult SeerLearnRedirect(nsIURI *targetURI, - nsIChannel *channel, - nsILoadContext *loadContext); - -} // mozilla::net -} // mozilla -%} diff --git a/netwerk/base/public/nsINetworkSeerVerifier.idl b/netwerk/base/public/nsINetworkSeerVerifier.idl deleted file mode 100644 index b8fabe360e3f..000000000000 --- a/netwerk/base/public/nsINetworkSeerVerifier.idl +++ /dev/null @@ -1,31 +0,0 @@ -/* vim: set ts=2 sts=2 et sw=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/. */ - -/** - * nsINetworkSeerVerifier - used for testing the network seer to ensure it - * does what we expect it to do. - */ - -#include "nsISupports.idl" - -interface nsIURI; - -[scriptable, uuid(ea273653-43a8-4632-8b30-4032e0918e8b)] -interface nsINetworkSeerVerifier : nsISupports -{ - /** - * Callback for when we do a predictive preconnect - * - * @param uri - The URI that was preconnected to - */ - void onPredictPreconnect(in nsIURI uri); - - /** - * Callback for when we do a predictive DNS lookup - * - * @param uri - The URI that was looked up - */ - void onPredictDNS(in nsIURI uri); -}; diff --git a/netwerk/base/public/nsISpeculativeConnect.idl b/netwerk/base/public/nsISpeculativeConnect.idl index f4bcd3cf4039..f77628f45cbe 100644 --- a/netwerk/base/public/nsISpeculativeConnect.idl +++ b/netwerk/base/public/nsISpeculativeConnect.idl @@ -30,31 +30,3 @@ interface nsISpeculativeConnect : nsISupports }; -/** - * This is used to override the default values for various values (documented - * inline) to determine whether or not to actually make a speculative - * connection. - */ -[builtinclass, uuid(2b6d6fb6-ab28-4f4c-af84-bfdbb7866d72)] -interface nsISpeculativeConnectionOverrider : nsISupports -{ - /** - * Used to determine the maximum number of unused speculative connections - * we will have open for a host at any one time - */ - [infallible] readonly attribute unsigned long parallelSpeculativeConnectLimit; - - /** - * Used to loosen the restrictions nsHttpConnectionMgr::RestrictConnections - * to allow more speculative connections when we're unsure if a host will - * connect via SPDY or not. - */ - [infallible] readonly attribute boolean ignorePossibleSpdyConnections; - - /** - * Used to determine if we will ignore the existence of any currently idle - * connections when we decide whether or not to make a speculative - * connection. - */ - [infallible] readonly attribute boolean ignoreIdle; -}; diff --git a/netwerk/base/src/Seer.cpp b/netwerk/base/src/Seer.cpp deleted file mode 100644 index 861d0f12e4ef..000000000000 --- a/netwerk/base/src/Seer.cpp +++ /dev/null @@ -1,2146 +0,0 @@ -/* vim: set ts=2 sts=2 et sw=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/. */ - -#include - -#include "Seer.h" - -#include "nsAppDirectoryServiceDefs.h" -#include "nsICancelable.h" -#include "nsIChannel.h" -#include "nsIDNSListener.h" -#include "nsIDNSService.h" -#include "nsIDocument.h" -#include "nsIFile.h" -#include "nsILoadContext.h" -#include "nsILoadGroup.h" -#include "nsINetworkSeerVerifier.h" -#include "nsIObserverService.h" -#include "nsIPrefBranch.h" -#include "nsIPrefService.h" -#include "nsISpeculativeConnect.h" -#include "nsIURI.h" -#include "nsNetUtil.h" -#include "nsServiceManagerUtils.h" -#include "nsTArray.h" -#include "nsThreadUtils.h" -#include "prlog.h" - -#include "mozIStorageConnection.h" -#include "mozIStorageService.h" -#include "mozIStorageStatement.h" -#include "mozStorageHelper.h" - -#include "mozilla/Preferences.h" -#include "mozilla/storage.h" -#include "mozilla/Telemetry.h" - -using namespace mozilla; - -namespace mozilla { -namespace net { - -#define RETURN_IF_FAILED(_rv) \ - do { \ - if (NS_FAILED(_rv)) { \ - return; \ - } \ - } while (0) - -const char SEER_ENABLED_PREF[] = "network.seer.enabled"; -const char SEER_SSL_HOVER_PREF[] = "network.seer.enable-hover-on-ssl"; - -const char SEER_PAGE_DELTA_DAY_PREF[] = "network.seer.page-degradation.day"; -const int SEER_PAGE_DELTA_DAY_DEFAULT = 0; -const char SEER_PAGE_DELTA_WEEK_PREF[] = "network.seer.page-degradation.week"; -const int SEER_PAGE_DELTA_WEEK_DEFAULT = 5; -const char SEER_PAGE_DELTA_MONTH_PREF[] = "network.seer.page-degradation.month"; -const int SEER_PAGE_DELTA_MONTH_DEFAULT = 10; -const char SEER_PAGE_DELTA_YEAR_PREF[] = "network.seer.page-degradation.year"; -const int SEER_PAGE_DELTA_YEAR_DEFAULT = 25; -const char SEER_PAGE_DELTA_MAX_PREF[] = "network.seer.page-degradation.max"; -const int SEER_PAGE_DELTA_MAX_DEFAULT = 50; -const char SEER_SUB_DELTA_DAY_PREF[] = - "network.seer.subresource-degradation.day"; -const int SEER_SUB_DELTA_DAY_DEFAULT = 1; -const char SEER_SUB_DELTA_WEEK_PREF[] = - "network.seer.subresource-degradation.week"; -const int SEER_SUB_DELTA_WEEK_DEFAULT = 10; -const char SEER_SUB_DELTA_MONTH_PREF[] = - "network.seer.subresource-degradation.month"; -const int SEER_SUB_DELTA_MONTH_DEFAULT = 25; -const char SEER_SUB_DELTA_YEAR_PREF[] = - "network.seer.subresource-degradation.year"; -const int SEER_SUB_DELTA_YEAR_DEFAULT = 50; -const char SEER_SUB_DELTA_MAX_PREF[] = - "network.seer.subresource-degradation.max"; -const int SEER_SUB_DELTA_MAX_DEFAULT = 100; - -const char SEER_PRECONNECT_MIN_PREF[] = - "network.seer.preconnect-min-confidence"; -const int PRECONNECT_MIN_DEFAULT = 90; -const char SEER_PRERESOLVE_MIN_PREF[] = - "network.seer.preresolve-min-confidence"; -const int PRERESOLVE_MIN_DEFAULT = 60; -const char SEER_REDIRECT_LIKELY_PREF[] = - "network.seer.redirect-likely-confidence"; -const int REDIRECT_LIKELY_DEFAULT = 75; - -const char SEER_MAX_QUEUE_SIZE_PREF[] = "network.seer.max-queue-size"; -const uint32_t SEER_MAX_QUEUE_SIZE_DEFAULT = 50; - -// All these time values are in usec -const long long ONE_DAY = 86400LL * 1000000LL; -const long long ONE_WEEK = 7LL * ONE_DAY; -const long long ONE_MONTH = 30LL * ONE_DAY; -const long long ONE_YEAR = 365LL * ONE_DAY; - -const long STARTUP_WINDOW = 5L * 60L * 1000000L; // 5min - -// Version for the database schema -static const int32_t SEER_SCHEMA_VERSION = 1; - -struct SeerTelemetryAccumulators { - Telemetry::AutoCounter mPredictAttempts; - Telemetry::AutoCounter mLearnAttempts; - Telemetry::AutoCounter mPredictFullQueue; - Telemetry::AutoCounter mLearnFullQueue; - Telemetry::AutoCounter mTotalPredictions; - Telemetry::AutoCounter mTotalPreconnects; - Telemetry::AutoCounter mTotalPreresolves; - Telemetry::AutoCounter mPredictionsCalculated; -}; - -// Are you ready for the fun part? Because here comes the fun part. The seer, -// which will do awesome stuff as you browse to make your browsing experience -// faster. - -static Seer *gSeer = nullptr; - -#if defined(PR_LOGGING) -static PRLogModuleInfo *gSeerLog = nullptr; -#define SEER_LOG(args) PR_LOG(gSeerLog, 4, args) -#else -#define SEER_LOG(args) -#endif - -NS_IMPL_ISUPPORTS5(Seer, - nsINetworkSeer, - nsIObserver, - nsIDNSListener, - nsISpeculativeConnectionOverrider, - nsIInterfaceRequestor) - -Seer::Seer() - :mInitialized(false) - ,mEnabled(true) - ,mEnableHoverOnSSL(false) - ,mPageDegradationDay(SEER_PAGE_DELTA_DAY_DEFAULT) - ,mPageDegradationWeek(SEER_PAGE_DELTA_WEEK_DEFAULT) - ,mPageDegradationMonth(SEER_PAGE_DELTA_MONTH_DEFAULT) - ,mPageDegradationYear(SEER_PAGE_DELTA_YEAR_DEFAULT) - ,mPageDegradationMax(SEER_PAGE_DELTA_MAX_DEFAULT) - ,mSubresourceDegradationDay(SEER_SUB_DELTA_DAY_DEFAULT) - ,mSubresourceDegradationWeek(SEER_SUB_DELTA_WEEK_DEFAULT) - ,mSubresourceDegradationMonth(SEER_SUB_DELTA_MONTH_DEFAULT) - ,mSubresourceDegradationYear(SEER_SUB_DELTA_YEAR_DEFAULT) - ,mSubresourceDegradationMax(SEER_SUB_DELTA_MAX_DEFAULT) - ,mPreconnectMinConfidence(PRECONNECT_MIN_DEFAULT) - ,mPreresolveMinConfidence(PRERESOLVE_MIN_DEFAULT) - ,mRedirectLikelyConfidence(REDIRECT_LIKELY_DEFAULT) - ,mMaxQueueSize(SEER_MAX_QUEUE_SIZE_DEFAULT) - ,mStatements(mDB) - ,mLastStartupTime(0) - ,mStartupCount(0) - ,mQueueSize(0) - ,mQueueSizeLock("Seer.mQueueSizeLock") -{ -#if defined(PR_LOGGING) - gSeerLog = PR_NewLogModule("NetworkSeer"); -#endif - - MOZ_ASSERT(!gSeer, "multiple Seer instances!"); - gSeer = this; -} - -Seer::~Seer() -{ - if (mInitialized) - Shutdown(); - - RemoveObserver(); - - gSeer = nullptr; -} - -// Seer::nsIObserver - -nsresult -Seer::InstallObserver() -{ - MOZ_ASSERT(NS_IsMainThread(), "Installing observer off main thread"); - - nsresult rv = NS_OK; - nsCOMPtr obs = - mozilla::services::GetObserverService(); - if (!obs) { - return NS_ERROR_NOT_AVAILABLE; - } - - rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); - if (!prefs) { - return NS_ERROR_NOT_AVAILABLE; - } - - Preferences::AddBoolVarCache(&mEnabled, SEER_ENABLED_PREF, true); - Preferences::AddBoolVarCache(&mEnableHoverOnSSL, SEER_SSL_HOVER_PREF, false); - Preferences::AddIntVarCache(&mPageDegradationDay, SEER_PAGE_DELTA_DAY_PREF, - SEER_PAGE_DELTA_DAY_DEFAULT); - Preferences::AddIntVarCache(&mPageDegradationWeek, SEER_PAGE_DELTA_WEEK_PREF, - SEER_PAGE_DELTA_WEEK_DEFAULT); - Preferences::AddIntVarCache(&mPageDegradationMonth, - SEER_PAGE_DELTA_MONTH_PREF, - SEER_PAGE_DELTA_MONTH_DEFAULT); - Preferences::AddIntVarCache(&mPageDegradationYear, SEER_PAGE_DELTA_YEAR_PREF, - SEER_PAGE_DELTA_YEAR_DEFAULT); - Preferences::AddIntVarCache(&mPageDegradationMax, SEER_PAGE_DELTA_MAX_PREF, - SEER_PAGE_DELTA_MAX_DEFAULT); - - Preferences::AddIntVarCache(&mSubresourceDegradationDay, - SEER_SUB_DELTA_DAY_PREF, - SEER_SUB_DELTA_DAY_DEFAULT); - Preferences::AddIntVarCache(&mSubresourceDegradationWeek, - SEER_SUB_DELTA_WEEK_PREF, - SEER_SUB_DELTA_WEEK_DEFAULT); - Preferences::AddIntVarCache(&mSubresourceDegradationMonth, - SEER_SUB_DELTA_MONTH_PREF, - SEER_SUB_DELTA_MONTH_DEFAULT); - Preferences::AddIntVarCache(&mSubresourceDegradationYear, - SEER_SUB_DELTA_YEAR_PREF, - SEER_SUB_DELTA_YEAR_DEFAULT); - Preferences::AddIntVarCache(&mSubresourceDegradationMax, - SEER_SUB_DELTA_MAX_PREF, - SEER_SUB_DELTA_MAX_DEFAULT); - - Preferences::AddIntVarCache(&mPreconnectMinConfidence, - SEER_PRECONNECT_MIN_PREF, - PRECONNECT_MIN_DEFAULT); - Preferences::AddIntVarCache(&mPreresolveMinConfidence, - SEER_PRERESOLVE_MIN_PREF, - PRERESOLVE_MIN_DEFAULT); - Preferences::AddIntVarCache(&mRedirectLikelyConfidence, - SEER_REDIRECT_LIKELY_PREF, - REDIRECT_LIKELY_DEFAULT); - - Preferences::AddIntVarCache(&mMaxQueueSize, SEER_MAX_QUEUE_SIZE_PREF, - SEER_MAX_QUEUE_SIZE_DEFAULT); - - return rv; -} - -void -Seer::RemoveObserver() -{ - MOZ_ASSERT(NS_IsMainThread(), "Removing observer off main thread"); - - nsCOMPtr obs = - mozilla::services::GetObserverService(); - if (obs) { - obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); - } -} - -NS_IMETHODIMP -Seer::Observe(nsISupports *subject, const char *topic, - const PRUnichar *data_unicode) -{ - nsresult rv = NS_OK; - - if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic)) { - gSeer->Shutdown(); - } - - return rv; -} - -// Seer::nsIDNSListener - -NS_IMETHODIMP -Seer::OnLookupComplete(nsICancelable *request, nsIDNSRecord *rec, - nsresult status) -{ - return NS_OK; -} - -// Seer::nsISpeculativeConnectionOverrider - -NS_IMETHODIMP -Seer::GetIgnoreIdle(bool *ignoreIdle) -{ - *ignoreIdle = true; - return NS_OK; -} - -NS_IMETHODIMP -Seer::GetIgnorePossibleSpdyConnections(bool *ignorePossibleSpdyConnections) -{ - *ignorePossibleSpdyConnections = true; - return NS_OK; -} - -NS_IMETHODIMP -Seer::GetParallelSpeculativeConnectLimit( - uint32_t *parallelSpeculativeConnectLimit) -{ - *parallelSpeculativeConnectLimit = 6; - return NS_OK; -} - -// Seer::nsIInterfaceRequestor - -NS_IMETHODIMP -Seer::GetInterface(const nsIID &iid, void **result) -{ - return QueryInterface(iid, result); -} - -// Seer::nsINetworkSeer - -nsresult -Seer::Init() -{ - if (!NS_IsMainThread()) { - MOZ_ASSERT(false, "Seer::Init called off the main thread!"); - return NS_ERROR_UNEXPECTED; - } - - nsresult rv = NS_OK; - - mStartupTime = PR_Now(); - - mAccumulators = new SeerTelemetryAccumulators(); - - rv = InstallObserver(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = NS_NewNamedThread("Network Seer", getter_AddRefs(mIOThread)); - NS_ENSURE_SUCCESS(rv, rv); - - mSpeculativeService = do_GetService("@mozilla.org/network/io-service;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - mStorageService = do_GetService("@mozilla.org/storage/service;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, - getter_AddRefs(mDBFile)); - NS_ENSURE_SUCCESS(rv, rv); - rv = mDBFile->AppendNative(NS_LITERAL_CSTRING("seer.sqlite")); - NS_ENSURE_SUCCESS(rv, rv); - - mInitialized = true; - - return rv; -} - -// Make sure that our sqlite storage is all set up with all the tables we need -// to do the work. It isn't the end of the world if this fails, since this is -// all an optimization, anyway. - -nsresult -Seer::EnsureInitStorage() -{ - MOZ_ASSERT(!NS_IsMainThread(), "Initializing seer storage on main thread"); - - if (mDB) { - return NS_OK; - } - - nsresult rv; - - rv = mStorageService->OpenDatabase(mDBFile, getter_AddRefs(mDB)); - if (NS_FAILED(rv)) { - // Retry once by trashing the file and trying to open again. If this fails, - // we can just bail, and hope for better luck next time. - rv = mDBFile->Remove(false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mStorageService->OpenDatabase(mDBFile, getter_AddRefs(mDB)); - NS_ENSURE_SUCCESS(rv, rv); - } - - mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF;")); - mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA foreign_keys = ON;")); - - // A table to make sure we're working with the database layout we expect - rv = mDB->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_seer_version (\n" - " version INTEGER NOT NULL\n" - ");\n")); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr stmt; - rv = mDB->CreateStatement( - NS_LITERAL_CSTRING("SELECT version FROM moz_seer_version;\n"), - getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - bool hasRows; - rv = stmt->ExecuteStep(&hasRows); - NS_ENSURE_SUCCESS(rv, rv); - if (hasRows) { - int32_t currentVersion; - rv = stmt->GetInt32(0, ¤tVersion); - NS_ENSURE_SUCCESS(rv, rv); - - // This is what we do while we only have one schema version. Later, we'll - // have to change this to actually upgrade things as appropriate. - MOZ_ASSERT(currentVersion == SEER_SCHEMA_VERSION, - "Invalid seer schema version!"); - if (currentVersion != SEER_SCHEMA_VERSION) { - return NS_ERROR_UNEXPECTED; - } - } else { - stmt = nullptr; - rv = mDB->CreateStatement( - NS_LITERAL_CSTRING("INSERT INTO moz_seer_version (version) VALUES " - "(:seer_version);"), - getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("seer_version"), - SEER_SCHEMA_VERSION); - NS_ENSURE_SUCCESS(rv, rv); - - stmt->Execute(); - } - - stmt = nullptr; - - // This table keeps track of the hosts we've seen at the top level of a - // pageload so we can map them to hosts used for subresources of a pageload. - rv = mDB->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_hosts (\n" - " id INTEGER PRIMARY KEY AUTOINCREMENT,\n" - " origin TEXT NOT NULL,\n" - " loads INTEGER DEFAULT 0,\n" - " last_load INTEGER DEFAULT 0\n" - ");\n")); - NS_ENSURE_SUCCESS(rv, rv); - - // And this is the table that keeps track of the hosts for subresources of a - // pageload. - rv = mDB->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_subhosts (\n" - " id INTEGER PRIMARY KEY AUTOINCREMENT,\n" - " hid INTEGER NOT NULL,\n" - " origin TEXT NOT NULL,\n" - " hits INTEGER DEFAULT 0,\n" - " last_hit INTEGER DEFAULT 0,\n" - " FOREIGN KEY(hid) REFERENCES moz_hosts(id) ON DELETE CASCADE\n" - ");\n")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDB->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS subhost_hid_origin_index " - "ON moz_subhosts (hid, origin);")); - NS_ENSURE_SUCCESS(rv, rv); - - // Table to keep track of how many times we've started up, and when the last - // time was. - rv = mDB->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_startups (\n" - " startups INTEGER,\n" - " last_startup INTEGER\n" - ");\n")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDB->CreateStatement( - NS_LITERAL_CSTRING("SELECT startups, last_startup FROM moz_startups;\n"), - getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - // We'll go ahead and keep track of our startup count here, since we can - // (mostly) equate "the service was created and asked to do stuff" with - // "the browser was started up". - rv = stmt->ExecuteStep(&hasRows); - NS_ENSURE_SUCCESS(rv, rv); - if (hasRows) { - // We've started up before. Update our startup statistics - stmt->GetInt32(0, &mStartupCount); - stmt->GetInt64(1, &mLastStartupTime); - - // This finalizes the statement - stmt = nullptr; - - rv = mDB->CreateStatement( - NS_LITERAL_CSTRING("UPDATE moz_startups SET startups = :startup_count " - "last_startup = :startup_time;\n"), - getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("startup_count"), - mStartupCount + 1); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startup_time"), - mStartupTime); - NS_ENSURE_SUCCESS(rv, rv); - - stmt->Execute(); - } else { - // This is our first startup, so let's go ahead and mark it as such - mStartupCount = 1; - - rv = mDB->CreateStatement( - NS_LITERAL_CSTRING("INSERT INTO moz_startups (startups, last_startup) " - "VALUES (1, :startup_time);\n"), - getter_AddRefs(stmt)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startup_time"), - mStartupTime); - NS_ENSURE_SUCCESS(rv, rv); - - stmt->Execute(); - } - - // This finalizes the statement - stmt = nullptr; - - // This table lists URIs loaded at startup, along with how many startups - // they've been loaded during, and when the last time was. - rv = mDB->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_startup_pages (\n" - " id INTEGER PRIMARY KEY AUTOINCREMENT,\n" - " uri TEXT NOT NULL,\n" - " hits INTEGER DEFAULT 0,\n" - " last_hit INTEGER DEFAULT 0\n" - ");\n")); - NS_ENSURE_SUCCESS(rv, rv); - - // This table is similar to moz_hosts above, but uses full URIs instead of - // hosts so that we can get more specific predictions for URIs that people - // visit often (such as their email or social network home pages). - rv = mDB->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_pages (\n" - " id integer PRIMARY KEY AUTOINCREMENT,\n" - " uri TEXT NOT NULL,\n" - " loads INTEGER DEFAULT 0,\n" - " last_load INTEGER DEFAULT 0\n" - ");\n")); - NS_ENSURE_SUCCESS(rv, rv); - - // This table is similar to moz_subhosts above, but is instead related to - // moz_pages for finer granularity. - rv = mDB->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_subresources (\n" - " id integer PRIMARY KEY AUTOINCREMENT,\n" - " pid INTEGER NOT NULL,\n" - " uri TEXT NOT NULL,\n" - " hits INTEGER DEFAULT 0,\n" - " last_hit INTEGER DEFAULT 0,\n" - " FOREIGN KEY(pid) REFERENCES moz_pages(id) ON DELETE CASCADE\n" - ");\n")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDB->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS subresource_pid_uri_index " - "ON moz_subresources (pid, uri);")); - NS_ENSURE_SUCCESS(rv, rv); - - // This table keeps track of URIs and what they end up finally redirecting to - // so we can handle redirects in a sane fashion, as well. - rv = mDB->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_redirects (\n" - " id integer PRIMARY KEY AUTOINCREMENT,\n" - " pid integer NOT NULL,\n" - " uri TEXT NOT NULL,\n" - " origin TEXT NOT NULL,\n" - " hits INTEGER DEFAULT 0,\n" - " last_hit INTEGER DEFAULT 0,\n" - " FOREIGN KEY(pid) REFERENCES moz_pages(id)\n" - ");\n")); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -class SeerThreadShutdownRunner : public nsRunnable -{ -public: - SeerThreadShutdownRunner(nsIThread *ioThread) - :mIOThread(ioThread) - { } - - NS_IMETHODIMP Run() MOZ_OVERRIDE - { - MOZ_ASSERT(NS_IsMainThread(), "Shut down seer io thread off main thread"); - mIOThread->Shutdown(); - return NS_OK; - } - -private: - nsCOMPtr mIOThread; -}; - -class SeerDBShutdownRunner : public nsRunnable -{ -public: - SeerDBShutdownRunner(nsIThread *ioThread, nsINetworkSeer *seer) - :mIOThread(ioThread) - { - mSeer = new nsMainThreadPtrHolder(seer); - } - - NS_IMETHODIMP Run() MOZ_OVERRIDE - { - MOZ_ASSERT(!NS_IsMainThread(), "Shutting down DB on main thread"); - - gSeer->mStatements.FinalizeStatements(); - gSeer->mDB->Close(); - gSeer->mDB = nullptr; - - nsRefPtr runner = - new SeerThreadShutdownRunner(mIOThread); - NS_DispatchToMainThread(runner); - - return NS_OK; - } - -private: - nsCOMPtr mIOThread; - - // Death grip to keep seer alive while we cleanly close its DB connection - nsMainThreadPtrHandle mSeer; -}; - -void -Seer::Shutdown() -{ - if (!NS_IsMainThread()) { - MOZ_ASSERT(false, "Seer::Shutdown called off the main thread!"); - return; - } - - mInitialized = false; - - if (mIOThread) { - nsCOMPtr ioThread; - mIOThread.swap(ioThread); - - if (mDB) { - nsRefPtr runner = - new SeerDBShutdownRunner(ioThread, this); - ioThread->Dispatch(runner, NS_DISPATCH_NORMAL); - } else { - nsRefPtr runner = - new SeerThreadShutdownRunner(ioThread); - NS_DispatchToMainThread(runner); - } - } -} - -nsresult -Seer::Create(nsISupports *aOuter, const nsIID& aIID, - void **aResult) -{ - nsresult rv; - - if (aOuter != nullptr) { - return NS_ERROR_NO_AGGREGATION; - } - - nsRefPtr svc = new Seer(); - - rv = svc->Init(); - if (NS_FAILED(rv)) { - SEER_LOG(("Failed to initialize seer, seer will be a noop")); - } - - // We treat init failure the same as the service being disabled, since this - // is all an optimization anyway. No need to freak people out. That's why we - // gladly continue on QI'ing here. - rv = svc->QueryInterface(aIID, aResult); - - return rv; -} - -// Get the full origin (scheme, host, port) out of a URI (maybe should be part -// of nsIURI instead?) -static void -ExtractOrigin(nsIURI *uri, nsAutoCString &s) -{ - s.Truncate(); - - nsAutoCString scheme; - nsresult rv = uri->GetScheme(scheme); - RETURN_IF_FAILED(rv); - - nsAutoCString host; - rv = uri->GetAsciiHost(host); - RETURN_IF_FAILED(rv); - - int32_t port; - rv = uri->GetPort(&port); - RETURN_IF_FAILED(rv); - - s.Assign(scheme); - s.AppendLiteral("://"); - s.Append(host); - if (port != -1) { - s.AppendLiteral(":"); - s.AppendInt(port); - } -} - -// An event to do the work for a prediction that needs to hit the sqlite -// database. These events should be created on the main thread, and run on -// the seer thread. -class SeerPredictionEvent : public nsRunnable -{ -public: - SeerPredictionEvent(nsIURI *targetURI, nsIURI *sourceURI, - SeerPredictReason reason, - nsINetworkSeerVerifier *verifier) - :mReason(reason) - { - MOZ_ASSERT(NS_IsMainThread(), "Creating prediction event off main thread"); - - mEnqueueTime = TimeStamp::Now(); - - if (verifier) { - mVerifier = new nsMainThreadPtrHolder(verifier); - } - if (targetURI) { - targetURI->GetAsciiSpec(mTargetURI.spec); - ExtractOrigin(targetURI, mTargetURI.origin); - } - if (sourceURI) { - sourceURI->GetAsciiSpec(mSourceURI.spec); - ExtractOrigin(sourceURI, mSourceURI.origin); - } - } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - MOZ_ASSERT(!NS_IsMainThread(), "Running prediction event on main thread"); - - Telemetry::AccumulateTimeDelta(Telemetry::SEER_PREDICT_WAIT_TIME, - mEnqueueTime); - - TimeStamp startTime = TimeStamp::Now(); - - nsresult rv = NS_OK; - - switch (mReason) { - case nsINetworkSeer::PREDICT_LOAD: - gSeer->PredictForPageload(mTargetURI, mVerifier, 0, mEnqueueTime); - break; - case nsINetworkSeer::PREDICT_STARTUP: - gSeer->PredictForStartup(mVerifier, mEnqueueTime); - break; - default: - MOZ_ASSERT(false, "Got unexpected value for predict reason"); - rv = NS_ERROR_UNEXPECTED; - } - - gSeer->FreeSpaceInQueue(); - - Telemetry::AccumulateTimeDelta(Telemetry::SEER_PREDICT_WORK_TIME, - startTime); - - return rv; - } - -private: - Seer::UriInfo mTargetURI; - Seer::UriInfo mSourceURI; - SeerPredictReason mReason; - SeerVerifierHandle mVerifier; - TimeStamp mEnqueueTime; -}; - -// Predicting for a link is easy, and doesn't require the round-trip to the -// seer thread and back to the main thread, since we don't have to hit the db -// for that. -void -Seer::PredictForLink(nsIURI *targetURI, nsIURI *sourceURI, - nsINetworkSeerVerifier *verifier) -{ - MOZ_ASSERT(NS_IsMainThread(), "Predicting for link off main thread"); - - if (!mSpeculativeService) { - return; - } - - if (!mEnableHoverOnSSL) { - bool isSSL = false; - sourceURI->SchemeIs("https", &isSSL); - if (isSSL) { - // We don't want to predict from an HTTPS page, to avoid info leakage - SEER_LOG(("Not predicting for link hover - on an SSL page")); - return; - } - } - - mSpeculativeService->SpeculativeConnect(targetURI, this); - if (verifier) { - verifier->OnPredictPreconnect(targetURI); - } -} - -// This runnable runs on the main thread, and is responsible for actually -// firing off predictive actions (such as TCP/TLS preconnects and DNS lookups) -class SeerPredictionRunner : public nsRunnable -{ -public: - SeerPredictionRunner(SeerVerifierHandle &verifier, TimeStamp predictStartTime) - :mVerifier(verifier) - ,mPredictStartTime(predictStartTime) - { } - - void AddPreconnect(const nsACString &uri) - { - mPreconnects.AppendElement(uri); - } - - void AddPreresolve(const nsACString &uri) - { - mPreresolves.AppendElement(uri); - } - - bool HasWork() const - { - return !(mPreconnects.IsEmpty() && mPreresolves.IsEmpty()); - } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - MOZ_ASSERT(NS_IsMainThread(), "Running prediction off main thread"); - - Telemetry::AccumulateTimeDelta(Telemetry::SEER_PREDICT_TIME_TO_ACTION, - mPredictStartTime); - - uint32_t len, i; - - len = mPreconnects.Length(); - for (i = 0; i < len; ++i) { - nsCOMPtr uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), mPreconnects[i]); - if (NS_FAILED(rv)) { - continue; - } - - ++gSeer->mAccumulators->mTotalPredictions; - ++gSeer->mAccumulators->mTotalPreconnects; - gSeer->mSpeculativeService->SpeculativeConnect(uri, gSeer); - if (mVerifier) { - mVerifier->OnPredictPreconnect(uri); - } - } - - len = mPreresolves.Length(); - nsCOMPtr mainThread = do_GetMainThread(); - for (i = 0; i < len; ++i) { - nsCOMPtr uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), mPreresolves[i]); - if (NS_FAILED(rv)) { - continue; - } - - ++gSeer->mAccumulators->mTotalPredictions; - ++gSeer->mAccumulators->mTotalPreresolves; - nsAutoCString hostname; - uri->GetAsciiHost(hostname); - nsCOMPtr tmpCancelable; - gSeer->mDnsService->AsyncResolve(hostname, - (nsIDNSService::RESOLVE_PRIORITY_MEDIUM | - nsIDNSService::RESOLVE_SPECULATE), - gSeer, mainThread, - getter_AddRefs(tmpCancelable)); - if (mVerifier) { - mVerifier->OnPredictDNS(uri); - } - } - - mPreconnects.Clear(); - mPreresolves.Clear(); - - return NS_OK; - } - -private: - nsTArray mPreconnects; - nsTArray mPreresolves; - SeerVerifierHandle mVerifier; - TimeStamp mPredictStartTime; -}; - -// This calculates how much to degrade our confidence in our data based on -// the last time this top-level resource was loaded. This "global degradation" -// applies to *all* subresources we have associated with the top-level -// resource. This will be in addition to any reduction in confidence we have -// associated with a particular subresource. -int -Seer::CalculateGlobalDegradation(PRTime now, PRTime lastLoad) -{ - int globalDegradation; - PRTime delta = now - lastLoad; - if (delta < ONE_DAY) { - globalDegradation = mPageDegradationDay; - } else if (delta < ONE_WEEK) { - globalDegradation = mPageDegradationWeek; - } else if (delta < ONE_MONTH) { - globalDegradation = mPageDegradationMonth; - } else if (delta < ONE_YEAR) { - globalDegradation = mPageDegradationYear; - } else { - globalDegradation = mPageDegradationMax; - } - - Telemetry::Accumulate(Telemetry::SEER_GLOBAL_DEGRADATION, globalDegradation); - return globalDegradation; -} - -// This calculates our overall confidence that a particular subresource will be -// loaded as part of a top-level load. -// @param baseConfidence - the basic confidence we have for this subresource, -// which is the percentage of time this top-level load -// loads the subresource in question -// @param lastHit - the timestamp of the last time we loaded this subresource as -// part of this top-level load -// @param lastPossible - the timestamp of the last time we performed this -// top-level load -// @param globalDegradation - the degradation for this top-level load as -// determined by CalculateGlobalDegradation -int -Seer::CalculateConfidence(int baseConfidence, PRTime lastHit, - PRTime lastPossible, int globalDegradation) -{ - ++mAccumulators->mPredictionsCalculated; - - int maxConfidence = 100; - int confidenceDegradation = 0; - - if (lastHit < lastPossible) { - // We didn't load this subresource the last time this top-level load was - // performed, so let's not bother preconnecting (at the very least). - maxConfidence = mPreconnectMinConfidence - 1; - - // Now calculate how much we want to degrade our confidence based on how - // long it's been between the last time we did this top-level load and the - // last time this top-level load included this subresource. - PRTime delta = lastPossible - lastHit; - if (delta == 0) { - confidenceDegradation = 0; - } else if (delta < ONE_DAY) { - confidenceDegradation = mSubresourceDegradationDay; - } else if (delta < ONE_WEEK) { - confidenceDegradation = mSubresourceDegradationWeek; - } else if (delta < ONE_MONTH) { - confidenceDegradation = mSubresourceDegradationMonth; - } else if (delta < ONE_YEAR) { - confidenceDegradation = mSubresourceDegradationYear; - } else { - confidenceDegradation = mSubresourceDegradationMax; - maxConfidence = 0; - } - } - - // Calculate our confidence and clamp it to between 0 and maxConfidence - // (<= 100) - int confidence = baseConfidence - confidenceDegradation - globalDegradation; - confidence = std::max(confidence, 0); - confidence = std::min(confidence, maxConfidence); - - Telemetry::Accumulate(Telemetry::SEER_BASE_CONFIDENCE, baseConfidence); - Telemetry::Accumulate(Telemetry::SEER_SUBRESOURCE_DEGRADATION, - confidenceDegradation); - Telemetry::Accumulate(Telemetry::SEER_CONFIDENCE, confidence); - return confidence; -} - -// (Maybe) adds a predictive action to the prediction runner, based on our -// calculated confidence for the subresource in question. -void -Seer::SetupPrediction(int confidence, const nsACString &uri, - SeerPredictionRunner *runner) -{ - if (confidence >= mPreconnectMinConfidence) { - runner->AddPreconnect(uri); - } else if (confidence >= mPreresolveMinConfidence) { - runner->AddPreresolve(uri); - } -} - -// This gets the data about the top-level load from our database, either from -// the pages table (which is specific to a particular URI), or from the hosts -// table (which is for a particular origin). -bool -Seer::LookupTopLevel(QueryType queryType, const nsACString &key, - TopLevelInfo &info) -{ - MOZ_ASSERT(!NS_IsMainThread(), "LookupTopLevel called on main thread."); - - nsCOMPtr stmt; - if (queryType == QUERY_PAGE) { - stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("SELECT id, loads, last_load FROM moz_pages WHERE " - "uri = :key;")); - } else { - stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("SELECT id, loads, last_load FROM moz_hosts WHERE " - "origin = :key;")); - } - NS_ENSURE_TRUE(stmt, false); - mozStorageStatementScoper scope(stmt); - - nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("key"), key); - NS_ENSURE_SUCCESS(rv, false); - - bool hasRows; - rv = stmt->ExecuteStep(&hasRows); - NS_ENSURE_SUCCESS(rv, false); - - if (!hasRows) { - return false; - } - - rv = stmt->GetInt32(0, &info.id); - NS_ENSURE_SUCCESS(rv, false); - - rv = stmt->GetInt32(1, &info.loadCount); - NS_ENSURE_SUCCESS(rv, false); - - rv = stmt->GetInt64(2, &info.lastLoad); - NS_ENSURE_SUCCESS(rv, false); - - return true; -} - -// Insert data about either a top-level page or a top-level origin into -// the database. -void -Seer::AddTopLevel(QueryType queryType, const nsACString &key, PRTime now) -{ - MOZ_ASSERT(!NS_IsMainThread(), "AddTopLevel called on main thread."); - - nsCOMPtr stmt; - if (queryType == QUERY_PAGE) { - stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("INSERT INTO moz_pages (uri, loads, last_load) " - "VALUES (:key, 1, :now);")); - } else { - stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("INSERT INTO moz_hosts (origin, loads, last_load) " - "VALUES (:key, 1, :now);")); - } - if (!stmt) { - return; - } - mozStorageStatementScoper scope(stmt); - - // Loading a page implicitly makes the seer learn about the page, - // so since we don't have it already, let's add it. - nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("key"), key); - RETURN_IF_FAILED(rv); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("now"), now); - RETURN_IF_FAILED(rv); - - rv = stmt->Execute(); -} - -// Update data about either a top-level page or a top-level origin in the -// database. -void -Seer::UpdateTopLevel(QueryType queryType, const TopLevelInfo &info, PRTime now) -{ - MOZ_ASSERT(!NS_IsMainThread(), "UpdateTopLevel called on main thread."); - - nsCOMPtr stmt; - if (queryType == QUERY_PAGE) { - stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("UPDATE moz_pages SET loads = :load_count, " - "last_load = :now WHERE id = :id;")); - } else { - stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("UPDATE moz_hosts SET loads = :load_count, " - "last_load = :now WHERE id = :id;")); - } - if (!stmt) { - return; - } - mozStorageStatementScoper scope(stmt); - - // First, let's update the page in the database, since loading a page - // implicitly learns about the page. - nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("load_count"), - info.loadCount + 1); - RETURN_IF_FAILED(rv); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("now"), now); - RETURN_IF_FAILED(rv); - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("id"), info.id); - RETURN_IF_FAILED(rv); - - rv = stmt->Execute(); -} - -// Tries to predict for a top-level load (either page-based or origin-based). -// Returns false if it failed to predict at all, true if it did some sort of -// prediction. -// @param queryType - whether to predict based on page or origin -// @param info - the db info about the top-level resource -bool -Seer::TryPredict(QueryType queryType, const TopLevelInfo &info, PRTime now, - SeerVerifierHandle &verifier, TimeStamp &predictStartTime) -{ - MOZ_ASSERT(!NS_IsMainThread(), "TryPredict called on main thread."); - - int globalDegradation = CalculateGlobalDegradation(now, info.lastLoad); - - // Now let's look up the subresources we know about for this page - nsCOMPtr stmt; - if (queryType == QUERY_PAGE) { - stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("SELECT uri, hits, last_hit FROM moz_subresources " - "WHERE pid = :id;")); - } else { - stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("SELECT origin, hits, last_hit FROM moz_subhosts " - "WHERE hid = :id;")); - } - NS_ENSURE_TRUE(stmt, false); - mozStorageStatementScoper scope(stmt); - - nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("id"), info.id); - NS_ENSURE_SUCCESS(rv, false); - - bool hasRows; - rv = stmt->ExecuteStep(&hasRows); - if (NS_FAILED(rv) || !hasRows) { - return false; - } - - nsRefPtr runner = - new SeerPredictionRunner(verifier, predictStartTime); - - while (hasRows) { - int32_t hitCount; - PRTime lastHit; - nsAutoCString subresource; - int baseConfidence, confidence; - - // We use goto nextrow here instead of just failling, because we want - // to do some sort of prediction if at all possible. Of course, it's - // probably unlikely that subsequent rows will succeed if one fails, but - // it's worth a shot. - - rv = stmt->GetUTF8String(0, subresource); - if NS_FAILED(rv) { - goto nextrow; - } - - rv = stmt->GetInt32(1, &hitCount); - if (NS_FAILED(rv)) { - goto nextrow; - } - - rv = stmt->GetInt64(2, &lastHit); - if (NS_FAILED(rv)) { - goto nextrow; - } - - baseConfidence = (hitCount * 100) / info.loadCount; - confidence = CalculateConfidence(baseConfidence, lastHit, info.lastLoad, - globalDegradation); - SetupPrediction(confidence, subresource, runner); - -nextrow: - rv = stmt->ExecuteStep(&hasRows); - NS_ENSURE_SUCCESS(rv, false); - } - - bool predicted = false; - - if (runner->HasWork()) { - NS_DispatchToMainThread(runner); - predicted = true; - } - - return predicted; -} - -// Find out if a top-level page is likely to redirect. -bool -Seer::WouldRedirect(const TopLevelInfo &info, PRTime now, UriInfo &newUri) -{ - MOZ_ASSERT(!NS_IsMainThread(), "WouldRedirect called on main thread."); - - nsCOMPtr stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("SELECT uri, origin, hits, last_hit " - "FROM moz_redirects WHERE pid = :id;")); - NS_ENSURE_TRUE(stmt, false); - mozStorageStatementScoper scope(stmt); - - nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("id"), info.id); - NS_ENSURE_SUCCESS(rv, false); - - bool hasRows; - rv = stmt->ExecuteStep(&hasRows); - if (NS_FAILED(rv) || !hasRows) { - return false; - } - - rv = stmt->GetUTF8String(0, newUri.spec); - NS_ENSURE_SUCCESS(rv, false); - - rv = stmt->GetUTF8String(1, newUri.origin); - NS_ENSURE_SUCCESS(rv, false); - - int32_t hitCount; - rv = stmt->GetInt32(2, &hitCount); - NS_ENSURE_SUCCESS(rv, false); - - PRTime lastHit; - rv = stmt->GetInt64(3, &lastHit); - NS_ENSURE_SUCCESS(rv, false); - - int globalDegradation = CalculateGlobalDegradation(now, info.lastLoad); - int baseConfidence = (hitCount * 100) / info.loadCount; - int confidence = CalculateConfidence(baseConfidence, lastHit, info.lastLoad, - globalDegradation); - - if (confidence > mRedirectLikelyConfidence) { - return true; - } - - return false; -} - -// This will add a page to our list of startup pages if it's being loaded -// before our startup window has expired. -void -Seer::MaybeLearnForStartup(const UriInfo &uri, const PRTime now) -{ - MOZ_ASSERT(!NS_IsMainThread(), "MaybeLearnForStartup called on main thread."); - - if ((now - mStartupTime) < STARTUP_WINDOW) { - LearnForStartup(uri); - } -} - -const int MAX_PAGELOAD_DEPTH = 10; - -// This is the driver for prediction based on a new pageload. -void -Seer::PredictForPageload(const UriInfo &uri, SeerVerifierHandle &verifier, - int stackCount, TimeStamp &predictStartTime) -{ - MOZ_ASSERT(!NS_IsMainThread(), "PredictForPageload called on main thread."); - - if (stackCount > MAX_PAGELOAD_DEPTH) { - SEER_LOG(("Too deep into pageload prediction")); - return; - } - - if (NS_FAILED(EnsureInitStorage())) { - return; - } - - PRTime now = PR_Now(); - - MaybeLearnForStartup(uri, now); - - TopLevelInfo pageInfo; - TopLevelInfo originInfo; - bool havePage = LookupTopLevel(QUERY_PAGE, uri.spec, pageInfo); - bool haveOrigin = LookupTopLevel(QUERY_ORIGIN, uri.origin, originInfo); - - if (!havePage) { - AddTopLevel(QUERY_PAGE, uri.spec, now); - } else { - UpdateTopLevel(QUERY_PAGE, pageInfo, now); - } - - if (!haveOrigin) { - AddTopLevel(QUERY_ORIGIN, uri.origin, now); - } else { - UpdateTopLevel(QUERY_ORIGIN, originInfo, now); - } - - UriInfo newUri; - if (havePage && WouldRedirect(pageInfo, now, newUri)) { - nsRefPtr runner = - new SeerPredictionRunner(verifier, predictStartTime); - runner->AddPreconnect(newUri.spec); - NS_DispatchToMainThread(runner); - PredictForPageload(newUri, verifier, stackCount + 1, predictStartTime); - return; - } - - bool predicted = false; - - // We always try to be as specific as possible in our predictions, so try - // to predict based on the full URI before we fall back to the origin. - if (havePage) { - predicted = TryPredict(QUERY_PAGE, pageInfo, now, verifier, - predictStartTime); - } - - if (!predicted && haveOrigin) { - predicted = TryPredict(QUERY_ORIGIN, originInfo, now, verifier, - predictStartTime); - } - - if (!predicted) { - Telemetry::AccumulateTimeDelta(Telemetry::SEER_PREDICT_TIME_TO_INACTION, - predictStartTime); - } -} - -// This is the driver for predicting at browser startup time based on pages that -// have previously been loaded close to startup. -void -Seer::PredictForStartup(SeerVerifierHandle &verifier, - TimeStamp &predictStartTime) -{ - MOZ_ASSERT(!NS_IsMainThread(), "PredictForStartup called on main thread"); - - if (NS_FAILED(EnsureInitStorage())) { - return; - } - - nsCOMPtr stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("SELECT uri, hits, last_hit FROM moz_startup_pages;")); - if (!stmt) { - return; - } - mozStorageStatementScoper scope(stmt); - nsresult rv; - bool hasRows; - - nsRefPtr runner = - new SeerPredictionRunner(verifier, predictStartTime); - - rv = stmt->ExecuteStep(&hasRows); - RETURN_IF_FAILED(rv); - - while (hasRows) { - nsAutoCString uri; - int32_t hitCount; - PRTime lastHit; - int baseConfidence, confidence; - - // We use goto nextrow here instead of just failling, because we want - // to do some sort of prediction if at all possible. Of course, it's - // probably unlikely that subsequent rows will succeed if one fails, but - // it's worth a shot. - - rv = stmt->GetUTF8String(0, uri); - if (NS_FAILED(rv)) { - goto nextrow; - } - - rv = stmt->GetInt32(1, &hitCount); - if (NS_FAILED(rv)) { - goto nextrow; - } - - rv = stmt->GetInt64(2, &lastHit); - if (NS_FAILED(rv)) { - goto nextrow; - } - - baseConfidence = (hitCount * 100) / mStartupCount; - confidence = CalculateConfidence(baseConfidence, lastHit, - mLastStartupTime, 0); - SetupPrediction(confidence, uri, runner); - -nextrow: - rv = stmt->ExecuteStep(&hasRows); - RETURN_IF_FAILED(rv); - } - - if (runner->HasWork()) { - NS_DispatchToMainThread(runner); - } else { - Telemetry::AccumulateTimeDelta(Telemetry::SEER_PREDICT_TIME_TO_INACTION, - predictStartTime); - } -} - -// All URIs we get passed *must* be http or https if they're not null. This -// helps ensure that. -static bool -IsNullOrHttp(nsIURI *uri) -{ - if (!uri) { - return true; - } - - bool isHTTP = false; - uri->SchemeIs("http", &isHTTP); - if (!isHTTP) { - uri->SchemeIs("https", &isHTTP); - } - - return isHTTP; -} - -nsresult -Seer::ReserveSpaceInQueue() -{ - MutexAutoLock lock(mQueueSizeLock); - - if (mQueueSize >= mMaxQueueSize) { - SEER_LOG(("Not enqueuing event - queue too large")); - return NS_ERROR_NOT_AVAILABLE; - } - - mQueueSize++; - return NS_OK; -} - -void -Seer::FreeSpaceInQueue() -{ - MutexAutoLock lock(mQueueSizeLock); - MOZ_ASSERT(mQueueSize > 0, "unexpected mQueueSize"); - mQueueSize--; -} - -// Called from the main thread to initiate predictive actions -NS_IMETHODIMP -Seer::Predict(nsIURI *targetURI, nsIURI *sourceURI, SeerPredictReason reason, - nsILoadContext *loadContext, nsINetworkSeerVerifier *verifier) -{ - MOZ_ASSERT(NS_IsMainThread(), - "Seer interface methods must be called on the main thread"); - - if (!mInitialized) { - return NS_ERROR_NOT_AVAILABLE; - } - - if (!mEnabled) { - return NS_ERROR_NOT_AVAILABLE; - } - - if (loadContext && loadContext->UsePrivateBrowsing()) { - // Don't want to do anything in PB mode - return NS_OK; - } - - if (!IsNullOrHttp(targetURI) || !IsNullOrHttp(sourceURI)) { - // Nothing we can do for non-HTTP[S] schemes - return NS_OK; - } - - // Ensure we've been given the appropriate arguments for the kind of - // prediction we're being asked to do - switch (reason) { - case nsINetworkSeer::PREDICT_LINK: - if (!targetURI || !sourceURI) { - return NS_ERROR_INVALID_ARG; - } - // Link hover is a special case where we can predict without hitting the - // db, so let's go ahead and fire off that prediction here. - PredictForLink(targetURI, sourceURI, verifier); - return NS_OK; - case nsINetworkSeer::PREDICT_LOAD: - if (!targetURI || sourceURI) { - return NS_ERROR_INVALID_ARG; - } - break; - case nsINetworkSeer::PREDICT_STARTUP: - if (targetURI || sourceURI) { - return NS_ERROR_INVALID_ARG; - } - break; - default: - return NS_ERROR_INVALID_ARG; - } - - ++mAccumulators->mPredictAttempts; - nsresult rv = ReserveSpaceInQueue(); - if (NS_FAILED(rv)) { - ++mAccumulators->mPredictFullQueue; - return NS_ERROR_NOT_AVAILABLE; - } - - nsRefPtr event = new SeerPredictionEvent(targetURI, - sourceURI, - reason, - verifier); - return mIOThread->Dispatch(event, NS_DISPATCH_NORMAL); -} - -// A runnable for updating our information in the database. This must always -// be dispatched to the seer thread. -class SeerLearnEvent : public nsRunnable -{ -public: - SeerLearnEvent(nsIURI *targetURI, nsIURI *sourceURI, SeerLearnReason reason) - :mReason(reason) - { - MOZ_ASSERT(NS_IsMainThread(), "Creating learn event off main thread"); - - mEnqueueTime = TimeStamp::Now(); - - targetURI->GetAsciiSpec(mTargetURI.spec); - ExtractOrigin(targetURI, mTargetURI.origin); - if (sourceURI) { - sourceURI->GetAsciiSpec(mSourceURI.spec); - ExtractOrigin(sourceURI, mSourceURI.origin); - } - } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - MOZ_ASSERT(!NS_IsMainThread(), "Running learn off main thread"); - - nsresult rv = NS_OK; - - Telemetry::AccumulateTimeDelta(Telemetry::SEER_LEARN_WAIT_TIME, - mEnqueueTime); - - TimeStamp startTime = TimeStamp::Now(); - - switch (mReason) { - case nsINetworkSeer::LEARN_LOAD_TOPLEVEL: - gSeer->LearnForToplevel(mTargetURI); - break; - case nsINetworkSeer::LEARN_LOAD_REDIRECT: - gSeer->LearnForRedirect(mTargetURI, mSourceURI); - break; - case nsINetworkSeer::LEARN_LOAD_SUBRESOURCE: - gSeer->LearnForSubresource(mTargetURI, mSourceURI); - break; - case nsINetworkSeer::LEARN_STARTUP: - gSeer->LearnForStartup(mTargetURI); - break; - default: - MOZ_ASSERT(false, "Got unexpected value for learn reason"); - rv = NS_ERROR_UNEXPECTED; - } - - gSeer->FreeSpaceInQueue(); - - Telemetry::AccumulateTimeDelta(Telemetry::SEER_LEARN_WORK_TIME, startTime); - - return rv; - } -private: - Seer::UriInfo mTargetURI; - Seer::UriInfo mSourceURI; - SeerLearnReason mReason; - TimeStamp mEnqueueTime; -}; - -void -Seer::LearnForToplevel(const UriInfo &uri) -{ - MOZ_ASSERT(!NS_IsMainThread(), "LearnForToplevel called on main thread."); - - if (NS_FAILED(EnsureInitStorage())) { - return; - } - - PRTime now = PR_Now(); - - MaybeLearnForStartup(uri, now); - - TopLevelInfo pageInfo; - TopLevelInfo originInfo; - bool havePage = LookupTopLevel(QUERY_PAGE, uri.spec, pageInfo); - bool haveOrigin = LookupTopLevel(QUERY_ORIGIN, uri.origin, originInfo); - - if (!havePage) { - AddTopLevel(QUERY_PAGE, uri.spec, now); - } else { - UpdateTopLevel(QUERY_PAGE, pageInfo, now); - } - - if (!haveOrigin) { - AddTopLevel(QUERY_ORIGIN, uri.origin, now); - } else { - UpdateTopLevel(QUERY_ORIGIN, originInfo, now); - } -} - -// Queries to look up information about a *specific* subresource associated -// with a *specific* top-level load. -bool -Seer::LookupSubresource(QueryType queryType, const int32_t parentId, - const nsACString &key, SubresourceInfo &info) -{ - MOZ_ASSERT(!NS_IsMainThread(), "LookupSubresource called on main thread."); - - nsCOMPtr stmt; - if (queryType == QUERY_PAGE) { - stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("SELECT id, hits, last_hit FROM moz_subresources " - "WHERE pid = :parent_id AND uri = :key;")); - } else { - stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("SELECT id, hits, last_hit FROM moz_subhosts WHERE " - "hid = :parent_id AND origin = :key;")); - } - NS_ENSURE_TRUE(stmt, false); - mozStorageStatementScoper scope(stmt); - - nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("parent_id"), - parentId); - NS_ENSURE_SUCCESS(rv, false); - - rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("key"), key); - NS_ENSURE_SUCCESS(rv, false); - - bool hasRows; - rv = stmt->ExecuteStep(&hasRows); - NS_ENSURE_SUCCESS(rv, false); - if (!hasRows) { - return false; - } - - rv = stmt->GetInt32(0, &info.id); - NS_ENSURE_SUCCESS(rv, false); - - rv = stmt->GetInt32(1, &info.hitCount); - NS_ENSURE_SUCCESS(rv, false); - - rv = stmt->GetInt64(2, &info.lastHit); - NS_ENSURE_SUCCESS(rv, false); - - return true; -} - -// Add information about a new subresource associated with a top-level load. -void -Seer::AddSubresource(QueryType queryType, const int32_t parentId, - const nsACString &key, const PRTime now) -{ - MOZ_ASSERT(!NS_IsMainThread(), "AddSubresource called on main thread."); - - nsCOMPtr stmt; - if (queryType == QUERY_PAGE) { - stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("INSERT INTO moz_subresources " - "(pid, uri, hits, last_hit) VALUES " - "(:parent_id, :key, 1, :now);")); - } else { - stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("INSERT INTO moz_subhosts " - "(hid, origin, hits, last_hit) VALUES " - "(:parent_id, :key, 1, :now);")); - } - if (!stmt) { - return; - } - mozStorageStatementScoper scope(stmt); - - nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("parent_id"), - parentId); - RETURN_IF_FAILED(rv); - - rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("key"), key); - RETURN_IF_FAILED(rv); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("now"), now); - RETURN_IF_FAILED(rv); - - rv = stmt->Execute(); -} - -// Update the information about a particular subresource associated with a -// top-level load -void -Seer::UpdateSubresource(QueryType queryType, const SubresourceInfo &info, - const PRTime now) -{ - MOZ_ASSERT(!NS_IsMainThread(), "UpdateSubresource called on main thread."); - - nsCOMPtr stmt; - if (queryType == QUERY_PAGE) { - stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("UPDATE moz_subresources SET hits = :hit_count, " - "last_hit = :now WHERE id = :id;")); - } else { - stmt = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("UPDATE moz_subhosts SET hits = :hit_count, " - "last_hit = :now WHERE id = :id;")); - } - if (!stmt) { - return; - } - mozStorageStatementScoper scope(stmt); - - nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("hit_count"), - info.hitCount + 1); - RETURN_IF_FAILED(rv); - - rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("now"), now); - RETURN_IF_FAILED(rv); - - rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("id"), info.id); - RETURN_IF_FAILED(rv); - - rv = stmt->Execute(); -} - -// Called when a subresource has been hit from a top-level load. Uses the two -// helper functions above to update the database appropriately. -void -Seer::LearnForSubresource(const UriInfo &targetURI, const UriInfo &sourceURI) -{ - MOZ_ASSERT(!NS_IsMainThread(), "LearnForSubresource called on main thread."); - - if (NS_FAILED(EnsureInitStorage())) { - return; - } - - TopLevelInfo pageInfo, originInfo; - bool havePage = LookupTopLevel(QUERY_PAGE, sourceURI.spec, pageInfo); - bool haveOrigin = LookupTopLevel(QUERY_ORIGIN, sourceURI.origin, - originInfo); - - if (!havePage && !haveOrigin) { - // Nothing to do, since we know nothing about the top level resource - return; - } - - SubresourceInfo resourceInfo; - bool haveResource = false; - if (havePage) { - haveResource = LookupSubresource(QUERY_PAGE, pageInfo.id, targetURI.spec, - resourceInfo); - } - - SubresourceInfo hostInfo; - bool haveHost = false; - if (haveOrigin) { - haveHost = LookupSubresource(QUERY_ORIGIN, originInfo.id, targetURI.origin, - hostInfo); - } - - PRTime now = PR_Now(); - - if (haveResource) { - UpdateSubresource(QUERY_PAGE, resourceInfo, now); - } else if (havePage) { - AddSubresource(QUERY_PAGE, pageInfo.id, targetURI.spec, now); - } - // Can't add a subresource to a page we don't have in our db. - - if (haveHost) { - UpdateSubresource(QUERY_ORIGIN, hostInfo, now); - } else if (haveOrigin) { - AddSubresource(QUERY_ORIGIN, originInfo.id, targetURI.origin, now); - } - // Can't add a subhost to a host we don't have in our db -} - -// This is called when a top-level loaded ended up redirecting to a different -// URI so we can keep track of that fact. -void -Seer::LearnForRedirect(const UriInfo &targetURI, const UriInfo &sourceURI) -{ - MOZ_ASSERT(!NS_IsMainThread(), "LearnForRedirect called on main thread."); - - if (NS_FAILED(EnsureInitStorage())) { - return; - } - - PRTime now = PR_Now(); - nsresult rv; - - nsCOMPtr getPage = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("SELECT id FROM moz_pages WHERE uri = :spec;")); - if (!getPage) { - return; - } - mozStorageStatementScoper scopedPage(getPage); - - // look up source in moz_pages - rv = getPage->BindUTF8StringByName(NS_LITERAL_CSTRING("spec"), - sourceURI.spec); - RETURN_IF_FAILED(rv); - - bool hasRows; - rv = getPage->ExecuteStep(&hasRows); - if (NS_FAILED(rv) || !hasRows) { - return; - } - - int32_t pageId; - rv = getPage->GetInt32(0, &pageId); - RETURN_IF_FAILED(rv); - - nsCOMPtr getRedirect = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("SELECT id, hits FROM moz_redirects WHERE " - "pid = :page_id AND uri = :spec;")); - if (!getRedirect) { - return; - } - mozStorageStatementScoper scopedRedirect(getRedirect); - - rv = getRedirect->BindInt32ByName(NS_LITERAL_CSTRING("page_id"), pageId); - RETURN_IF_FAILED(rv); - - rv = getRedirect->BindUTF8StringByName(NS_LITERAL_CSTRING("spec"), - targetURI.spec); - RETURN_IF_FAILED(rv); - - rv = getRedirect->ExecuteStep(&hasRows); - RETURN_IF_FAILED(rv); - - if (!hasRows) { - // This is the first time we've seen this top-level redirect to this URI - nsCOMPtr addRedirect = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("INSERT INTO moz_redirects " - "(pid, uri, origin, hits, last_hit) VALUES " - "(:page_id, :spec, :origin, 1, :now);")); - if (!addRedirect) { - return; - } - mozStorageStatementScoper scopedAdd(addRedirect); - - rv = addRedirect->BindInt32ByName(NS_LITERAL_CSTRING("page_id"), pageId); - RETURN_IF_FAILED(rv); - - rv = addRedirect->BindUTF8StringByName(NS_LITERAL_CSTRING("spec"), - targetURI.spec); - RETURN_IF_FAILED(rv); - - rv = addRedirect->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), - targetURI.origin); - RETURN_IF_FAILED(rv); - - rv = addRedirect->BindInt64ByName(NS_LITERAL_CSTRING("now"), now); - RETURN_IF_FAILED(rv); - - rv = addRedirect->Execute(); - } else { - // We've seen this redirect before - int32_t redirId, hits; - rv = getRedirect->GetInt32(0, &redirId); - RETURN_IF_FAILED(rv); - - rv = getRedirect->GetInt32(1, &hits); - RETURN_IF_FAILED(rv); - - nsCOMPtr updateRedirect = - mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("UPDATE moz_redirects SET hits = :hits, " - "last_hit = :now WHERE id = :redir;")); - if (!updateRedirect) { - return; - } - mozStorageStatementScoper scopedUpdate(updateRedirect); - - rv = updateRedirect->BindInt32ByName(NS_LITERAL_CSTRING("hits"), hits + 1); - RETURN_IF_FAILED(rv); - - rv = updateRedirect->BindInt64ByName(NS_LITERAL_CSTRING("now"), now); - RETURN_IF_FAILED(rv); - - rv = updateRedirect->BindInt32ByName(NS_LITERAL_CSTRING("redir"), redirId); - RETURN_IF_FAILED(rv); - - updateRedirect->Execute(); - } -} - -// Add information about a top-level load to our list of startup pages -void -Seer::LearnForStartup(const UriInfo &uri) -{ - MOZ_ASSERT(!NS_IsMainThread(), "LearnForStartup called on main thread."); - - if (NS_FAILED(EnsureInitStorage())) { - return; - } - - nsCOMPtr getPage = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("SELECT id, hits FROM moz_startup_pages WHERE " - "uri = :origin;")); - if (!getPage) { - return; - } - mozStorageStatementScoper scopedPage(getPage); - nsresult rv; - - rv = getPage->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), uri.origin); - RETURN_IF_FAILED(rv); - - bool hasRows; - rv = getPage->ExecuteStep(&hasRows); - RETURN_IF_FAILED(rv); - - if (hasRows) { - // We've loaded this page on startup before - int32_t pageId, hitCount; - - rv = getPage->GetInt32(0, &pageId); - RETURN_IF_FAILED(rv); - - rv = getPage->GetInt32(1, &hitCount); - RETURN_IF_FAILED(rv); - - nsCOMPtr updatePage = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("UPDATE moz_startup_pages SET hits = :hit_count, " - "last_hit = :startup_time WHERE id = :page_id;")); - if (!updatePage) { - return; - } - mozStorageStatementScoper scopedUpdate(updatePage); - - rv = updatePage->BindInt32ByName(NS_LITERAL_CSTRING("hit_count"), - hitCount + 1); - RETURN_IF_FAILED(rv); - - rv = updatePage->BindInt64ByName(NS_LITERAL_CSTRING("startup_time"), - mStartupTime); - RETURN_IF_FAILED(rv); - - rv = updatePage->BindInt32ByName(NS_LITERAL_CSTRING("page_id"), pageId); - RETURN_IF_FAILED(rv); - - updatePage->Execute(); - } else { - // New startup page - nsCOMPtr addPage = mStatements.GetCachedStatement( - NS_LITERAL_CSTRING("INSERT INTO moz_startup_pages (uri, hits, " - "last_hit) VALUES (:origin, 1, :startup_time);")); - if (!addPage) { - return; - } - mozStorageStatementScoper scopedAdd(addPage); - rv = addPage->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), - uri.origin); - RETURN_IF_FAILED(rv); - - rv = addPage->BindInt64ByName(NS_LITERAL_CSTRING("startup_time"), - mStartupTime); - RETURN_IF_FAILED(rv); - - addPage->Execute(); - } -} - -// Called from the main thread to update the database -NS_IMETHODIMP -Seer::Learn(nsIURI *targetURI, nsIURI *sourceURI, SeerLearnReason reason, - nsILoadContext *loadContext) -{ - MOZ_ASSERT(NS_IsMainThread(), - "Seer interface methods must be called on the main thread"); - - if (!mInitialized) { - return NS_ERROR_NOT_AVAILABLE; - } - - if (!mEnabled) { - return NS_ERROR_NOT_AVAILABLE; - } - - if (loadContext && loadContext->UsePrivateBrowsing()) { - // Don't want to do anything in PB mode - return NS_OK; - } - - if (!IsNullOrHttp(targetURI) || !IsNullOrHttp(sourceURI)) { - return NS_ERROR_INVALID_ARG; - } - - switch (reason) { - case nsINetworkSeer::LEARN_LOAD_TOPLEVEL: - case nsINetworkSeer::LEARN_STARTUP: - if (!targetURI || sourceURI) { - return NS_ERROR_INVALID_ARG; - } - break; - case nsINetworkSeer::LEARN_LOAD_REDIRECT: - case nsINetworkSeer::LEARN_LOAD_SUBRESOURCE: - if (!targetURI || !sourceURI) { - return NS_ERROR_INVALID_ARG; - } - break; - default: - return NS_ERROR_INVALID_ARG; - } - - ++mAccumulators->mLearnAttempts; - nsresult rv = ReserveSpaceInQueue(); - if (NS_FAILED(rv)) { - ++mAccumulators->mLearnFullQueue; - return NS_ERROR_NOT_AVAILABLE; - } - - nsRefPtr event = new SeerLearnEvent(targetURI, sourceURI, - reason); - return mIOThread->Dispatch(event, NS_DISPATCH_NORMAL); -} - -// Runnable to clear out the database. Dispatched from the main thread to the -// seer thread -class SeerResetEvent : public nsRunnable -{ -public: - SeerResetEvent() - { } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - MOZ_ASSERT(!NS_IsMainThread(), "Running reset on main thread"); - - gSeer->ResetInternal(); - - return NS_OK; - } -}; - -// Helper that actually does the database wipe. -void -Seer::ResetInternal() -{ - MOZ_ASSERT(!NS_IsMainThread(), "Resetting db on main thread"); - - nsresult rv = EnsureInitStorage(); - RETURN_IF_FAILED(rv); - - mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_redirects")); - mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_startup_pages")); - mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_startups")); - - // These cascade to moz_subresources and moz_subhosts, respectively. - mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_pages")); - mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM moz_hosts")); -} - -// Called on the main thread to clear out all our knowledge. Tabula Rasa FTW! -NS_IMETHODIMP -Seer::Reset() -{ - MOZ_ASSERT(NS_IsMainThread(), - "Seer interface methods must be called on the main thread"); - - if (!mInitialized) { - return NS_ERROR_NOT_AVAILABLE; - } - - nsRefPtr event = new SeerResetEvent(); - return mIOThread->Dispatch(event, NS_DISPATCH_NORMAL); -} - -// Helper functions to make using the seer easier from native code - -static nsresult -EnsureGlobalSeer(nsINetworkSeer **aSeer) -{ - nsresult rv; - nsCOMPtr seer = do_GetService("@mozilla.org/network/seer;1", - &rv); - NS_ENSURE_SUCCESS(rv, rv); - - NS_IF_ADDREF(*aSeer = seer); - return NS_OK; -} - -nsresult -SeerPredict(nsIURI *targetURI, nsIURI *sourceURI, SeerPredictReason reason, - nsILoadContext *loadContext, nsINetworkSeerVerifier *verifier) -{ - nsCOMPtr seer; - nsresult rv = EnsureGlobalSeer(getter_AddRefs(seer)); - NS_ENSURE_SUCCESS(rv, rv); - - return seer->Predict(targetURI, sourceURI, reason, loadContext, verifier); -} - -nsresult -SeerLearn(nsIURI *targetURI, nsIURI *sourceURI, SeerLearnReason reason, - nsILoadContext *loadContext) -{ - nsCOMPtr seer; - nsresult rv = EnsureGlobalSeer(getter_AddRefs(seer)); - NS_ENSURE_SUCCESS(rv, rv); - - return seer->Learn(targetURI, sourceURI, reason, loadContext); -} - -nsresult -SeerLearn(nsIURI *targetURI, nsIURI *sourceURI, SeerLearnReason reason, - nsILoadGroup *loadGroup) -{ - nsCOMPtr seer; - nsresult rv = EnsureGlobalSeer(getter_AddRefs(seer)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr loadContext; - - if (loadGroup) { - nsCOMPtr callbacks; - loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks)); - if (callbacks) { - loadContext = do_GetInterface(callbacks); - } - } - - return seer->Learn(targetURI, sourceURI, reason, loadContext); -} - -nsresult -SeerLearn(nsIURI *targetURI, nsIURI *sourceURI, SeerLearnReason reason, - nsIDocument *document) -{ - nsCOMPtr seer; - nsresult rv = EnsureGlobalSeer(getter_AddRefs(seer)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr loadContext; - - if (document) { - loadContext = document->GetLoadContext(); - } - - return seer->Learn(targetURI, sourceURI, reason, loadContext); -} - -nsresult -SeerLearnRedirect(nsIURI *targetURI, nsIChannel *channel, - nsILoadContext *loadContext) -{ - nsCOMPtr seer; - nsresult rv = EnsureGlobalSeer(getter_AddRefs(seer)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr sourceURI; - rv = channel->GetOriginalURI(getter_AddRefs(sourceURI)); - NS_ENSURE_SUCCESS(rv, rv); - - bool sameUri; - rv = targetURI->Equals(sourceURI, &sameUri); - NS_ENSURE_SUCCESS(rv, rv); - - if (sameUri) { - return NS_OK; - } - - return seer->Learn(targetURI, sourceURI, - nsINetworkSeer::LEARN_LOAD_REDIRECT, loadContext); -} - -} // ::mozilla::net -} // ::mozilla diff --git a/netwerk/base/src/Seer.h b/netwerk/base/src/Seer.h deleted file mode 100644 index 73e831ab5b8f..000000000000 --- a/netwerk/base/src/Seer.h +++ /dev/null @@ -1,206 +0,0 @@ -/* vim: set ts=2 sts=2 et sw=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/. */ - -#ifndef mozilla_net_Seer_h -#define mozilla_net_Seer_h - -#include "nsINetworkSeer.h" - -#include "nsCOMPtr.h" -#include "nsIDNSListener.h" -#include "nsIInterfaceRequestor.h" -#include "nsIObserver.h" -#include "nsISpeculativeConnect.h" -#include "nsProxyRelease.h" - -#include "mozilla/Mutex.h" -#include "mozilla/storage/StatementCache.h" -#include "mozilla/TimeStamp.h" - -class nsIDNSService; -class nsINetworkSeerVerifier; -class nsIThread; - -class mozIStorageConnection; -class mozIStorageService; -class mozIStorageStatement; - -namespace mozilla { -namespace net { - -typedef nsMainThreadPtrHandle SeerVerifierHandle; - -class SeerPredictionRunner; -struct SeerTelemetryAccumulators; - -class Seer : public nsINetworkSeer - , public nsIObserver - , public nsIDNSListener - , public nsISpeculativeConnectionOverrider - , public nsIInterfaceRequestor -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSINETWORKSEER - NS_DECL_NSIOBSERVER - NS_DECL_NSIDNSLISTENER - NS_DECL_NSISPECULATIVECONNECTIONOVERRIDER - NS_DECL_NSIINTERFACEREQUESTOR - - Seer(); - virtual ~Seer(); - - nsresult Init(); - void Shutdown(); - static nsresult Create(nsISupports *outer, const nsIID& iid, void **result); - -private: - friend class SeerPredictionEvent; - friend class SeerLearnEvent; - friend class SeerResetEvent; - friend class SeerPredictionRunner; - friend class SeerDBShutdownRunner; - - nsresult EnsureInitStorage(); - - // This is a proxy for the information we need from an nsIURI - struct UriInfo { - nsAutoCString spec; - nsAutoCString origin; - }; - - void PredictForLink(nsIURI *targetURI, - nsIURI *sourceURI, - nsINetworkSeerVerifier *verifier); - void PredictForPageload(const UriInfo &dest, - SeerVerifierHandle &verifier, - int stackCount, - TimeStamp &predictStartTime); - void PredictForStartup(SeerVerifierHandle &verifier, - TimeStamp &predictStartTime); - - // Whether we're working on a page or an origin - enum QueryType { - QUERY_PAGE = 0, - QUERY_ORIGIN - }; - - // Holds info from the db about a top-level page or origin - struct TopLevelInfo { - int32_t id; - int32_t loadCount; - PRTime lastLoad; - }; - - // Holds info from the db about a subresource - struct SubresourceInfo { - int32_t id; - int32_t hitCount; - PRTime lastHit; - }; - - nsresult ReserveSpaceInQueue(); - void FreeSpaceInQueue(); - - int CalculateGlobalDegradation(PRTime now, - PRTime lastLoad); - int CalculateConfidence(int baseConfidence, - PRTime lastHit, - PRTime lastPossible, - int globalDegradation); - void SetupPrediction(int confidence, - const nsACString &uri, - SeerPredictionRunner *runner); - - bool LookupTopLevel(QueryType queryType, - const nsACString &key, - TopLevelInfo &info); - void AddTopLevel(QueryType queryType, - const nsACString &key, - PRTime now); - void UpdateTopLevel(QueryType queryType, - const TopLevelInfo &info, - PRTime now); - bool TryPredict(QueryType queryType, - const TopLevelInfo &info, - PRTime now, - SeerVerifierHandle &verifier, - TimeStamp &predictStartTime); - bool WouldRedirect(const TopLevelInfo &info, - PRTime now, - UriInfo &newUri); - - bool LookupSubresource(QueryType queryType, - const int32_t parentId, - const nsACString &key, - SubresourceInfo &info); - void AddSubresource(QueryType queryType, - const int32_t parentId, - const nsACString &key, PRTime now); - void UpdateSubresource(QueryType queryType, - const SubresourceInfo &info, - PRTime now); - - void MaybeLearnForStartup(const UriInfo &uri, const PRTime now); - - void LearnForToplevel(const UriInfo &uri); - void LearnForSubresource(const UriInfo &targetURI, const UriInfo &sourceURI); - void LearnForRedirect(const UriInfo &targetURI, const UriInfo &sourceURI); - void LearnForStartup(const UriInfo &uri); - - void ResetInternal(); - - // Observer-related stuff - nsresult InstallObserver(); - void RemoveObserver(); - - bool mInitialized; - - bool mEnabled; - bool mEnableHoverOnSSL; - - int mPageDegradationDay; - int mPageDegradationWeek; - int mPageDegradationMonth; - int mPageDegradationYear; - int mPageDegradationMax; - - int mSubresourceDegradationDay; - int mSubresourceDegradationWeek; - int mSubresourceDegradationMonth; - int mSubresourceDegradationYear; - int mSubresourceDegradationMax; - - int mPreconnectMinConfidence; - int mPreresolveMinConfidence; - int mRedirectLikelyConfidence; - - int32_t mMaxQueueSize; - - nsCOMPtr mIOThread; - - nsCOMPtr mSpeculativeService; - - nsCOMPtr mDBFile; - nsCOMPtr mStorageService; - nsCOMPtr mDB; - mozilla::storage::StatementCache mStatements; - - PRTime mStartupTime; - PRTime mLastStartupTime; - int32_t mStartupCount; - - nsCOMPtr mDnsService; - - int32_t mQueueSize; - mozilla::Mutex mQueueSizeLock; - - nsAutoPtr mAccumulators; -}; - -} // ::mozilla::net -} // ::mozilla - -#endif // mozilla_net_Seer_h diff --git a/netwerk/base/src/moz.build b/netwerk/base/src/moz.build index 914db2018b04..1b4e57cff867 100644 --- a/netwerk/base/src/moz.build +++ b/netwerk/base/src/moz.build @@ -74,7 +74,6 @@ CPP_SOURCES += [ 'nsURLHelper.cpp', 'nsURLParsers.cpp', 'nsUnicharStreamLoader.cpp', - 'Seer.cpp', ] if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'os2': diff --git a/netwerk/build/nsNetCID.h b/netwerk/build/nsNetCID.h index 544bdc74150f..cc5a4caed96e 100644 --- a/netwerk/build/nsNetCID.h +++ b/netwerk/build/nsNetCID.h @@ -436,17 +436,6 @@ { 0x8d, 0x17, 0xa2, 0x7e, 0x44, 0xa8, 0x39, 0x3e } \ } -// service implementing nsINetworkSeer -#define NS_NETWORKSEER_CONTRACTID \ - "@mozilla.org/network/seer;1" -#define NS_NETWORKSEER_CID \ -{ /* {1C218009-A531-46AD-8351-1E7F45D5A3C4} */ \ - 0x1C218009, \ - 0xA531, \ - 0x46AD, \ - { 0x83, 0x51, 0x1E, 0x7F, 0x45, 0xD5, 0xA3, 0xC4 } \ -} - /****************************************************************************** * netwerk/cache/ classes */ diff --git a/netwerk/build/nsNetModule.cpp b/netwerk/build/nsNetModule.cpp index 45641ff8bd4f..3f7577314715 100644 --- a/netwerk/build/nsNetModule.cpp +++ b/netwerk/build/nsNetModule.cpp @@ -36,7 +36,6 @@ #include "nsXULAppAPI.h" #include "nsCategoryCache.h" #include "nsIContentSniffer.h" -#include "Seer.h" #include "nsNetUtil.h" #include "mozilla/net/NeckoChild.h" @@ -813,7 +812,6 @@ NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID); NS_DEFINE_NAMED_CID(NS_SERIALIZATION_HELPER_CID); NS_DEFINE_NAMED_CID(NS_REDIRECTCHANNELREGISTRAR_CID); NS_DEFINE_NAMED_CID(NS_CACHE_STORAGE_SERVICE_CID); -NS_DEFINE_NAMED_CID(NS_NETWORKSEER_CID); static const mozilla::Module::CIDEntry kNeckoCIDs[] = { { &kNS_IOSERVICE_CID, false, nullptr, nsIOServiceConstructor }, @@ -954,7 +952,6 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = { { &kNS_SERIALIZATION_HELPER_CID, false, nullptr, nsSerializationHelperConstructor }, { &kNS_REDIRECTCHANNELREGISTRAR_CID, false, nullptr, RedirectChannelRegistrarConstructor }, { &kNS_CACHE_STORAGE_SERVICE_CID, false, nullptr, CacheStorageServiceConstructor }, - { &kNS_NETWORKSEER_CID, false, NULL, mozilla::net::Seer::Create }, { nullptr } }; @@ -1098,7 +1095,6 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = { { NS_SERIALIZATION_HELPER_CONTRACTID, &kNS_SERIALIZATION_HELPER_CID }, { NS_REDIRECTCHANNELREGISTRAR_CONTRACTID, &kNS_REDIRECTCHANNELREGISTRAR_CID }, { NS_CACHE_STORAGE_SERVICE_CONTRACTID, &kNS_CACHE_STORAGE_SERVICE_CID }, - { NS_NETWORKSEER_CONTRACTID, &kNS_NETWORKSEER_CID }, { nullptr } }; diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index 61337f22541a..fce775e1c4f2 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -320,70 +320,27 @@ nsHttpConnectionMgr::DoShiftReloadConnectionCleanup(nsHttpConnectionInfo *aCI) return rv; } -class SpeculativeConnectArgs -{ -public: - SpeculativeConnectArgs() { mOverridesOK = false; } - virtual ~SpeculativeConnectArgs() {} - - // Added manually so we can use nsRefPtr without inheriting from - // nsISupports - NS_IMETHOD_(nsrefcnt) AddRef(void); - NS_IMETHOD_(nsrefcnt) Release(void); - -public: // intentional! - nsRefPtr mTrans; - - bool mOverridesOK; - uint32_t mParallelSpeculativeConnectLimit; - bool mIgnoreIdle; - bool mIgnorePossibleSpdyConnections; - - // As above, added manually so we can use nsRefPtr without inheriting from - // nsISupports -protected: - ::mozilla::ThreadSafeAutoRefCnt mRefCnt; - NS_DECL_OWNINGTHREAD -}; - -NS_IMPL_ADDREF(SpeculativeConnectArgs) -NS_IMPL_RELEASE(SpeculativeConnectArgs) - nsresult nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci, nsIInterfaceRequestor *callbacks, uint32_t caps) { - MOZ_ASSERT(NS_IsMainThread(), "nsHttpConnectionMgr::SpeculativeConnect called off main thread!"); - LOG(("nsHttpConnectionMgr::SpeculativeConnect [ci=%s]\n", ci->HashKey().get())); - nsRefPtr args = new SpeculativeConnectArgs(); - // Wrap up the callbacks and the target to ensure they're released on the target // thread properly. nsCOMPtr wrappedCallbacks; NS_NewInterfaceRequestorAggregation(callbacks, nullptr, getter_AddRefs(wrappedCallbacks)); caps |= ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0; - args->mTrans = new NullHttpTransaction(ci, wrappedCallbacks, caps); - - nsCOMPtr overrider = - do_GetInterface(callbacks); - if (overrider) { - args->mOverridesOK = true; - overrider->GetParallelSpeculativeConnectLimit( - &args->mParallelSpeculativeConnectLimit); - overrider->GetIgnoreIdle(&args->mIgnoreIdle); - overrider->GetIgnorePossibleSpdyConnections( - &args->mIgnorePossibleSpdyConnections); - } + nsRefPtr trans = + new NullHttpTransaction(ci, wrappedCallbacks, caps); nsresult rv = - PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, args); + PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, trans); if (NS_SUCCEEDED(rv)) - args.forget(); + trans.forget(); return rv; } @@ -1272,8 +1229,7 @@ nsHttpConnectionMgr::ClosePersistentConnectionsCB(const nsACString &key, } bool -nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent, - bool ignorePossibleSpdyConnections) +nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); @@ -1283,8 +1239,7 @@ nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent, bool doRestrict = ent->mConnInfo->UsingSSL() && gHttpHandler->IsSpdyEnabled() && - ((!ent->mTestedSpdy && !ignorePossibleSpdyConnections) || - ent->mUsingSpdy) && + (!ent->mTestedSpdy || ent->mUsingSpdy) && (ent->mHalfOpens.Length() || ent->mActiveConns.Length()); // If there are no restrictions, we are done @@ -1293,7 +1248,7 @@ nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent, // If the restriction is based on a tcp handshake in progress // let that connect and then see if it was SPDY or not - if (ent->UnconnectedHalfOpens() && !ignorePossibleSpdyConnections) + if (ent->UnconnectedHalfOpens()) return true; // There is a concern that a host is using a mix of HTTP/1 and SPDY. @@ -2565,14 +2520,14 @@ nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, void *param) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); - nsRefPtr args = - dont_AddRef(static_cast(param)); + nsRefPtr trans = + dont_AddRef(static_cast(param)); LOG(("nsHttpConnectionMgr::OnMsgSpeculativeConnect [ci=%s]\n", - args->mTrans->ConnectionInfo()->HashKey().get())); + trans->ConnectionInfo()->HashKey().get())); nsConnectionEntry *ent = - GetOrCreateConnectionEntry(args->mTrans->ConnectionInfo()); + GetOrCreateConnectionEntry(trans->ConnectionInfo()); // If spdy has previously made a preferred entry for this host via // the ip pooling rules. If so, connect to the preferred host instead of @@ -2581,22 +2536,10 @@ nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, void *param) if (preferredEntry) ent = preferredEntry; - uint32_t parallelSpeculativeConnectLimit = - gHttpHandler->ParallelSpeculativeConnectLimit(); - bool ignorePossibleSpdyConnections = false; - bool ignoreIdle = false; - - if (args->mOverridesOK) { - parallelSpeculativeConnectLimit = args->mParallelSpeculativeConnectLimit; - ignorePossibleSpdyConnections = args->mIgnorePossibleSpdyConnections; - ignoreIdle = args->mIgnoreIdle; - } - - if (mNumHalfOpenConns < parallelSpeculativeConnectLimit && - (ignoreIdle || !ent->mIdleConns.Length()) && - !RestrictConnections(ent, ignorePossibleSpdyConnections) && - !AtActiveConnectionLimit(ent, args->mTrans->Caps())) { - CreateTransport(ent, args->mTrans, args->mTrans->Caps(), true); + if (mNumHalfOpenConns < gHttpHandler->ParallelSpeculativeConnectLimit() && + !ent->mIdleConns.Length() && !RestrictConnections(ent) && + !AtActiveConnectionLimit(ent, trans->Caps())) { + CreateTransport(ent, trans, trans->Caps(), true); } else { LOG((" Transport not created due to existing connection count\n")); diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.h b/netwerk/protocol/http/nsHttpConnectionMgr.h index 35fec1d29ca1..9e539286d414 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.h +++ b/netwerk/protocol/http/nsHttpConnectionMgr.h @@ -516,7 +516,7 @@ private: nsresult BuildPipeline(nsConnectionEntry *, nsAHttpTransaction *, nsHttpPipeline **); - bool RestrictConnections(nsConnectionEntry *, bool = false); + bool RestrictConnections(nsConnectionEntry *); nsresult ProcessNewTransaction(nsHttpTransaction *); nsresult EnsureSocketThreadTarget(); void ClosePersistentConnections(nsConnectionEntry *ent); diff --git a/netwerk/test/unit/test_seer.js b/netwerk/test/unit/test_seer.js deleted file mode 100644 index 48c672865006..000000000000 --- a/netwerk/test/unit/test_seer.js +++ /dev/null @@ -1,295 +0,0 @@ -var Cc = Components.classes; -var Ci = Components.interfaces; -var Cu = Components.utils; -var Cr = Components.results; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/FileUtils.jsm"); - -var seer = null; -var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); -var profile = null; - -function extract_origin(uri) { - var o = uri.scheme + "://" + uri.asciiHost; - if (uri.port !== -1) { - o = o + ":" + uri.port; - } - return o; -} - -var LoadContext = function _loadContext() { -}; - -LoadContext.prototype = { - usePrivateBrowsing: false, - - getInterface: function loadContext_getInterface(iid) { - return this.QueryInterface(iid); - }, - - QueryInterface: function loadContext_QueryInterface(iid) { - if (iid.equals(Ci.nsINetworkSeerVerifier) || - iid.equals(Ci.nsILoadContext)) { - return this; - } - - throw Cr.NS_ERROR_NO_INTERFACE; - } -}; - -var load_context = new LoadContext(); - -var Verifier = function _verifier(testing, expected_preconnects, expected_preresolves) { - this.verifying = testing; - this.expected_preconnects = expected_preconnects; - this.expected_preresolves = expected_preresolves; -}; - -Verifier.prototype = { - verifying: null, - expected_preconnects: null, - expected_preresolves: null, - - getInterface: function verifier_getInterface(iid) { - return this.QueryInterface(iid); - }, - - QueryInterface: function verifier_QueryInterface(iid) { - if (iid.equals(Ci.nsINetworkSeerVerifier) || - iid.equals(Ci.nsISupports)) { - return this; - } - - throw Cr.NS_ERROR_NO_INTERFACE; - }, - - maybe_run_next_test: function verifier_maybe_run_next_test() { - if (this.expected_preconnects.length === 0 && - this.expected_preresolves.length === 0) { - do_check_true(true, "Well this is unexpected..."); - run_next_test(); - } - }, - - onPredictPreconnect: function verifier_onPredictPreconnect(uri) { - var origin = extract_origin(uri); - var index = this.expected_preconnects.indexOf(origin); - if (index == -1) { - do_check_true(false, "Got preconnect for unexpected uri " + origin); - } else { - this.expected_preconnects.splice(index, 1); - } - this.maybe_run_next_test(); - }, - - onPredictDNS: function verifier_onPredictDNS(uri) { - var origin = extract_origin(uri); - var index = this.expected_preresolves.indexOf(origin); - if (index == -1) { - do_check_true(false, "Got preresolve for unexpected uri " + origin); - } else { - this.expected_preresolves.splice(index, 1); - } - this.maybe_run_next_test(); - } -}; - -function reset_seer() { - seer.reset(); -} - -function newURI(s) { - return ios.newURI(s, null, null); -} - -function test_link_hover() { - reset_seer(); - var uri = newURI("http://localhost:4444/foo/bar"); - var referrer = newURI("http://localhost:4444/foo"); - var preconns = ["http://localhost:4444"]; - - var verifier = new Verifier("hover", preconns, []); - seer.predict(uri, referrer, seer.PREDICT_LINK, load_context, verifier); -} - -function test_pageload() { - reset_seer(); - var toplevel = "http://localhost:4444/index.html"; - var subresources = [ - "http://localhost:4444/style.css", - "http://localhost:4443/jquery.js", - "http://localhost:4444/image.png" - ]; - - var tluri = newURI(toplevel); - seer.learn(tluri, null, seer.LEARN_LOAD_TOPLEVEL, load_context); - var preconns = []; - for (var i = 0; i < subresources.length; i++) { - var sruri = newURI(subresources[i]); - seer.learn(sruri, tluri, seer.LEARN_LOAD_SUBRESOURCE, load_context); - preconns.push(extract_origin(sruri)); - } - - var verifier = new Verifier("pageload", preconns, []); - seer.predict(tluri, null, seer.PREDICT_LOAD, load_context, verifier); -} - -function test_redirect() { - reset_seer(); - var initial = "http://localhost:4443/redirect"; - var target = "http://localhost:4444/index.html"; - var subresources = [ - "http://localhost:4444/style.css", - "http://localhost:4443/jquery.js", - "http://localhost:4444/image.png" - ]; - - var inituri = newURI(initial); - var targeturi = newURI(target); - seer.learn(inituri, null, seer.LEARN_LOAD_TOPLEVEL, load_context); - seer.learn(targeturi, inituri, seer.LEARN_LOAD_REDIRECT, load_context); - seer.learn(targeturi, null, seer.LEARN_LOAD_TOPLEVEL, load_context); - - var preconns = []; - preconns.push(extract_origin(targeturi)); - for (var i = 0; i < subresources.length; i++) { - var sruri = newURI(subresources[i]); - seer.learn(sruri, targeturi, seer.LEARN_LOAD_SUBRESOURCE, load_context); - preconns.push(extract_origin(sruri)); - } - - var verifier = new Verifier("redirect", preconns, []); - seer.predict(inituri, null, seer.PREDICT_LOAD, load_context, verifier); -} - -function test_startup() { - reset_seer(); - var uris = [ - "http://localhost:4444/startup", - "http://localhost:4443/startup" - ]; - var preconns = []; - for (var i = 0; i < uris.length; i++) { - var uri = newURI(uris[i]); - seer.learn(uri, null, seer.LEARN_STARTUP, load_context); - preconns.push(extract_origin(uri)); - } - - var verifier = new Verifier("startup", preconns, []); - seer.predict(null, null, seer.PREDICT_STARTUP, load_context, verifier); -} - -// A class used to guarantee serialization of SQL queries so we can properly -// update last hit times on subresources to ensure the seer tries to do DNS -// preresolve on them instead of preconnecting -var DnsContinueVerifier = function _dnsContinueVerifier(subresource, tluri, preresolves) { - this.subresource = subresource; - this.tluri = tluri; - this.preresolves = preresolves; -}; - -DnsContinueVerifier.prototype = { - subresource: null, - tluri: null, - preresolves: null, - - getInterface: function _dnsContinueVerifier_getInterface(iid) { - return this.QueryInterface(iid); - }, - - QueryInterface: function _dnsContinueVerifier_QueryInterface(iid) { - if (iid.equals(Ci.nsISupports) || - iid.equals(Ci.nsINetworkSeerVerifier)) { - return this; - } - - throw Cr.NS_ERROR_NO_INTERFACE; - }, - - onPredictPreconnect: function _dnsContinueVerifier_onPredictPreconnect() { - // This means that the seer has learned and done our "checkpoint" prediction - // Now we can get on with the prediction we actually want to test - - // tstamp is 10 days older than now - just over 1 week, which will ensure we - // hit our cutoff for dns vs. preconnect. This is all in usec, hence the - // x1000 on the Date object value. - var tstamp = (new Date().valueOf() * 1000) - (10 * 86400 * 1000000); - - var dbfile = FileUtils.getFile("ProfD", ["seer.sqlite"]); - var dbconn = Services.storage.openDatabase(dbfile); - // We also need to update hits, since the toplevel has been "loaded" a - // second time (from the prediction that kicked off this callback) to ensure - // that the seer will try to do anything for this subresource. - var stmt = "UPDATE moz_subresources SET last_hit = " + tstamp + ", hits = 2 WHERE uri = '" + this.subresource + "';"; - dbconn.executeSimpleSQL(stmt); - dbconn.close(); - - var verifier = new Verifier("dns", [], this.preresolves); - seer.predict(this.tluri, null, seer.PREDICT_LOAD, load_context, verifier); - }, - - onPredictDNS: function _dnsContinueVerifier_onPredictDNS() { - do_check_true(false, "Shouldn't have gotten a preresolve prediction here!"); - } -}; - -function test_dns() { - reset_seer(); - var toplevel = "http://localhost:4444/index.html"; - var subresource = "http://localhost:4443/jquery.js"; - - var tluri = newURI(toplevel); - seer.learn(tluri, null, seer.LEARN_LOAD_TOPLEVEL, load_context); - var sruri = newURI(subresource); - seer.learn(sruri, tluri, seer.LEARN_LOAD_SUBRESOURCE, load_context); - - var preresolves = [extract_origin(sruri)]; - var continue_verifier = new DnsContinueVerifier(subresource, tluri, preresolves); - // Fire off a prediction that will do preconnects so we know when the seer - // thread has gotten to the point where we can update the database manually - seer.predict(tluri, null, seer.PREDICT_LOAD, load_context, continue_verifier); -} - -function test_origin() { - reset_seer(); - var toplevel = "http://localhost:4444/index.html"; - var subresources = [ - "http://localhost:4444/style.css", - "http://localhost:4443/jquery.js", - "http://localhost:4444/image.png" - ]; - - var tluri = newURI(toplevel); - seer.learn(tluri, null, seer.LEARN_LOAD_TOPLEVEL, load_context); - var preconns = []; - for (var i = 0; i < subresources.length; i++) { - var sruri = newURI(subresources[i]); - seer.learn(sruri, tluri, seer.LEARN_LOAD_SUBRESOURCE, load_context); - var origin = extract_origin(sruri); - if (preconns.indexOf(origin) === -1) { - preconns.push(origin); - } - } - - var loaduri = newURI("http://localhost:4444/anotherpage.html"); - var verifier = new Verifier("origin", preconns, []); - seer.predict(loaduri, null, seer.PREDICT_LOAD, load_context, verifier); -} - -var tests = [ - test_link_hover, - test_pageload, - test_redirect, - test_startup, - test_dns, - test_origin -]; - -function run_test() { - tests.forEach(add_test); - profile = do_get_profile(); - seer = Cc["@mozilla.org/network/seer;1"].getService(Ci.nsINetworkSeer); - do_register_cleanup(reset_seer); - run_next_test(); -} diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 2bc9922a5e65..8d5d5bcdc3de 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -281,4 +281,3 @@ skip-if = os == "android" [test_addr_in_use_error.js] [test_about_networking.js] [test_ping_aboutnetworking.js] -[test_seer.js] diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 4fdcf89d720a..5013ef7e0746 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -1643,126 +1643,6 @@ "extended_statistics_ok": true, "description": "Time for an unsuccessful DNS OS resolution (msec)" }, - "SEER_PREDICT_ATTEMPTS": { - "kind": "exponential", - "high": "1000 * 1000", - "n_buckets": 50, - "extended_statistics_ok": true, - "description": "Number of times nsINetworkSeer::Predict is called and attempts to predict" - }, - "SEER_LEARN_ATTEMPTS": { - "kind": "exponential", - "high": "1000 * 1000", - "n_buckets": 50, - "extended_statistics_ok": true, - "description": "Number of times nsINetworkSeer::Learn is called and attempts to learn" - }, - "SEER_PREDICT_FULL_QUEUE": { - "kind": "exponential", - "high": "60000", - "n_buckets": 50, - "extended_statistics_ok": true, - "description": "Number of times nsINetworkSeer::Predict doesn't continue because the queue is full" - }, - "SEER_LEARN_FULL_QUEUE": { - "kind": "exponential", - "high": "60000", - "n_buckets": 50, - "extended_statistics_ok": true, - "description": "Number of times nsINetworkSeer::Learn doesn't continue because the queue is full" - }, - "SEER_PREDICT_WAIT_TIME": { - "kind": "exponential", - "high": "3000", - "n_buckets": 10, - "extended_statistics_ok": true, - "description": "Amount of time a predict event waits in the queue (ms)" - }, - "SEER_PREDICT_WORK_TIME": { - "kind": "exponential", - "high": "3000", - "n_buckets": 10, - "extended_statistics_ok": true, - "description": "Amount of time spent doing the work for predict (ms)" - }, - "SEER_LEARN_WAIT_TIME": { - "kind": "exponential", - "high": "3000", - "n_buckets": 10, - "extended_statistics_ok": true, - "description": "Amount of time a learn event waits in the queue (ms)" - }, - "SEER_LEARN_WORK_TIME": { - "kind": "exponential", - "high": "3000", - "n_buckets": 10, - "extended_statistics_ok": true, - "description": "Amount of time spent doing the work for learn (ms)" - }, - "SEER_TOTAL_PREDICTIONS": { - "kind": "exponential", - "high": "1000 * 1000", - "n_buckets": 50, - "extended_statistics_ok": true, - "description": "How many actual predictions (preresolves, preconnects, ...) happen" - }, - "SEER_TOTAL_PRECONNECTS": { - "kind": "exponential", - "high": "1000 * 1000", - "n_buckets": 50, - "extended_statistics_ok": true, - "description": "How many actual preconnects happen" - }, - "SEER_TOTAL_PRERESOLVES": { - "kind": "exponential", - "high": "1000 * 1000", - "n_buckets": 50, - "extended_statistics_ok": true, - "description": "How many actual preresolves happen" - }, - "SEER_PREDICTIONS_CALCULATED": { - "kind": "exponential", - "high": "1000 * 1000", - "n_buckets": 50, - "extended_statistics_ok": true, - "description": "How many prediction calculations are performed" - }, - "SEER_GLOBAL_DEGRADATION": { - "kind": "linear", - "high": "100", - "n_buckets": 50, - "description": "The global degradation calculated" - }, - "SEER_SUBRESOURCE_DEGRADATION": { - "kind": "linear", - "high": "100", - "n_buckets": 50, - "description": "The degradation calculated for a subresource" - }, - "SEER_BASE_CONFIDENCE": { - "kind": "linear", - "high": "100", - "n_buckets": 50, - "description": "The base confidence calculated for a subresource" - }, - "SEER_CONFIDENCE": { - "kind": "linear", - "high": "100", - "n_buckets": 50, - "description": "The final confidence calculated for a subresource" - }, - "SEER_PREDICT_TIME_TO_ACTION": { - "kind": "exponential", - "high": "3000", - "n_buckets": 10, - "description": "How long it takes from the time Predict() is called to the time we take action" - }, - "SEER_PREDICT_TIME_TO_INACTION": { - "kind": "exponential", - "high": "3000", - "n_buckets": 10, - "description": "How long it takes from the time Predict() is called to the time we figure out there's nothing to do" - }, "FIND_PLUGINS": { "kind": "exponential", "high": "3000", diff --git a/toolkit/forgetaboutsite/ForgetAboutSite.jsm b/toolkit/forgetaboutsite/ForgetAboutSite.jsm index 1be2fe0fd56f..f4e14e09e225 100644 --- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm +++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm @@ -216,11 +216,5 @@ this.ForgetAboutSite = { handleCompletion: function() onContentPrefsRemovalFinished(), handleError: function() {} }); - - // Predictive network data - like cache, no way to clear this per - // domain, so just trash it all - let ns = Cc["@mozilla.org/network/seer;1"]. - getService(Ci.nsINetworkSeer); - ns.reset(); } }; From 4eeac9eae5510be5a421844c0e398db73b7524d6 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Sat, 21 Sep 2013 23:04:10 -0400 Subject: [PATCH 23/24] Bug 918926 - Minimize the crashreporter #includes; r=ted --- dom/ipc/CrashReporterParent.h | 1 + toolkit/crashreporter/nsExceptionHandler.cpp | 15 ++++----------- toolkit/crashreporter/nsExceptionHandler.h | 12 +++++++----- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/dom/ipc/CrashReporterParent.h b/dom/ipc/CrashReporterParent.h index 4d4cbd1fa837..5f78567ba10c 100644 --- a/dom/ipc/CrashReporterParent.h +++ b/dom/ipc/CrashReporterParent.h @@ -9,6 +9,7 @@ #include "nsIFile.h" #ifdef MOZ_CRASHREPORTER #include "nsExceptionHandler.h" +#include "nsDataHashtable.h" #endif namespace mozilla { diff --git a/toolkit/crashreporter/nsExceptionHandler.cpp b/toolkit/crashreporter/nsExceptionHandler.cpp index e85b3218c367..38e2148df887 100644 --- a/toolkit/crashreporter/nsExceptionHandler.cpp +++ b/toolkit/crashreporter/nsExceptionHandler.cpp @@ -3,14 +3,13 @@ * 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 "nsExceptionHandler.h" +#include "nsDataHashtable.h" #include "mozilla/dom/CrashReporterChild.h" #include "mozilla/Services.h" #include "nsIObserverService.h" #include "mozilla/Util.h" -#include "nsXULAppAPI.h" - -#include "nsExceptionHandler.h" #include "nsThreadUtils.h" #if defined(XP_WIN32) @@ -18,6 +17,8 @@ #undef WIN32_LEAN_AND_MEAN #endif +#include "nsXULAppAPI.h" +#include "nsIXULAppInfo.h" #include "nsIWindowsRegKey.h" #include "client/windows/crash_generation/client_info.h" #include "client/windows/crash_generation/crash_generation_server.h" @@ -42,15 +43,12 @@ #include #include "mac_utils.h" #elif defined(XP_LINUX) -#include "nsDirectoryServiceDefs.h" #include "nsIINIParser.h" #include "common/linux/linux_libc_support.h" #include "third_party/lss/linux_syscall_support.h" #include "client/linux/crash_generation/client_info.h" #include "client/linux/crash_generation/crash_generation_server.h" #include "client/linux/handler/exception_handler.h" -#include "client/linux/minidump_writer/linux_dumper.h" -#include "client/linux/minidump_writer/minidump_writer.h" #include #include #include @@ -77,10 +75,7 @@ using mozilla::InjectCrashRunnable; #include "nsDebug.h" #include "nsCRT.h" #include "nsIFile.h" -#include "nsIFileStreams.h" -#include "nsInterfaceHashtable.h" #include "prprf.h" -#include "nsIXULAppInfo.h" #include #include @@ -94,8 +89,6 @@ CFStringRef reporterClientAppID = CFSTR("org.mozilla.crashreporter"); #include "common/linux/file_id.h" #endif -#include "nsIUUIDGenerator.h" - using google_breakpad::CrashGenerationServer; using google_breakpad::ClientInfo; #ifdef XP_LINUX diff --git a/toolkit/crashreporter/nsExceptionHandler.h b/toolkit/crashreporter/nsExceptionHandler.h index 680f81bc25e0..eb486209cc7f 100644 --- a/toolkit/crashreporter/nsExceptionHandler.h +++ b/toolkit/crashreporter/nsExceptionHandler.h @@ -6,13 +6,11 @@ #ifndef nsExceptionHandler_h__ #define nsExceptionHandler_h__ -#include "nscore.h" -#include "nsDataHashtable.h" -#include "nsXPCOM.h" +#include +#include +#include "nsError.h" #include "nsStringGlue.h" -#include "nsIFile.h" - #if defined(XP_WIN32) #ifdef WIN32_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN @@ -24,6 +22,10 @@ #include #endif +class nsIFile; +template class nsDataHashtable; +class nsCStringHashKey; + namespace CrashReporter { nsresult SetExceptionHandler(nsIFile* aXREDirectory, bool force=false); nsresult UnsetExceptionHandler(); From 50a9eb0861eb7ad63e6012eb00864212d44a8e06 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Sat, 21 Sep 2013 23:04:57 -0400 Subject: [PATCH 24/24] Bug 918928 - Minimize the #includes in netwerk/base; r=mcmanus --- netwerk/base/src/ArrayBufferInputStream.h | 1 - netwerk/base/src/BackgroundFileSaver.cpp | 2 -- netwerk/base/src/Dashboard.cpp | 6 ++++- netwerk/base/src/EventTokenBucket.cpp | 6 ++++- netwerk/base/src/EventTokenBucket.h | 3 ++- netwerk/base/src/ProxyAutoConfig.cpp | 11 ++++++++- netwerk/base/src/ProxyAutoConfig.h | 20 +++++++--------- netwerk/base/src/Tickler.cpp | 8 +++---- netwerk/base/src/Tickler.h | 15 ++++++++---- netwerk/base/src/nsAsyncStreamCopier.cpp | 3 +-- netwerk/base/src/nsAsyncStreamCopier.h | 5 ++-- netwerk/base/src/nsBaseChannel.cpp | 2 -- netwerk/base/src/nsBaseChannel.h | 3 ++- netwerk/base/src/nsBufferedStreams.cpp | 2 -- netwerk/base/src/nsChannelClassifier.h | 1 - netwerk/base/src/nsDNSPrefetch.cpp | 4 +--- netwerk/base/src/nsDNSPrefetch.h | 1 - netwerk/base/src/nsDirectoryIndexStream.cpp | 11 ++++----- netwerk/base/src/nsDirectoryIndexStream.h | 5 ++-- netwerk/base/src/nsDownloader.h | 5 ++-- netwerk/base/src/nsFileStreams.cpp | 7 +----- netwerk/base/src/nsFileStreams.h | 3 --- netwerk/base/src/nsIOService.cpp | 23 +++++++------------ netwerk/base/src/nsIOService.h | 18 ++++++--------- netwerk/base/src/nsIncrementalDownload.cpp | 3 --- netwerk/base/src/nsInputStreamPump.cpp | 9 +++----- netwerk/base/src/nsInputStreamPump.h | 11 ++++----- netwerk/base/src/nsLoadGroup.cpp | 11 +++------ netwerk/base/src/nsLoadGroup.h | 8 ++----- netwerk/base/src/nsMediaFragmentURIParser.cpp | 1 + netwerk/base/src/nsMediaFragmentURIParser.h | 5 ++-- netwerk/base/src/nsNetAddr.cpp | 1 - netwerk/base/src/nsPACMan.cpp | 4 +--- netwerk/base/src/nsPACMan.h | 5 ++-- netwerk/base/src/nsPreloadedStream.cpp | 1 - netwerk/base/src/nsProtocolProxyService.cpp | 5 +--- netwerk/base/src/nsProtocolProxyService.h | 7 +++--- netwerk/base/src/nsRequestObserverProxy.cpp | 2 -- netwerk/base/src/nsSerializationHelper.h | 1 - netwerk/base/src/nsServerSocket.cpp | 1 - netwerk/base/src/nsServerSocket.h | 2 +- netwerk/base/src/nsSimpleURI.cpp | 4 +--- netwerk/base/src/nsSimpleURI.h | 3 +-- netwerk/base/src/nsSocketTransport2.cpp | 7 +++--- netwerk/base/src/nsSocketTransport2.h | 7 +++--- .../base/src/nsSocketTransportService2.cpp | 3 --- netwerk/base/src/nsSocketTransportService2.h | 5 ++-- netwerk/base/src/nsStandardURL.cpp | 6 ++--- netwerk/base/src/nsStandardURL.h | 7 ++---- netwerk/base/src/nsStreamListenerWrapper.cpp | 4 +++- netwerk/base/src/nsStreamLoader.h | 4 ++-- netwerk/base/src/nsStreamTransportService.cpp | 3 +-- netwerk/base/src/nsStreamTransportService.h | 3 ++- netwerk/base/src/nsSyncStreamListener.cpp | 1 + netwerk/base/src/nsSyncStreamListener.h | 1 - .../base/src/nsTemporaryFileInputStream.cpp | 1 + netwerk/base/src/nsTemporaryFileInputStream.h | 4 +++- netwerk/base/src/nsUDPServerSocket.cpp | 2 -- netwerk/base/src/nsUDPServerSocket.h | 1 - netwerk/base/src/nsURIChecker.cpp | 1 - netwerk/base/src/nsURIChecker.h | 2 -- netwerk/base/src/nsURLHelper.cpp | 8 ------- netwerk/base/src/nsURLParsers.cpp | 2 -- netwerk/base/src/nsUnicharStreamLoader.cpp | 2 +- netwerk/build/nsNetModule.cpp | 1 + netwerk/protocol/http/nsHttpTransaction.cpp | 1 + 66 files changed, 132 insertions(+), 193 deletions(-) diff --git a/netwerk/base/src/ArrayBufferInputStream.h b/netwerk/base/src/ArrayBufferInputStream.h index b8b7755448e9..0e32f2449be9 100644 --- a/netwerk/base/src/ArrayBufferInputStream.h +++ b/netwerk/base/src/ArrayBufferInputStream.h @@ -7,7 +7,6 @@ #define ArrayBufferInputStream_h #include "nsIArrayBufferInputStream.h" -#include "mozilla/Util.h" #include "js/Value.h" #define NS_ARRAYBUFFERINPUTSTREAM_CONTRACTID "@mozilla.org/io/arraybuffer-input-stream;1" diff --git a/netwerk/base/src/BackgroundFileSaver.cpp b/netwerk/base/src/BackgroundFileSaver.cpp index cf075adc3675..16f5dbffd10e 100644 --- a/netwerk/base/src/BackgroundFileSaver.cpp +++ b/netwerk/base/src/BackgroundFileSaver.cpp @@ -4,7 +4,6 @@ * 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 "mozilla/RefPtr.h" #include "pk11pub.h" #include "ScopedNSSTypes.h" #include "secoidt.h" @@ -13,7 +12,6 @@ #include "nsIPipe.h" #include "nsNetUtil.h" #include "nsThreadUtils.h" -#include "nsXPCOMStrings.h" #include "BackgroundFileSaver.h" #include "mozilla/Telemetry.h" diff --git a/netwerk/base/src/Dashboard.cpp b/netwerk/base/src/Dashboard.cpp index dcab5f1ae504..dca7005b74ae 100644 --- a/netwerk/base/src/Dashboard.cpp +++ b/netwerk/base/src/Dashboard.cpp @@ -7,9 +7,13 @@ #include "mozilla/net/HttpInfo.h" #include "nsCxPusher.h" #include "nsHttp.h" +#include "nsICancelable.h" #include "nsIDNSService.h" +#include "nsIDNSRecord.h" +#include "nsIInputStream.h" +#include "nsISocketTransport.h" #include "nsIThread.h" -#include "nsSocketTransport2.h" +#include "nsSocketTransportService2.h" #include "nsThreadUtils.h" using mozilla::AutoSafeJSContext; diff --git a/netwerk/base/src/EventTokenBucket.cpp b/netwerk/base/src/EventTokenBucket.cpp index aa09670992aa..c708d562f165 100644 --- a/netwerk/base/src/EventTokenBucket.cpp +++ b/netwerk/base/src/EventTokenBucket.cpp @@ -6,9 +6,13 @@ #include "EventTokenBucket.h" +#include "nsICancelable.h" #include "nsNetUtil.h" #include "nsSocketTransportService2.h" -#include "nsThreadUtils.h" + +#ifdef DEBUG +#include "MainThreadUtils.h" +#endif #ifdef XP_WIN #include diff --git a/netwerk/base/src/EventTokenBucket.h b/netwerk/base/src/EventTokenBucket.h index fa5575d840ef..8118d4b6f018 100644 --- a/netwerk/base/src/EventTokenBucket.h +++ b/netwerk/base/src/EventTokenBucket.h @@ -9,11 +9,12 @@ #include "nsCOMPtr.h" #include "nsDeque.h" -#include "nsICancelable.h" #include "nsITimer.h" #include "mozilla/TimeStamp.h" +class nsICancelable; + namespace mozilla { namespace net { diff --git a/netwerk/base/src/ProxyAutoConfig.cpp b/netwerk/base/src/ProxyAutoConfig.cpp index 62eb13259466..62db38b98698 100644 --- a/netwerk/base/src/ProxyAutoConfig.cpp +++ b/netwerk/base/src/ProxyAutoConfig.cpp @@ -9,7 +9,6 @@ #include "nsIDNSListener.h" #include "nsIDNSRecord.h" #include "nsIDNSService.h" -#include "nsNetUtil.h" #include "nsThreadUtils.h" #include "nsIConsoleService.h" #include "nsJSUtils.h" @@ -17,6 +16,8 @@ #include "prnetdb.h" #include "nsITimer.h" #include "mozilla/net/DNS.h" +#include "nsServiceManagerUtils.h" +#include "nsNetCID.h" namespace mozilla { namespace net { @@ -326,6 +327,14 @@ bool PACResolve(const nsCString &aHostName, NetAddr *aNetAddr, return sRunning->ResolveAddress(aHostName, aNetAddr, aTimeout); } +ProxyAutoConfig::ProxyAutoConfig() + : mJSRuntime(nullptr) + , mJSNeedsSetup(false) + , mShutdown(false) +{ + MOZ_COUNT_CTOR(ProxyAutoConfig); +} + bool ProxyAutoConfig::ResolveAddress(const nsCString &aHostName, NetAddr *aNetAddr, diff --git a/netwerk/base/src/ProxyAutoConfig.h b/netwerk/base/src/ProxyAutoConfig.h index 2e322c73358d..6edd4599a408 100644 --- a/netwerk/base/src/ProxyAutoConfig.h +++ b/netwerk/base/src/ProxyAutoConfig.h @@ -8,15 +8,17 @@ #define ProxyAutoConfig_h__ #include "nsString.h" -#include "prio.h" -#include "nsITimer.h" -#include "nsAutoPtr.h" -#include "mozilla/net/DNS.h" -#include "js/TypeDecls.h" +#include "nsCOMPtr.h" + +class nsITimer; +namespace JS { +class Value; +} namespace mozilla { namespace net { class JSRuntimeWrapper; +union NetAddr; // The ProxyAutoConfig class is meant to be created and run on a // non main thread. It synchronously resolves PAC files by blocking that @@ -24,13 +26,7 @@ class JSRuntimeWrapper; class ProxyAutoConfig { public: - ProxyAutoConfig() - : mJSRuntime(nullptr) - , mJSNeedsSetup(false) - , mShutdown(false) - { - MOZ_COUNT_CTOR(ProxyAutoConfig); - } + ProxyAutoConfig(); ~ProxyAutoConfig(); nsresult Init(const nsCString &aPACURI, diff --git a/netwerk/base/src/Tickler.cpp b/netwerk/base/src/Tickler.cpp index 0b240fc4cafc..1a86262c2c15 100644 --- a/netwerk/base/src/Tickler.cpp +++ b/netwerk/base/src/Tickler.cpp @@ -3,15 +3,15 @@ * 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 "Tickler.h" + +#ifdef MOZ_USE_WIFI_TICKLER #include "nsComponentManagerUtils.h" #include "nsIPrefBranch.h" #include "nsIPrefService.h" #include "nsServiceManagerUtils.h" -#include "prnetdb.h" -#include "Tickler.h" #include "nsThreadUtils.h" - -#ifdef MOZ_USE_WIFI_TICKLER +#include "prnetdb.h" #include "AndroidBridge.h" diff --git a/netwerk/base/src/Tickler.h b/netwerk/base/src/Tickler.h index 75f42ae2441f..6b950ea421f0 100644 --- a/netwerk/base/src/Tickler.h +++ b/netwerk/base/src/Tickler.h @@ -27,6 +27,15 @@ // The tickler only applies to wifi on mobile right now. Hopefully it // can also be restricted to particular handset models in the future. +#if defined(ANDROID) && !defined(MOZ_B2G) +#define MOZ_USE_WIFI_TICKLER +#endif + +#include "mozilla/Attributes.h" +#include "nsISupports.h" +#include + +#ifdef MOZ_USE_WIFI_TICKLER #include "mozilla/Mutex.h" #include "mozilla/TimeStamp.h" #include "nsAutoPtr.h" @@ -34,16 +43,14 @@ #include "nsIThread.h" #include "nsITimer.h" #include "nsWeakReference.h" +#include "prio.h" class nsIPrefBranch; +#endif namespace mozilla { namespace net { -#if defined(ANDROID) && !defined(MOZ_B2G) -#define MOZ_USE_WIFI_TICKLER -#endif - #ifdef MOZ_USE_WIFI_TICKLER class Tickler MOZ_FINAL : public nsSupportsWeakReference diff --git a/netwerk/base/src/nsAsyncStreamCopier.cpp b/netwerk/base/src/nsAsyncStreamCopier.cpp index 1cb0c612942c..70a68584452e 100644 --- a/netwerk/base/src/nsAsyncStreamCopier.cpp +++ b/netwerk/base/src/nsAsyncStreamCopier.cpp @@ -2,11 +2,10 @@ * 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 "nsIOService.h" #include "nsAsyncStreamCopier.h" +#include "nsIOService.h" #include "nsIEventTarget.h" #include "nsStreamUtils.h" -#include "nsNetSegmentUtils.h" #include "nsNetUtil.h" #include "prlog.h" diff --git a/netwerk/base/src/nsAsyncStreamCopier.h b/netwerk/base/src/nsAsyncStreamCopier.h index 8f3bf174eb5b..38c2897e229b 100644 --- a/netwerk/base/src/nsAsyncStreamCopier.h +++ b/netwerk/base/src/nsAsyncStreamCopier.h @@ -6,13 +6,12 @@ #define nsAsyncStreamCopier_h__ #include "nsIAsyncStreamCopier.h" -#include "nsIAsyncInputStream.h" -#include "nsIAsyncOutputStream.h" -#include "nsIRequestObserver.h" #include "mozilla/Mutex.h" #include "nsStreamUtils.h" #include "nsCOMPtr.h" +class nsIRequestObserver; + //----------------------------------------------------------------------------- class nsAsyncStreamCopier : public nsIAsyncStreamCopier diff --git a/netwerk/base/src/nsBaseChannel.cpp b/netwerk/base/src/nsBaseChannel.cpp index 610cf52877d0..ada5ab567b5b 100644 --- a/netwerk/base/src/nsBaseChannel.cpp +++ b/netwerk/base/src/nsBaseChannel.cpp @@ -4,11 +4,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsBaseChannel.h" -#include "nsChannelProperties.h" #include "nsURLHelper.h" #include "nsNetUtil.h" #include "nsMimeTypes.h" -#include "nsIOService.h" #include "nsIHttpEventSink.h" #include "nsIHttpChannel.h" #include "nsIChannelEventSink.h" diff --git a/netwerk/base/src/nsBaseChannel.h b/netwerk/base/src/nsBaseChannel.h index b071082b7ce1..957f8acd1f38 100644 --- a/netwerk/base/src/nsBaseChannel.h +++ b/netwerk/base/src/nsBaseChannel.h @@ -13,7 +13,6 @@ #include "nsInputStreamPump.h" #include "nsIChannel.h" -#include "nsIInputStream.h" #include "nsIURI.h" #include "nsILoadGroup.h" #include "nsIStreamListener.h" @@ -25,6 +24,8 @@ #include "nsThreadUtils.h" #include "nsNetUtil.h" +class nsIInputStream; + //----------------------------------------------------------------------------- // nsBaseChannel is designed to be subclassed. The subclass is responsible for // implementing the OpenContentStream method, which will be called by the diff --git a/netwerk/base/src/nsBufferedStreams.cpp b/netwerk/base/src/nsBufferedStreams.cpp index 084bd63ddd1f..60d918ab2201 100644 --- a/netwerk/base/src/nsBufferedStreams.cpp +++ b/netwerk/base/src/nsBufferedStreams.cpp @@ -5,10 +5,8 @@ #include "ipc/IPCMessageUtils.h" -#include "nsAlgorithm.h" #include "nsBufferedStreams.h" #include "nsStreamUtils.h" -#include "nsCRT.h" #include "nsNetCID.h" #include "nsIClassInfoImpl.h" #include "mozilla/ipc/InputStreamUtils.h" diff --git a/netwerk/base/src/nsChannelClassifier.h b/netwerk/base/src/nsChannelClassifier.h index 35e255061122..8985ea9dd0b6 100644 --- a/netwerk/base/src/nsChannelClassifier.h +++ b/netwerk/base/src/nsChannelClassifier.h @@ -6,7 +6,6 @@ #define nsChannelClassifier_h__ #include "nsIURIClassifier.h" -#include "nsIRunnable.h" #include "nsCOMPtr.h" #include "mozilla/Attributes.h" diff --git a/netwerk/base/src/nsDNSPrefetch.cpp b/netwerk/base/src/nsDNSPrefetch.cpp index 9de5bf542c23..198dcc485c81 100644 --- a/netwerk/base/src/nsDNSPrefetch.cpp +++ b/netwerk/base/src/nsDNSPrefetch.cpp @@ -7,12 +7,10 @@ #include "nsCOMPtr.h" #include "nsString.h" -#include "nsNetUtil.h" - #include "nsIDNSListener.h" -#include "nsIDNSRecord.h" #include "nsIDNSService.h" #include "nsICancelable.h" +#include "nsIURI.h" static nsIDNSService *sDNSService = nullptr; diff --git a/netwerk/base/src/nsDNSPrefetch.h b/netwerk/base/src/nsDNSPrefetch.h index 7853993759e3..5fc4203d897e 100644 --- a/netwerk/base/src/nsDNSPrefetch.h +++ b/netwerk/base/src/nsDNSPrefetch.h @@ -6,7 +6,6 @@ #ifndef nsDNSPrefetch_h___ #define nsDNSPrefetch_h___ -#include "nsCOMPtr.h" #include "nsString.h" #include "mozilla/TimeStamp.h" #include "mozilla/Attributes.h" diff --git a/netwerk/base/src/nsDirectoryIndexStream.cpp b/netwerk/base/src/nsDirectoryIndexStream.cpp index a4021b7e5595..e5a93c83cacf 100644 --- a/netwerk/base/src/nsDirectoryIndexStream.cpp +++ b/netwerk/base/src/nsDirectoryIndexStream.cpp @@ -16,23 +16,20 @@ #include "nsEscape.h" #include "nsDirectoryIndexStream.h" -#include "nsXPIDLString.h" -#include "prio.h" #include "prlog.h" #ifdef PR_LOGGING static PRLogModuleInfo* gLog; #endif #include "nsISimpleEnumerator.h" +#ifdef THREADSAFE_I18N +#include "nsCollationCID.h" #include "nsICollation.h" #include "nsILocale.h" #include "nsILocaleService.h" -#include "nsCollationCID.h" -#include "nsIPlatformCharset.h" -#include "nsReadableUtils.h" +#endif +#include "nsIFile.h" #include "nsURLHelper.h" -#include "nsNetUtil.h" -#include "nsCRT.h" #include "nsNativeCharsetUtils.h" // NOTE: This runs on the _file transport_ thread. diff --git a/netwerk/base/src/nsDirectoryIndexStream.h b/netwerk/base/src/nsDirectoryIndexStream.h index 9de724bc5458..9f2cefbcabcd 100644 --- a/netwerk/base/src/nsDirectoryIndexStream.h +++ b/netwerk/base/src/nsDirectoryIndexStream.h @@ -8,12 +8,11 @@ #include "mozilla/Attributes.h" -#include "nsIFile.h" #include "nsString.h" #include "nsIInputStream.h" -#include "nsCOMPtr.h" #include "nsCOMArray.h" -#include "nsITextToSubURI.h" + +class nsIFile; class nsDirectoryIndexStream MOZ_FINAL : public nsIInputStream { diff --git a/netwerk/base/src/nsDownloader.h b/netwerk/base/src/nsDownloader.h index f348a3b2c27f..9f1520434d0c 100644 --- a/netwerk/base/src/nsDownloader.h +++ b/netwerk/base/src/nsDownloader.h @@ -6,10 +6,11 @@ #define nsDownloader_h__ #include "nsIDownloader.h" -#include "nsIOutputStream.h" -#include "nsIFile.h" #include "nsCOMPtr.h" +class nsIFile; +class nsIOutputStream; + class nsDownloader : public nsIDownloader { public: diff --git a/netwerk/base/src/nsFileStreams.cpp b/netwerk/base/src/nsFileStreams.cpp index 281d1e797e4b..bb32e876f7f4 100644 --- a/netwerk/base/src/nsFileStreams.cpp +++ b/netwerk/base/src/nsFileStreams.cpp @@ -19,16 +19,11 @@ #include "private/pprio.h" #include "nsFileStreams.h" -#include "nsXPIDLString.h" -#include "prerror.h" -#include "nsCRT.h" #include "nsIFile.h" -#include "nsDirectoryIndexStream.h" -#include "nsMimeTypes.h" #include "nsReadLine.h" -#include "nsNetUtil.h" #include "nsIClassInfoImpl.h" #include "mozilla/ipc/InputStreamUtils.h" +#include "nsNetCID.h" #define NS_NO_INPUT_BUFFERING 1 // see http://bugzilla.mozilla.org/show_bug.cgi?id=41067 diff --git a/netwerk/base/src/nsFileStreams.h b/netwerk/base/src/nsFileStreams.h index efab19a39b2d..f1a0a46e1e93 100644 --- a/netwerk/base/src/nsFileStreams.h +++ b/netwerk/base/src/nsFileStreams.h @@ -6,7 +6,6 @@ #ifndef nsFileStreams_h__ #define nsFileStreams_h__ -#include "nsAlgorithm.h" #include "nsAutoPtr.h" #include "nsIFileStreams.h" #include "nsIFile.h" @@ -16,8 +15,6 @@ #include "nsISeekableStream.h" #include "nsILineInputStream.h" #include "nsCOMPtr.h" -#include "prlog.h" -#include "prio.h" #include "nsIIPCSerializableInputStream.h" #include "nsReadLine.h" #include diff --git a/netwerk/base/src/nsIOService.cpp b/netwerk/base/src/nsIOService.cpp index 4f63495c5421..139838fb7f8a 100644 --- a/netwerk/base/src/nsIOService.cpp +++ b/netwerk/base/src/nsIOService.cpp @@ -10,40 +10,33 @@ #include "nsIProtocolHandler.h" #include "nsIFileProtocolHandler.h" #include "nscore.h" -#include "nsIServiceManager.h" #include "nsIURI.h" -#include "nsIStreamListener.h" #include "prprf.h" -#include "prlog.h" -#include "nsLoadGroup.h" -#include "nsInputStreamChannel.h" -#include "nsXPIDLString.h" -#include "nsReadableUtils.h" -#include "nsIErrorService.h" +#include "nsIErrorService.h" #include "netCore.h" #include "nsIObserverService.h" #include "nsIPrefService.h" -#include "nsIPrefLocalizedString.h" -#include "nsICategoryManager.h" #include "nsXPCOM.h" -#include "nsISupportsPrimitives.h" #include "nsIProxiedProtocolHandler.h" #include "nsIProxyInfo.h" #include "nsEscape.h" #include "nsNetCID.h" -#include "nsISocketTransport.h" #include "nsCRT.h" #include "nsSimpleNestedURI.h" #include "nsNetUtil.h" -#include "nsThreadUtils.h" -#include "nsIPermissionManager.h" #include "nsTArray.h" #include "nsIConsoleService.h" #include "nsIUploadChannel2.h" #include "nsXULAppAPI.h" -#include "nsIProxiedChannel.h" #include "nsIProtocolProxyCallback.h" #include "nsICancelable.h" +#include "nsINetworkLinkService.h" +#include "nsPISocketTransportService.h" +#include "nsAsyncRedirectVerifyHelper.h" +#include "nsURLHelper.h" +#include "nsPIDNSService.h" +#include "nsIProtocolProxyService2.h" +#include "MainThreadUtils.h" #if defined(XP_WIN) #include "nsNativeConnectionHelper.h" diff --git a/netwerk/base/src/nsIOService.h b/netwerk/base/src/nsIOService.h index 3e38aac2e265..f2a847c6755a 100644 --- a/netwerk/base/src/nsIOService.h +++ b/netwerk/base/src/nsIOService.h @@ -6,26 +6,16 @@ #ifndef nsIOService_h__ #define nsIOService_h__ -#include "necko-config.h" - -#include "nsString.h" +#include "nsStringFwd.h" #include "nsIIOService2.h" #include "nsTArray.h" -#include "nsPISocketTransportService.h" -#include "nsPIDNSService.h" -#include "nsIProtocolProxyService2.h" #include "nsCOMPtr.h" -#include "nsURLHelper.h" #include "nsWeakPtr.h" -#include "nsIURLParser.h" #include "nsIObserver.h" #include "nsWeakReference.h" #include "nsINetUtil.h" #include "nsIChannelEventSink.h" -#include "nsIContentSniffer.h" #include "nsCategoryCache.h" -#include "nsINetworkLinkService.h" -#include "nsAsyncRedirectVerifyHelper.h" #include "nsISpeculativeConnect.h" #include "mozilla/Attributes.h" @@ -39,7 +29,13 @@ static const char gScheme[][sizeof("resource")] = {"chrome", "file", "http", "jar", "resource"}; +class nsAsyncRedirectVerifyHelper; +class nsINetworkLinkService; class nsIPrefBranch; +class nsIProtocolProxyService2; +class nsIProxyInfo; +class nsPIDNSService; +class nsPISocketTransportService; class nsIOService MOZ_FINAL : public nsIIOService2 , public nsIObserver diff --git a/netwerk/base/src/nsIncrementalDownload.cpp b/netwerk/base/src/nsIncrementalDownload.cpp index 6f6a0036d4ff..946fa04aca5f 100644 --- a/netwerk/base/src/nsIncrementalDownload.cpp +++ b/netwerk/base/src/nsIncrementalDownload.cpp @@ -14,14 +14,11 @@ #include "nsIInterfaceRequestor.h" #include "nsIObserverService.h" #include "nsIObserver.h" -#include "nsIPropertyBag2.h" -#include "nsIServiceManager.h" #include "nsIFile.h" #include "nsITimer.h" #include "nsNetUtil.h" #include "nsAutoPtr.h" #include "nsWeakReference.h" -#include "nsChannelProperties.h" #include "prio.h" #include "prprf.h" #include diff --git a/netwerk/base/src/nsInputStreamPump.cpp b/netwerk/base/src/nsInputStreamPump.cpp index 65c8a59e5cbc..98c9bcbda60a 100644 --- a/netwerk/base/src/nsInputStreamPump.cpp +++ b/netwerk/base/src/nsInputStreamPump.cpp @@ -4,22 +4,19 @@ * 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 "mozilla/DebugOnly.h" #include "nsIOService.h" #include "nsInputStreamPump.h" -#include "nsIServiceManager.h" #include "nsIStreamTransportService.h" -#include "nsIInterfaceRequestorUtils.h" #include "nsISeekableStream.h" #include "nsITransport.h" #include "nsIThreadRetargetableStreamListener.h" -#include "nsStreamUtils.h" -#include "nsNetUtil.h" #include "nsThreadUtils.h" #include "nsCOMPtr.h" #include "prlog.h" -#include "nsPrintfCString.h" #include "GeckoProfiler.h" +#include "nsIStreamListener.h" +#include "nsILoadGroup.h" +#include "nsNetCID.h" #include static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID); diff --git a/netwerk/base/src/nsInputStreamPump.h b/netwerk/base/src/nsInputStreamPump.h index ffc7163bc70c..a379c78a8807 100644 --- a/netwerk/base/src/nsInputStreamPump.h +++ b/netwerk/base/src/nsInputStreamPump.h @@ -7,18 +7,15 @@ #define nsInputStreamPump_h__ #include "nsIInputStreamPump.h" -#include "nsIInputStream.h" -#include "nsIURI.h" -#include "nsILoadGroup.h" -#include "nsIStreamListener.h" -#include "nsIInterfaceRequestor.h" -#include "nsIProgressEventSink.h" #include "nsIAsyncInputStream.h" -#include "nsIThread.h" #include "nsIThreadRetargetableRequest.h" #include "nsCOMPtr.h" #include "mozilla/Attributes.h" +class nsIInputStream; +class nsILoadGroup; +class nsIStreamListener; + class nsInputStreamPump MOZ_FINAL : public nsIInputStreamPump , public nsIInputStreamCallback , public nsIThreadRetargetableRequest diff --git a/netwerk/base/src/nsLoadGroup.cpp b/netwerk/base/src/nsLoadGroup.cpp index bcc07a72dccb..e4a037f603fc 100644 --- a/netwerk/base/src/nsLoadGroup.cpp +++ b/netwerk/base/src/nsLoadGroup.cpp @@ -10,22 +10,17 @@ #include "nsArrayEnumerator.h" #include "nsCOMArray.h" -#include "nsEnumeratorUtils.h" -#include "nsIServiceManager.h" #include "nsCOMPtr.h" -#include "nsIURI.h" #include "prlog.h" -#include "nsCRT.h" -#include "netCore.h" -#include "nsXPIDLString.h" -#include "nsReadableUtils.h" #include "nsString.h" #include "nsTArray.h" -#include "nsIHttpChannelInternal.h" #include "mozilla/Atomics.h" #include "mozilla/Telemetry.h" #include "nsAutoPtr.h" #include "mozilla/net/PSpdyPush3.h" +#include "nsITimedChannel.h" +#include "nsIInterfaceRequestor.h" +#include "nsIRequestObserver.h" using namespace mozilla; diff --git a/netwerk/base/src/nsLoadGroup.h b/netwerk/base/src/nsLoadGroup.h index 32102c679cc1..988f3dbd07ae 100644 --- a/netwerk/base/src/nsLoadGroup.h +++ b/netwerk/base/src/nsLoadGroup.h @@ -9,20 +9,16 @@ #include "nsILoadGroup.h" #include "nsILoadGroupChild.h" #include "nsPILoadGroupInternal.h" -#include "nsIChannel.h" -#include "nsIStreamListener.h" #include "nsAgg.h" #include "nsCOMPtr.h" #include "nsWeakPtr.h" #include "nsWeakReference.h" -#include "nsIInterfaceRequestor.h" -#include "nsIInterfaceRequestorUtils.h" #include "nsISupportsPriority.h" -#include "nsITimedChannel.h" #include "pldhash.h" #include "mozilla/TimeStamp.h" -class nsILoadGroupConnectionInfo; +class nsILoadGroupConnectionInfo; +class nsITimedChannel; class nsLoadGroup : public nsILoadGroup, public nsILoadGroupChild, diff --git a/netwerk/base/src/nsMediaFragmentURIParser.cpp b/netwerk/base/src/nsMediaFragmentURIParser.cpp index d73a2f99414f..582148524ad9 100644 --- a/netwerk/base/src/nsMediaFragmentURIParser.cpp +++ b/netwerk/base/src/nsMediaFragmentURIParser.cpp @@ -7,6 +7,7 @@ #include "nsTArray.h" #include "nsCharSeparatedTokenizer.h" #include "nsEscape.h" +#include "nsIURI.h" #include #include "nsMediaFragmentURIParser.h" diff --git a/netwerk/base/src/nsMediaFragmentURIParser.h b/netwerk/base/src/nsMediaFragmentURIParser.h index dd0210eefb85..1b50d4461610 100644 --- a/netwerk/base/src/nsMediaFragmentURIParser.h +++ b/netwerk/base/src/nsMediaFragmentURIParser.h @@ -7,10 +7,11 @@ #define nsMediaFragmentURIParser_h__ #include "mozilla/Maybe.h" -#include "nsIURI.h" -#include "nsString.h" +#include "nsStringFwd.h" #include "nsRect.h" +class nsIURI; + // Class to handle parsing of a W3C media fragment URI as per // spec at: http://www.w3.org/TR/media-frags/ // Only the temporaral URI portion of the spec is implemented. diff --git a/netwerk/base/src/nsNetAddr.cpp b/netwerk/base/src/nsNetAddr.cpp index efbf7c2ec796..bd2babd179cc 100644 --- a/netwerk/base/src/nsNetAddr.cpp +++ b/netwerk/base/src/nsNetAddr.cpp @@ -6,7 +6,6 @@ #include "nsNetAddr.h" #include "nsString.h" -#include "prnetdb.h" #include "mozilla/net/DNS.h" using namespace mozilla::net; diff --git a/netwerk/base/src/nsPACMan.cpp b/netwerk/base/src/nsPACMan.cpp index be1a8e08123a..2ff015bde561 100644 --- a/netwerk/base/src/nsPACMan.cpp +++ b/netwerk/base/src/nsPACMan.cpp @@ -12,10 +12,8 @@ #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsNetUtil.h" -#include "nsCRT.h" -#include "prmon.h" #include "nsIAsyncVerifyRedirectCallback.h" -#include "nsProxyRelease.h" +#include "nsISystemProxySettings.h" //----------------------------------------------------------------------------- using namespace mozilla; diff --git a/netwerk/base/src/nsPACMan.h b/netwerk/base/src/nsPACMan.h index df1e2b8f8234..969b10657d98 100644 --- a/netwerk/base/src/nsPACMan.h +++ b/netwerk/base/src/nsPACMan.h @@ -11,19 +11,18 @@ #include "nsIInterfaceRequestor.h" #include "nsIChannelEventSink.h" #include "ProxyAutoConfig.h" -#include "nsICancelable.h" #include "nsThreadUtils.h" #include "nsIURI.h" #include "nsCOMPtr.h" #include "nsString.h" #include "mozilla/Attributes.h" #include "mozilla/LinkedList.h" -#include "nsIThread.h" #include "nsAutoPtr.h" -#include "nsISystemProxySettings.h" #include "mozilla/TimeStamp.h" class nsPACMan; +class nsISystemProxySettings; +class nsIThread; /** * This class defines a callback interface used by AsyncGetProxyForURI. diff --git a/netwerk/base/src/nsPreloadedStream.cpp b/netwerk/base/src/nsPreloadedStream.cpp index c1a09833820b..a1faefd093d1 100644 --- a/netwerk/base/src/nsPreloadedStream.cpp +++ b/netwerk/base/src/nsPreloadedStream.cpp @@ -7,7 +7,6 @@ #include "nsIRunnable.h" #include "nsThreadUtils.h" -#include "nsAlgorithm.h" #include namespace mozilla { diff --git a/netwerk/base/src/nsProtocolProxyService.cpp b/netwerk/base/src/nsProtocolProxyService.cpp index fe9dc0b1be89..6f7de63b7837 100644 --- a/netwerk/base/src/nsProtocolProxyService.cpp +++ b/netwerk/base/src/nsProtocolProxyService.cpp @@ -10,8 +10,6 @@ #include "nsProtocolProxyService.h" #include "nsProxyInfo.h" #include "nsIClassInfoImpl.h" -#include "nsIServiceManager.h" -#include "nsXPIDLString.h" #include "nsIIOService.h" #include "nsIObserverService.h" #include "nsIProtocolHandler.h" @@ -21,17 +19,16 @@ #include "nsPIDNSService.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" -#include "nsReadableUtils.h" #include "nsThreadUtils.h" #include "nsString.h" #include "nsNetUtil.h" #include "nsNetCID.h" -#include "nsCRT.h" #include "prnetdb.h" #include "nsPACMan.h" #include "nsProxyRelease.h" #include "mozilla/Mutex.h" #include "mozilla/CondVar.h" +#include "nsISystemProxySettings.h" //---------------------------------------------------------------------------- diff --git a/netwerk/base/src/nsProtocolProxyService.h b/netwerk/base/src/nsProtocolProxyService.h index 64aeba447c50..4108e3035a89 100644 --- a/netwerk/base/src/nsProtocolProxyService.h +++ b/netwerk/base/src/nsProtocolProxyService.h @@ -10,16 +10,12 @@ #include "nsCOMPtr.h" #include "nsAutoPtr.h" #include "nsTArray.h" -#include "nsIPrefBranch.h" #include "nsIProtocolProxyService2.h" #include "nsIProtocolProxyFilter.h" -#include "nsISystemProxySettings.h" #include "nsIProxyInfo.h" #include "nsIObserver.h" #include "nsDataHashtable.h" #include "nsHashKeys.h" -#include "nsPACMan.h" -#include "prtime.h" #include "prio.h" #include "mozilla/Attributes.h" @@ -27,6 +23,9 @@ typedef nsDataHashtable nsFailedProxyTable; class nsProxyInfo; struct nsProtocolInfo; +class nsIPrefBranch; +class nsISystemProxySettings; +class nsPACMan; class nsProtocolProxyService MOZ_FINAL : public nsIProtocolProxyService2 , public nsIObserver diff --git a/netwerk/base/src/nsRequestObserverProxy.cpp b/netwerk/base/src/nsRequestObserverProxy.cpp index cca7ae151852..1fd0151595e6 100644 --- a/netwerk/base/src/nsRequestObserverProxy.cpp +++ b/netwerk/base/src/nsRequestObserverProxy.cpp @@ -8,9 +8,7 @@ #include "nscore.h" #include "nsRequestObserverProxy.h" #include "nsIRequest.h" -#include "nsIServiceManager.h" #include "nsAutoPtr.h" -#include "nsString.h" #include "prlog.h" using namespace mozilla; diff --git a/netwerk/base/src/nsSerializationHelper.h b/netwerk/base/src/nsSerializationHelper.h index 77ab0db9ec28..a2304630f123 100644 --- a/netwerk/base/src/nsSerializationHelper.h +++ b/netwerk/base/src/nsSerializationHelper.h @@ -14,7 +14,6 @@ #include "mozilla/Attributes.h" class nsISerializable; -class nsISupports; /** * Serialize an object to an ASCII string. diff --git a/netwerk/base/src/nsServerSocket.cpp b/netwerk/base/src/nsServerSocket.cpp index 2e79d3ad9cdc..5d0f650ecbf6 100644 --- a/netwerk/base/src/nsServerSocket.cpp +++ b/netwerk/base/src/nsServerSocket.cpp @@ -3,7 +3,6 @@ * 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 "nsIServiceManager.h" #include "nsSocketTransport2.h" #include "nsServerSocket.h" #include "nsProxyRelease.h" diff --git a/netwerk/base/src/nsServerSocket.h b/netwerk/base/src/nsServerSocket.h index 79c2327bf5b2..6534b75a5af9 100644 --- a/netwerk/base/src/nsServerSocket.h +++ b/netwerk/base/src/nsServerSocket.h @@ -6,8 +6,8 @@ #ifndef nsServerSocket_h__ #define nsServerSocket_h__ +#include "nsASocketHandler.h" #include "nsIServerSocket.h" -#include "nsSocketTransportService2.h" #include "mozilla/Mutex.h" //----------------------------------------------------------------------------- diff --git a/netwerk/base/src/nsSimpleURI.cpp b/netwerk/base/src/nsSimpleURI.cpp index 76bba913334f..b6113ec9ac1a 100644 --- a/netwerk/base/src/nsSimpleURI.cpp +++ b/netwerk/base/src/nsSimpleURI.cpp @@ -9,10 +9,8 @@ #include "nsSimpleURI.h" #include "nscore.h" -#include "nsCRT.h" #include "nsString.h" -#include "nsReadableUtils.h" -#include "prprf.h" +#include "plstr.h" #include "nsURLHelper.h" #include "nsNetCID.h" #include "nsIObjectInputStream.h" diff --git a/netwerk/base/src/nsSimpleURI.h b/netwerk/base/src/nsSimpleURI.h index 94bffab1d807..46607b43b9d5 100644 --- a/netwerk/base/src/nsSimpleURI.h +++ b/netwerk/base/src/nsSimpleURI.h @@ -7,8 +7,7 @@ #define nsSimpleURI_h__ #include "mozilla/MemoryReporting.h" -#include "nsIURL.h" -#include "nsAgg.h" +#include "nsIURI.h" #include "nsISerializable.h" #include "nsString.h" #include "nsIClassInfo.h" diff --git a/netwerk/base/src/nsSocketTransport2.cpp b/netwerk/base/src/nsSocketTransport2.cpp index 5160f112d7b1..d09b46f2ed15 100644 --- a/netwerk/base/src/nsSocketTransport2.cpp +++ b/netwerk/base/src/nsSocketTransport2.cpp @@ -20,14 +20,11 @@ #include "nsNetUtil.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" -#include "netCore.h" #include "plstr.h" -#include "prnetdb.h" #include "prerr.h" #include "NetworkActivityMonitor.h" #include "mozilla/VisualEventTracer.h" #include "nsThreadUtils.h" -#include "nsIServiceManager.h" #include "nsISocketProviderService.h" #include "nsISocketProvider.h" #include "nsISSLSocketControl.h" @@ -35,6 +32,10 @@ #include "nsIPipe.h" #include "nsIProgrammingLanguage.h" #include "nsIClassInfoImpl.h" +#include "nsURLHelper.h" +#include "nsIDNSService.h" +#include "nsIDNSRecord.h" +#include "nsICancelable.h" #include #if defined(XP_WIN) diff --git a/netwerk/base/src/nsSocketTransport2.h b/netwerk/base/src/nsSocketTransport2.h index 96d9ec5c35e1..c957336ae16d 100644 --- a/netwerk/base/src/nsSocketTransport2.h +++ b/netwerk/base/src/nsSocketTransport2.h @@ -15,18 +15,19 @@ #include "nsCOMPtr.h" #include "nsISocketTransport.h" -#include "nsIInterfaceRequestor.h" #include "nsIAsyncInputStream.h" #include "nsIAsyncOutputStream.h" #include "nsIDNSListener.h" -#include "nsIDNSRecord.h" -#include "nsICancelable.h" #include "nsIClassInfo.h" #include "mozilla/net/DNS.h" +#include "nsASocketHandler.h" #include "prerror.h" class nsSocketTransport; +class nsICancelable; +class nsIDNSRecord; +class nsIInterfaceRequestor; nsresult ErrorAccordingToNSPR(PRErrorCode errorCode); diff --git a/netwerk/base/src/nsSocketTransportService2.cpp b/netwerk/base/src/nsSocketTransportService2.cpp index 727a42faa46f..918776dd8268 100644 --- a/netwerk/base/src/nsSocketTransportService2.cpp +++ b/netwerk/base/src/nsSocketTransportService2.cpp @@ -9,15 +9,12 @@ #include "nsSocketTransportService2.h" #include "nsSocketTransport2.h" -#include "nsReadableUtils.h" #include "nsError.h" #include "prnetdb.h" #include "prerror.h" -#include "plstr.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsServiceManagerUtils.h" -#include "nsIOService.h" #include "NetworkActivityMonitor.h" #include "nsIObserverService.h" #include "mozilla/Services.h" diff --git a/netwerk/base/src/nsSocketTransportService2.h b/netwerk/base/src/nsSocketTransportService2.h index 5150a2e3f6e6..3f729f18b855 100644 --- a/netwerk/base/src/nsSocketTransportService2.h +++ b/netwerk/base/src/nsSocketTransportService2.h @@ -11,16 +11,15 @@ #include "nsIRunnable.h" #include "nsEventQueue.h" #include "nsCOMPtr.h" -#include "pldhash.h" #include "prinrval.h" #include "prlog.h" #include "prinit.h" -#include "prio.h" -#include "nsASocketHandler.h" #include "nsIObserver.h" #include "mozilla/Mutex.h" #include "mozilla/net/DashboardTypes.h" +class nsASocketHandler; + //----------------------------------------------------------------------------- #if defined(PR_LOGGING) diff --git a/netwerk/base/src/nsStandardURL.cpp b/netwerk/base/src/nsStandardURL.cpp index fa921793adad..d2a0683539c5 100644 --- a/netwerk/base/src/nsStandardURL.cpp +++ b/netwerk/base/src/nsStandardURL.cpp @@ -7,8 +7,6 @@ #include "IPCMessageUtils.h" #include "nsStandardURL.h" -#include "nsDependentSubstring.h" -#include "nsReadableUtils.h" #include "nsCRT.h" #include "nsEscape.h" #include "nsIFile.h" @@ -18,11 +16,11 @@ #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsIIDNService.h" -#include "nsNetUtil.h" #include "prlog.h" #include "nsAutoPtr.h" #include "nsIProgrammingLanguage.h" -#include "nsVoidArray.h" +#include "nsIURLParser.h" +#include "nsNetCID.h" #include "mozilla/MemoryReporting.h" #include "mozilla/ipc/URIUtils.h" #include diff --git a/netwerk/base/src/nsStandardURL.h b/netwerk/base/src/nsStandardURL.h index 17c6bfc9e0c7..227f85e62688 100644 --- a/netwerk/base/src/nsStandardURL.h +++ b/netwerk/base/src/nsStandardURL.h @@ -7,16 +7,11 @@ #define nsStandardURL_h__ #include "nsString.h" -#include "nsDependentString.h" -#include "nsDependentSubstring.h" #include "nsISerializable.h" #include "nsIFileURL.h" #include "nsIStandardURL.h" -#include "nsIFile.h" -#include "nsIURLParser.h" #include "nsIUnicodeEncoder.h" #include "nsIObserver.h" -#include "nsIIOService.h" #include "nsCOMPtr.h" #include "nsURLHelper.h" #include "nsIClassInfo.h" @@ -35,6 +30,8 @@ class nsIBinaryOutputStream; class nsIIDNService; class nsICharsetConverterManager; class nsIPrefBranch; +class nsIFile; +class nsIURLParser; //----------------------------------------------------------------------------- // standard URL implementation diff --git a/netwerk/base/src/nsStreamListenerWrapper.cpp b/netwerk/base/src/nsStreamListenerWrapper.cpp index ada9a1d4890e..7815564d0b9c 100644 --- a/netwerk/base/src/nsStreamListenerWrapper.cpp +++ b/netwerk/base/src/nsStreamListenerWrapper.cpp @@ -3,7 +3,9 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsStreamListenerWrapper.h" -#include "nsThreadUtils.h" +#ifdef DEBUG +#include "MainThreadUtils.h" +#endif NS_IMPL_ISUPPORTS3(nsStreamListenerWrapper, nsIStreamListener, diff --git a/netwerk/base/src/nsStreamLoader.h b/netwerk/base/src/nsStreamLoader.h index a92fd4019305..c35bf70a794e 100644 --- a/netwerk/base/src/nsStreamLoader.h +++ b/netwerk/base/src/nsStreamLoader.h @@ -6,12 +6,12 @@ #ifndef nsStreamLoader_h__ #define nsStreamLoader_h__ -#include "nsIRequest.h" #include "nsIStreamLoader.h" #include "nsCOMPtr.h" -#include "nsString.h" #include "mozilla/Attributes.h" +class nsIRequest; + class nsStreamLoader MOZ_FINAL : public nsIStreamLoader { public: diff --git a/netwerk/base/src/nsStreamTransportService.cpp b/netwerk/base/src/nsStreamTransportService.cpp index 01c4c88c8877..361cb4f9c658 100644 --- a/netwerk/base/src/nsStreamTransportService.cpp +++ b/netwerk/base/src/nsStreamTransportService.cpp @@ -10,14 +10,13 @@ #include "nsError.h" #include "nsNetCID.h" -#include "nsIServiceManager.h" #include "nsIAsyncInputStream.h" #include "nsIAsyncOutputStream.h" #include "nsISeekableStream.h" #include "nsIPipe.h" #include "nsITransport.h" -#include "nsIRunnable.h" #include "nsIObserverService.h" +#include "nsIThreadPool.h" #include "mozilla/Services.h" //----------------------------------------------------------------------------- diff --git a/netwerk/base/src/nsStreamTransportService.h b/netwerk/base/src/nsStreamTransportService.h index b198640fa8cb..26915cb5a686 100644 --- a/netwerk/base/src/nsStreamTransportService.h +++ b/netwerk/base/src/nsStreamTransportService.h @@ -4,11 +4,12 @@ #include "nsIStreamTransportService.h" #include "nsIEventTarget.h" -#include "nsIThreadPool.h" #include "nsIObserver.h" #include "nsCOMPtr.h" #include "mozilla/Attributes.h" +class nsIThreadPool; + class nsStreamTransportService MOZ_FINAL : public nsIStreamTransportService , public nsIEventTarget , public nsIObserver diff --git a/netwerk/base/src/nsSyncStreamListener.cpp b/netwerk/base/src/nsSyncStreamListener.cpp index 3ab376772756..ddd0e7f5649f 100644 --- a/netwerk/base/src/nsSyncStreamListener.cpp +++ b/netwerk/base/src/nsSyncStreamListener.cpp @@ -5,6 +5,7 @@ #include "nsIOService.h" #include "nsSyncStreamListener.h" #include "nsIPipe.h" +#include "nsThreadUtils.h" #include nsresult diff --git a/netwerk/base/src/nsSyncStreamListener.h b/netwerk/base/src/nsSyncStreamListener.h index 474add18ce9b..4b3a4b246703 100644 --- a/netwerk/base/src/nsSyncStreamListener.h +++ b/netwerk/base/src/nsSyncStreamListener.h @@ -8,7 +8,6 @@ #include "nsISyncStreamListener.h" #include "nsIInputStream.h" #include "nsIOutputStream.h" -#include "nsThreadUtils.h" #include "nsCOMPtr.h" #include "mozilla/Attributes.h" diff --git a/netwerk/base/src/nsTemporaryFileInputStream.cpp b/netwerk/base/src/nsTemporaryFileInputStream.cpp index 030c21982e2c..72eef84eb482 100644 --- a/netwerk/base/src/nsTemporaryFileInputStream.cpp +++ b/netwerk/base/src/nsTemporaryFileInputStream.cpp @@ -5,6 +5,7 @@ #include "nsTemporaryFileInputStream.h" #include "nsStreamUtils.h" +#include NS_IMPL_ISUPPORTS1(nsTemporaryFileInputStream, nsIInputStream) diff --git a/netwerk/base/src/nsTemporaryFileInputStream.h b/netwerk/base/src/nsTemporaryFileInputStream.h index cb86146ec691..6c14e532e91a 100644 --- a/netwerk/base/src/nsTemporaryFileInputStream.h +++ b/netwerk/base/src/nsTemporaryFileInputStream.h @@ -6,8 +6,10 @@ #ifndef nsTemporaryFileInputStream_h__ #define nsTemporaryFileInputStream_h__ -#include "nsFileStreams.h" #include "mozilla/Mutex.h" +#include "nsIInputStream.h" +#include "nsAutoPtr.h" +#include "prio.h" class nsTemporaryFileInputStream : public nsIInputStream { diff --git a/netwerk/base/src/nsUDPServerSocket.cpp b/netwerk/base/src/nsUDPServerSocket.cpp index 3bf844ecb89f..526d1efdb38a 100644 --- a/netwerk/base/src/nsUDPServerSocket.cpp +++ b/netwerk/base/src/nsUDPServerSocket.cpp @@ -3,7 +3,6 @@ * 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 "nsIServiceManager.h" #include "nsSocketTransport2.h" #include "nsUDPServerSocket.h" #include "nsProxyRelease.h" @@ -19,7 +18,6 @@ #include "nsStreamUtils.h" #include "nsIPipe.h" #include "prerror.h" -#include "nsINSSErrorsService.h" #include "nsThreadUtils.h" using namespace mozilla::net; diff --git a/netwerk/base/src/nsUDPServerSocket.h b/netwerk/base/src/nsUDPServerSocket.h index 1f65ad152a91..f82235ce770c 100644 --- a/netwerk/base/src/nsUDPServerSocket.h +++ b/netwerk/base/src/nsUDPServerSocket.h @@ -7,7 +7,6 @@ #define nsUDPServerSocket_h__ #include "nsIUDPServerSocket.h" -#include "nsSocketTransportService2.h" #include "mozilla/Mutex.h" #include "nsIOutputStream.h" #include "nsAutoPtr.h" diff --git a/netwerk/base/src/nsURIChecker.cpp b/netwerk/base/src/nsURIChecker.cpp index 7b1814d10341..3832eb2e0971 100644 --- a/netwerk/base/src/nsURIChecker.cpp +++ b/netwerk/base/src/nsURIChecker.cpp @@ -4,7 +4,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsURIChecker.h" -#include "nsIServiceManager.h" #include "nsIAuthPrompt.h" #include "nsIHttpChannel.h" #include "nsNetUtil.h" diff --git a/netwerk/base/src/nsURIChecker.h b/netwerk/base/src/nsURIChecker.h index 77609ab01a6a..f2848335bf9b 100644 --- a/netwerk/base/src/nsURIChecker.h +++ b/netwerk/base/src/nsURIChecker.h @@ -11,8 +11,6 @@ #include "nsIStreamListener.h" #include "nsIChannelEventSink.h" #include "nsIInterfaceRequestor.h" -#include "nsIIOService.h" -#include "nsIURI.h" #include "nsCOMPtr.h" //----------------------------------------------------------------------------- diff --git a/netwerk/base/src/nsURLHelper.cpp b/netwerk/base/src/nsURLHelper.cpp index 9e6e267caea3..eab4a6a39fca 100644 --- a/netwerk/base/src/nsURLHelper.cpp +++ b/netwerk/base/src/nsURLHelper.cpp @@ -7,19 +7,11 @@ #include "mozilla/RangedPtr.h" #include "nsURLHelper.h" -#include "nsReadableUtils.h" -#include "nsIServiceManager.h" -#include "nsIIOService.h" #include "nsIFile.h" #include "nsIURLParser.h" -#include "nsIURI.h" -#include "nsMemory.h" -#include "nsEscape.h" #include "nsCOMPtr.h" #include "nsCRT.h" #include "nsNetCID.h" -#include "netCore.h" -#include "prprf.h" #include "prnetdb.h" using namespace mozilla; diff --git a/netwerk/base/src/nsURLParsers.cpp b/netwerk/base/src/nsURLParsers.cpp index 5b4984b8dde1..363c75c31d7c 100644 --- a/netwerk/base/src/nsURLParsers.cpp +++ b/netwerk/base/src/nsURLParsers.cpp @@ -9,11 +9,9 @@ #include "nsURLParsers.h" #include "nsURLHelper.h" -#include "nsIURI.h" #include "prtypes.h" #include "nsString.h" #include "nsCRT.h" -#include "netCore.h" using namespace mozilla; diff --git a/netwerk/base/src/nsUnicharStreamLoader.cpp b/netwerk/base/src/nsUnicharStreamLoader.cpp index 0898e39ebd5d..21200d8e8270 100644 --- a/netwerk/base/src/nsUnicharStreamLoader.cpp +++ b/netwerk/base/src/nsUnicharStreamLoader.cpp @@ -8,7 +8,7 @@ #include "nsUnicharStreamLoader.h" #include "nsIInputStream.h" #include "nsICharsetConverterManager.h" -#include "nsIServiceManager.h" +#include "nsServiceManagerUtils.h" #include // 1024 bytes is specified in diff --git a/netwerk/build/nsNetModule.cpp b/netwerk/build/nsNetModule.cpp index 3f7577314715..5f697f206086 100644 --- a/netwerk/build/nsNetModule.cpp +++ b/netwerk/build/nsNetModule.cpp @@ -37,6 +37,7 @@ #include "nsCategoryCache.h" #include "nsIContentSniffer.h" #include "nsNetUtil.h" +#include "nsIThreadPool.h" #include "mozilla/net/NeckoChild.h" #include "nsNetCID.h" diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp index 2f0519e9be29..82bde54438ab 100644 --- a/netwerk/protocol/http/nsHttpTransaction.cpp +++ b/netwerk/protocol/http/nsHttpTransaction.cpp @@ -31,6 +31,7 @@ #include "nsServiceManagerUtils.h" // do_GetService #include "nsIHttpActivityObserver.h" #include "nsSocketTransportService2.h" +#include "nsICancelable.h" #include