From fa3e172e3c7df15245d006387a3f38107adcd9f1 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 18 May 2017 11:56:27 +0200 Subject: [PATCH 01/35] Bug 1365598 - Updating the Constructor of HTMLOptionElement, r=smaug --- dom/html/HTMLOptionElement.cpp | 59 ++++++++++++++--------------- dom/html/HTMLOptionElement.h | 7 ++-- dom/webidl/HTMLOptionElement.webidl | 2 +- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/dom/html/HTMLOptionElement.cpp b/dom/html/HTMLOptionElement.cpp index 5d1d17170d4d..44615650f27d 100644 --- a/dom/html/HTMLOptionElement.cpp +++ b/dom/html/HTMLOptionElement.cpp @@ -372,10 +372,11 @@ HTMLOptionElement::GetSelect() already_AddRefed HTMLOptionElement::Option(const GlobalObject& aGlobal, - const Optional& aText, + const nsAString& aText, const Optional& aValue, - const Optional& aDefaultSelected, - const Optional& aSelected, ErrorResult& aError) + bool aDefaultSelected, + bool aSelected, + ErrorResult& aError) { nsCOMPtr win = do_QueryInterface(aGlobal.GetAsSupports()); nsIDocument* doc; @@ -391,45 +392,43 @@ HTMLOptionElement::Option(const GlobalObject& aGlobal, RefPtr option = new HTMLOptionElement(nodeInfo); - if (aText.WasPassed()) { + if (!aText.IsEmpty()) { // Create a new text node and append it to the option RefPtr textContent = new nsTextNode(option->NodeInfo()->NodeInfoManager()); - textContent->SetText(aText.Value(), false); + textContent->SetText(aText, false); aError = option->AppendChildTo(textContent, false); if (aError.Failed()) { return nullptr; } + } - if (aValue.WasPassed()) { - // Set the value attribute for this element. We're calling SetAttr - // directly because we want to pass aNotify == false. - aError = option->SetAttr(kNameSpaceID_None, nsGkAtoms::value, - aValue.Value(), false); - if (aError.Failed()) { - return nullptr; - } + if (aValue.WasPassed()) { + // Set the value attribute for this element. We're calling SetAttr + // directly because we want to pass aNotify == false. + aError = option->SetAttr(kNameSpaceID_None, nsGkAtoms::value, + aValue.Value(), false); + if (aError.Failed()) { + return nullptr; + } + } - if (aDefaultSelected.WasPassed()) { - if (aDefaultSelected.Value()) { - // We're calling SetAttr directly because we want to pass - // aNotify == false. - aError = option->SetAttr(kNameSpaceID_None, nsGkAtoms::selected, - EmptyString(), false); - if (aError.Failed()) { - return nullptr; - } - } + if (aDefaultSelected) { + // We're calling SetAttr directly because we want to pass + // aNotify == false. + aError = option->SetAttr(kNameSpaceID_None, nsGkAtoms::selected, + EmptyString(), false); + if (aError.Failed()) { + return nullptr; + } + } - if (aSelected.WasPassed()) { - option->SetSelected(aSelected.Value(), aError); - if (aError.Failed()) { - return nullptr; - } - } - } + if (aSelected) { + option->SetSelected(true, aError); + if (aError.Failed()) { + return nullptr; } } diff --git a/dom/html/HTMLOptionElement.h b/dom/html/HTMLOptionElement.h index 51c866f3f4f5..6e893762888d 100644 --- a/dom/html/HTMLOptionElement.h +++ b/dom/html/HTMLOptionElement.h @@ -25,10 +25,11 @@ public: static already_AddRefed Option(const GlobalObject& aGlobal, - const Optional& aText, + const nsAString& aText, const Optional& aValue, - const Optional& aDefaultSelected, - const Optional& aSelected, ErrorResult& aError); + bool aDefaultSelected, + bool aSelected, + ErrorResult& aError); NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLOptionElement, option) diff --git a/dom/webidl/HTMLOptionElement.webidl b/dom/webidl/HTMLOptionElement.webidl index 34e6e6c23e7d..907a37047024 100644 --- a/dom/webidl/HTMLOptionElement.webidl +++ b/dom/webidl/HTMLOptionElement.webidl @@ -11,7 +11,7 @@ * and create derivative works of this document. */ -[HTMLConstructor, NamedConstructor=Option(optional DOMString text, optional DOMString value, optional boolean defaultSelected, optional boolean selected)] +[HTMLConstructor, NamedConstructor=Option(optional DOMString text = "", optional DOMString value, optional boolean defaultSelected = false, optional boolean selected = false)] interface HTMLOptionElement : HTMLElement { [SetterThrows] attribute boolean disabled; From b96a934bb0eadf99f4e4c9d87e147654945c55c5 Mon Sep 17 00:00:00 2001 From: Shawn Huang Date: Thu, 18 May 2017 19:02:22 +0800 Subject: [PATCH 02/35] Bug 1365478 - Use BoolVarCache to cache preferences in XMLHttpRequest, r=baku --- dom/xhr/XMLHttpRequestMainThread.cpp | 38 ++++++++++++++++++++++++++-- dom/xhr/XMLHttpRequestMainThread.h | 8 ++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/dom/xhr/XMLHttpRequestMainThread.cpp b/dom/xhr/XMLHttpRequestMainThread.cpp index 38313469103d..f8e589fec1b6 100644 --- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -2948,7 +2948,7 @@ XMLHttpRequestMainThread::SendInternal(const BodyExtractorBase* aBody) mIsMappedArrayBuffer = false; if (mResponseType == XMLHttpRequestResponseType::Arraybuffer && - Preferences::GetBool("dom.mapped_arraybuffer.enabled", true)) { + IsMappedArrayBufferEnabled()) { nsCOMPtr uri; nsAutoCString scheme; @@ -3049,6 +3049,40 @@ XMLHttpRequestMainThread::SendInternal(const BodyExtractorBase* aBody) return rv; } +/* static */ +bool +XMLHttpRequestMainThread::IsMappedArrayBufferEnabled() +{ + static bool sMappedArrayBufferAdded = false; + static bool sIsMappedArrayBufferEnabled; + + if (!sMappedArrayBufferAdded) { + Preferences::AddBoolVarCache(&sIsMappedArrayBufferEnabled, + "dom.mapped_arraybuffer.enabled", + true); + sMappedArrayBufferAdded = true; + } + + return sIsMappedArrayBufferEnabled; +} + +/* static */ +bool +XMLHttpRequestMainThread::IsLowercaseResponseHeader() +{ + static bool sLowercaseResponseHeaderAdded = false; + static bool sIsLowercaseResponseHeaderEnabled; + + if (!sLowercaseResponseHeaderAdded) { + Preferences::AddBoolVarCache(&sIsLowercaseResponseHeaderEnabled, + "dom.xhr.lowercase_header.enabled", + false); + sLowercaseResponseHeaderAdded = true; + } + + return sIsLowercaseResponseHeaderEnabled; +} + // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader NS_IMETHODIMP XMLHttpRequestMainThread::SetRequestHeader(const nsACString& aName, @@ -3767,7 +3801,7 @@ NS_IMETHODIMP XMLHttpRequestMainThread:: nsHeaderVisitor::VisitHeader(const nsACString &header, const nsACString &value) { if (mXHR.IsSafeHeader(header, mHttpChannel)) { - if (!Preferences::GetBool("dom.xhr.lowercase_header.enabled", false)) { + if (!IsLowercaseResponseHeader()) { if(!mHeaderList.InsertElementSorted(HeaderEntry(header, value), fallible)) { return NS_ERROR_OUT_OF_MEMORY; diff --git a/dom/xhr/XMLHttpRequestMainThread.h b/dom/xhr/XMLHttpRequestMainThread.h index 9821a8baf358..637aa164c1b1 100644 --- a/dom/xhr/XMLHttpRequestMainThread.h +++ b/dom/xhr/XMLHttpRequestMainThread.h @@ -306,6 +306,14 @@ private: void UnsuppressEventHandlingAndResume(); + // Check pref "dom.mapped_arraybuffer.enabled" to make sure ArrayBuffer is + // supported. + static bool IsMappedArrayBufferEnabled(); + + // Check pref "dom.xhr.lowercase_header.enabled" to make sure lowercased + // response header is supported. + static bool IsLowercaseResponseHeader(); + public: virtual void Send(JSContext* /*aCx*/, ErrorResult& aRv) override From 73b521bf6ceb6863c75d125fd3647108373ff1b7 Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Thu, 18 May 2017 12:08:56 +0100 Subject: [PATCH 03/35] Bug 1351358 Part 1: Add flag to nsISHEntry to indicate if it was originally loaded in this process. r=bz This flag is for when we've loaded a URI in a remote type that is not the default for compatibility reasons (for example related http in the file content process). So that we can load the history entry in that same process as well. --- docshell/shistory/nsISHEntry.idl | 6 ++++++ docshell/shistory/nsSHEntry.cpp | 11 +++++++++++ docshell/shistory/nsSHEntry.h | 1 + 3 files changed, 18 insertions(+) diff --git a/docshell/shistory/nsISHEntry.idl b/docshell/shistory/nsISHEntry.idl index b0b8b8b7eaac..2679cc735e96 100644 --- a/docshell/shistory/nsISHEntry.idl +++ b/docshell/shistory/nsISHEntry.idl @@ -328,6 +328,12 @@ interface nsISHEntry : nsISupports */ attribute boolean scrollRestorationIsManual; + /** + * Flag to indicate that the history entry was originally loaded in the + * current process. This flag does not survive a browser process switch. + */ + readonly attribute boolean loadedInThisProcess; + /** * Set the session history it belongs to. It's only set on root entries. */ diff --git a/docshell/shistory/nsSHEntry.cpp b/docshell/shistory/nsSHEntry.cpp index 3df9f2fb1b7c..192a876a389a 100644 --- a/docshell/shistory/nsSHEntry.cpp +++ b/docshell/shistory/nsSHEntry.cpp @@ -37,6 +37,7 @@ nsSHEntry::nsSHEntry() , mURIWasModified(false) , mIsSrcdocEntry(false) , mScrollRestorationIsManual(false) + , mLoadedInThisProcess(false) { } @@ -60,6 +61,7 @@ nsSHEntry::nsSHEntry(const nsSHEntry& aOther) , mURIWasModified(aOther.mURIWasModified) , mIsSrcdocEntry(aOther.mIsSrcdocEntry) , mScrollRestorationIsManual(false) + , mLoadedInThisProcess(aOther.mLoadedInThisProcess) { } @@ -460,6 +462,8 @@ nsSHEntry::Create(nsIURI* aURI, const nsAString& aTitle, mIsSrcdocEntry = false; mSrcdocData = NullString(); + mLoadedInThisProcess = true; + return NS_OK; } @@ -645,6 +649,13 @@ nsSHEntry::SetScrollRestorationIsManual(bool aIsManual) return NS_OK; } +NS_IMETHODIMP +nsSHEntry::GetLoadedInThisProcess(bool* aLoadedInThisProcess) +{ + *aLoadedInThisProcess = mLoadedInThisProcess; + return NS_OK; +} + NS_IMETHODIMP nsSHEntry::GetChildCount(int32_t* aCount) { diff --git a/docshell/shistory/nsSHEntry.h b/docshell/shistory/nsSHEntry.h index f5dfcb2ca789..9c1f4587b982 100644 --- a/docshell/shistory/nsSHEntry.h +++ b/docshell/shistory/nsSHEntry.h @@ -64,6 +64,7 @@ private: bool mURIWasModified; bool mIsSrcdocEntry; bool mScrollRestorationIsManual; + bool mLoadedInThisProcess; }; #endif /* nsSHEntry_h */ From 47421837bf1fa8b7521be5c9e9891e00680c2301 Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Thu, 18 May 2017 12:08:56 +0100 Subject: [PATCH 04/35] Bug 1351358 Part 2: Limit the http pages that will load in the file content process with allowLinkedWebInFileUriProcess pref. r=Gijs, r=mystor This change means that any related http pages driven through content (window.open, links, etc.) will continue to be loaded in the file content process. Same-origin loads via the UI will also remain in the file content process. Cross-origin loads via the UI will cause a process switch. History navigation will stay in the process, if it was originally loaded in that process. --- browser/base/content/browser.js | 4 +- browser/base/content/tabbrowser.xml | 1 + .../components/sessionstore/SessionStore.jsm | 65 ++++++++++--------- browser/modules/E10SUtils.jsm | 46 +++++++++++-- 4 files changed, 76 insertions(+), 40 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index fb05b42ff935..00d854c3eed9 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1017,7 +1017,8 @@ function _loadURIWithFlags(browser, uri, params) { let currentRemoteType = browser.remoteType; let requiredRemoteType = - E10SUtils.getRemoteTypeForURI(uri, gMultiProcessBrowser, currentRemoteType); + E10SUtils.getRemoteTypeForURI(uri, gMultiProcessBrowser, currentRemoteType, + browser.currentURI); let mustChangeProcess = requiredRemoteType != currentRemoteType; // !requiredRemoteType means we're loading in the parent/this process. @@ -1052,6 +1053,7 @@ function _loadURIWithFlags(browser, uri, params) { flags, referrer: referrer ? referrer.spec : null, referrerPolicy, + remoteType: requiredRemoteType, postData } diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 63a49afe1b85..e5278d47c6c8 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1854,6 +1854,7 @@ E10SUtils.getRemoteTypeForURI(aURL, gMultiProcessBrowser, preferredRemoteType, + aBrowser.currentURI, aOptions.freshProcess); // If this URL can't load in the current browser then flip it to the diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index c53f1f4bc3ee..eaee455af04f 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -2888,6 +2888,7 @@ var SessionStoreInternal = { // whether or not a historyIndex is passed in. Thus, we extract it from // the loadArguments. reloadInFreshProcess: !!recentLoadArguments.reloadInFreshProcess, + remoteType: recentLoadArguments.remoteType, // Make sure that SessionStore knows that this restoration is due // to a navigation, as opposed to us restoring a closed window or tab. restoreContentReason: RESTORE_TAB_CONTENT_REASON.NAVIGATE_AND_RESTORE, @@ -3536,18 +3537,14 @@ var SessionStoreInternal = { NS_ASSERT(!tab.linkedBrowser.__SS_restoreState, "must reset tab before calling restoreTab()"); - let restoreImmediately = options.restoreImmediately; let loadArguments = options.loadArguments; let browser = tab.linkedBrowser; let window = tab.ownerGlobal; let tabbrowser = window.gBrowser; let forceOnDemand = options.forceOnDemand; - let reloadInFreshProcess = options.reloadInFreshProcess; - let restoreContentReason = options.restoreContentReason; - let willRestoreImmediately = restoreImmediately || - tabbrowser.selectedBrowser == browser || - loadArguments; + let willRestoreImmediately = options.restoreImmediately || + tabbrowser.selectedBrowser == browser; let isBrowserInserted = browser.isConnected; @@ -3666,8 +3663,7 @@ var SessionStoreInternal = { // This could cause us to ignore MAX_CONCURRENT_TAB_RESTORES a bit, but // it ensures each window will have its selected tab loaded. if (willRestoreImmediately) { - this.restoreTabContent(tab, loadArguments, reloadInFreshProcess, - restoreContentReason); + this.restoreTabContent(tab, options); } else if (!forceOnDemand) { TabRestoreQueue.add(tab); this.restoreNextTab(); @@ -3709,18 +3705,12 @@ var SessionStoreInternal = { * * @param aTab * the tab to restore - * @param aLoadArguments - * optional load arguments used for loadURI() - * @param aReloadInFreshProcess - * true if we want to reload into a fresh process - * @param aReason - * The reason for why this tab content is being restored. - * Should be one of the values within RESTORE_TAB_CONTENT_REASON. - * Defaults to RESTORE_TAB_CONTENT_REASON.SET_STATE. + * @param aOptions + * optional arguments used when performing process switch during load */ - restoreTabContent(aTab, aLoadArguments = null, aReloadInFreshProcess = false, - aReason = RESTORE_TAB_CONTENT_REASON.SET_STATE) { - if (aTab.hasAttribute("customizemode") && !aLoadArguments) { + restoreTabContent(aTab, aOptions = {}) { + let loadArguments = aOptions.loadArguments; + if (aTab.hasAttribute("customizemode") && !loadArguments) { return; } @@ -3731,10 +3721,10 @@ var SessionStoreInternal = { let activeIndex = tabData.index - 1; let activePageData = tabData.entries[activeIndex] || null; let uri = activePageData ? activePageData.url || null : null; - if (aLoadArguments) { - uri = aLoadArguments.uri; - if (aLoadArguments.userContextId) { - browser.setAttribute("usercontextid", aLoadArguments.userContextId); + if (loadArguments) { + uri = loadArguments.uri; + if (loadArguments.userContextId) { + browser.setAttribute("usercontextid", loadArguments.userContextId); } } @@ -3748,12 +3738,21 @@ var SessionStoreInternal = { // process, or we have a browser with a grouped session history (as we don't // support restoring into browsers with grouped session histories directly). let newFrameloader = - aReloadInFreshProcess || !!browser.frameLoader.groupedSHistory; - let isRemotenessUpdate = - tabbrowser.updateBrowserRemotenessByURL(browser, uri, { - freshProcess: aReloadInFreshProcess, - newFrameloader, - }); + aOptions.reloadInFreshProcess || !!browser.frameLoader.groupedSHistory; + + let isRemotenessUpdate; + if (aOptions.remoteType !== undefined) { + // We already have a selected remote type so we update to that. + isRemotenessUpdate = + tabbrowser.updateBrowserRemoteness(browser, !!aOptions.remoteType, + { remoteType: aOptions.remoteType, + newFrameloader }); + } else { + isRemotenessUpdate = + tabbrowser.updateBrowserRemotenessByURL(browser, uri, { + newFrameloader, + }); + } if (isRemotenessUpdate) { // We updated the remoteness, so we need to send the history down again. @@ -3766,7 +3765,7 @@ var SessionStoreInternal = { this._sendRestoreHistory(browser, { tabData, epoch, - loadArguments: aLoadArguments, + loadArguments, isRemotenessUpdate, }); } @@ -3778,8 +3777,10 @@ var SessionStoreInternal = { } browser.messageManager.sendAsyncMessage("SessionStore:restoreTabContent", - {loadArguments: aLoadArguments, isRemotenessUpdate, - reason: aReason, requestTime: Services.telemetry.msSystemNow()}); + {loadArguments, isRemotenessUpdate, + reason: aOptions.restoreContentReason || + RESTORE_TAB_CONTENT_REASON.SET_STATE, + requestTime: Services.telemetry.msSystemNow()}); }, /** diff --git a/browser/modules/E10SUtils.jsm b/browser/modules/E10SUtils.jsm index 18e0936a6b7d..8d3956849a50 100644 --- a/browser/modules/E10SUtils.jsm +++ b/browser/modules/E10SUtils.jsm @@ -46,7 +46,7 @@ const EXTENSION_REMOTE_TYPE = "extension"; const LARGE_ALLOCATION_REMOTE_TYPE = "webLargeAllocation"; const DEFAULT_REMOTE_TYPE = WEB_REMOTE_TYPE; -function validatedWebRemoteType(aPreferredRemoteType) { +function validatedWebRemoteType(aPreferredRemoteType, aTargetUri, aCurrentUri) { if (!aPreferredRemoteType) { return WEB_REMOTE_TYPE; } @@ -57,7 +57,20 @@ function validatedWebRemoteType(aPreferredRemoteType) { if (allowLinkedWebInFileUriProcess && aPreferredRemoteType == FILE_REMOTE_TYPE) { - return aPreferredRemoteType; + // If aCurrentUri is passed then we should only allow FILE_REMOTE_TYPE + // when it is same origin as target. + if (aCurrentUri) { + const sm = Services.scriptSecurityManager; + try { + // checkSameOriginURI throws when not same origin. + sm.checkSameOriginURI(aCurrentUri, aTargetUri, false); + return FILE_REMOTE_TYPE; + } catch (e) { + return WEB_REMOTE_TYPE; + } + } + + return FILE_REMOTE_TYPE; } return WEB_REMOTE_TYPE; @@ -79,6 +92,7 @@ this.E10SUtils = { getRemoteTypeForURI(aURL, aMultiProcess, aPreferredRemoteType = DEFAULT_REMOTE_TYPE, + aCurrentUri, aLargeAllocation = false) { if (!aMultiProcess) { return NOT_REMOTE; @@ -95,7 +109,7 @@ this.E10SUtils = { let uri; try { - uri = Services.io.newURI(aURL); + uri = Services.uriFixup.createFixupURI(aURL, Ci.nsIURIFixup.FIXUP_FLAG_NONE); } catch (e) { // If we have an invalid URI, it's still possible that it might get // fixed-up into a valid URI later on. However, we don't want to return @@ -105,11 +119,12 @@ this.E10SUtils = { } return this.getRemoteTypeForURIObject(uri, aMultiProcess, - aPreferredRemoteType); + aPreferredRemoteType, aCurrentUri); }, getRemoteTypeForURIObject(aURI, aMultiProcess, - aPreferredRemoteType = DEFAULT_REMOTE_TYPE) { + aPreferredRemoteType = DEFAULT_REMOTE_TYPE, + aCurrentUri) { if (!aMultiProcess) { return NOT_REMOTE; } @@ -182,10 +197,11 @@ this.E10SUtils = { if (aURI instanceof Ci.nsINestedURI) { let innerURI = aURI.QueryInterface(Ci.nsINestedURI).innerURI; return this.getRemoteTypeForURIObject(innerURI, aMultiProcess, - aPreferredRemoteType); + aPreferredRemoteType, + aCurrentUri); } - return validatedWebRemoteType(aPreferredRemoteType); + return validatedWebRemoteType(aPreferredRemoteType, aURI, aCurrentUri); } }, @@ -210,6 +226,22 @@ this.E10SUtils = { return false; } + // Allow history load if loaded in this process before. + let webNav = aDocShell.QueryInterface(Ci.nsIWebNavigation); + let sessionHistory = webNav.sessionHistory; + let requestedIndex = sessionHistory.requestedIndex; + if (requestedIndex >= 0) { + if (sessionHistory.getEntryAtIndex(requestedIndex, false).loadedInThisProcess) { + return true; + } + + // If not originally loaded in this process allow it if the URI would + // normally be allowed to load in this process by default. + let remoteType = Services.appinfo.remoteType; + return remoteType == + this.getRemoteTypeForURIObject(aURI, true, remoteType, webNav.currentURI); + } + // If the URI can be loaded in the current process then continue return this.shouldLoadURIInThisProcess(aURI); }, From d0c57d84c12b1475e9f7c5dc7312a7872001f91b Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Thu, 18 May 2017 12:08:56 +0100 Subject: [PATCH 05/35] Bug 1351358 Part 2.5: Move logic in the parent for switching in and out of Large Allocation process into browser.js RedirectLoad. r=mystor This fixes a bug where we swap out of the Large Allocation process, when reloaded via the UI. --- browser/base/content/browser.js | 12 ++++++++++++ browser/base/content/tabbrowser.xml | 12 ++---------- browser/components/sessionstore/SessionStore.jsm | 10 +++++----- browser/modules/E10SUtils.jsm | 7 +------ 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 00d854c3eed9..bb5f420603cf 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1099,6 +1099,18 @@ function LoadInOtherProcess(browser, loadOptions, historyIndex = -1) { // Called when a docshell has attempted to load a page in an incorrect process. // This function is responsible for loading the page in the correct process. function RedirectLoad({ target: browser, data }) { + if (data.loadOptions.reloadInFreshProcess) { + // Convert the fresh process load option into a large allocation remote type + // to use common processing from this point. + data.loadOptions.remoteType = E10SUtils.LARGE_ALLOCATION_REMOTE_TYPE; + data.loadOptions.newFrameloader = true; + } else if (browser.remoteType == E10SUtils.LARGE_ALLOCATION_REMOTE_TYPE) { + // If we're in a Large-Allocation process, we prefer switching back into a + // normal content process, as that way we can clean up the L-A process. + data.loadOptions.remoteType = + E10SUtils.getRemoteTypeForURI(data.loadOptions.uri, gMultiProcessBrowser); + } + // We should only start the redirection if the browser window has finished // starting up. Otherwise, we should wait until the startup is done. if (gBrowserInit.delayedStartupFinished) { diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index e5278d47c6c8..bea66300b69a 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1841,21 +1841,13 @@ if (!gMultiProcessBrowser) return this.updateBrowserRemoteness(aBrowser, false); - // If we're in a LargeAllocation process, we prefer switching back - // into a normal content process, as that way we can clean up the - // L-A process. let currentRemoteType = aBrowser.getAttribute("remoteType") || null; - let preferredRemoteType = currentRemoteType; - if (currentRemoteType == E10SUtils.LARGE_ALLOCATION_REMOTE_TYPE) { - preferredRemoteType = E10SUtils.DEFAULT_REMOTE_TYPE; - } aOptions.remoteType = E10SUtils.getRemoteTypeForURI(aURL, gMultiProcessBrowser, - preferredRemoteType, - aBrowser.currentURI, - aOptions.freshProcess); + currentRemoteType, + aBrowser.currentURI); // If this URL can't load in the current browser then flip it to the // correct type. diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index eaee455af04f..321c56bd8788 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -2887,7 +2887,7 @@ var SessionStoreInternal = { // We want to make sure that this information is passed to restoreTab // whether or not a historyIndex is passed in. Thus, we extract it from // the loadArguments. - reloadInFreshProcess: !!recentLoadArguments.reloadInFreshProcess, + newFrameloader: recentLoadArguments.newFrameloader, remoteType: recentLoadArguments.remoteType, // Make sure that SessionStore knows that this restoration is due // to a navigation, as opposed to us restoring a closed window or tab. @@ -3734,11 +3734,11 @@ var SessionStoreInternal = { // flip the remoteness of any browser that is not being displayed. this.markTabAsRestoring(aTab); - // We need a new frameloader either if we are reloading into a fresh - // process, or we have a browser with a grouped session history (as we don't - // support restoring into browsers with grouped session histories directly). + // We need a new frameloader if we are reloading into a browser with a + // grouped session history (as we don't support restoring into browsers + // with grouped session histories directly). let newFrameloader = - aOptions.reloadInFreshProcess || !!browser.frameLoader.groupedSHistory; + aOptions.newFrameloader || !!browser.frameLoader.groupedSHistory; let isRemotenessUpdate; if (aOptions.remoteType !== undefined) { diff --git a/browser/modules/E10SUtils.jsm b/browser/modules/E10SUtils.jsm index 8d3956849a50..da0e18c0186c 100644 --- a/browser/modules/E10SUtils.jsm +++ b/browser/modules/E10SUtils.jsm @@ -92,16 +92,11 @@ this.E10SUtils = { getRemoteTypeForURI(aURL, aMultiProcess, aPreferredRemoteType = DEFAULT_REMOTE_TYPE, - aCurrentUri, - aLargeAllocation = false) { + aCurrentUri) { if (!aMultiProcess) { return NOT_REMOTE; } - if (aLargeAllocation) { - return LARGE_ALLOCATION_REMOTE_TYPE; - } - // loadURI in browser.xml treats null as about:blank if (!aURL) { aURL = "about:blank"; From 4ed9410b46131195b4b6688a57de6364776d5739 Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Thu, 18 May 2017 12:08:57 +0100 Subject: [PATCH 06/35] Bug 1351358 Part 3: In tabbrowser.xml adoptTab, make sure the new tab is in the same process as adoptee. r=Gijs This ensures that we never get a short-lived process for the new tab, which can happen if we are not at the maximum for that remote type. We also set the correct remoteType. --- browser/base/content/tabbrowser.xml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index bea66300b69a..672fafecda29 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3597,21 +3597,19 @@ Date: Thu, 18 May 2017 12:08:57 +0100 Subject: [PATCH 07/35] Bug 1351358 Part 4: Select correct remote type upfront when passed window arguments in browser.js onDCL. r=Gijs This prevents short-lived processes when we are not at the maximum normal content process count and the new window's first tab is not going to load in that remote type. --- browser/base/content/browser.js | 44 ++++++++++++++++++++++++----- browser/base/content/tabbrowser.xml | 11 +++++--- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index bb5f420603cf..8b104738b51e 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1150,17 +1150,47 @@ addEventListener("DOMContentLoaded", function onDCL() { let initBrowser = document.getAnonymousElementByAttribute(gBrowser, "anonid", "initialBrowser"); - // The window's first argument is a tab if and only if we are swapping tabs. - // We must set the browser's usercontextid before updateBrowserRemoteness(), - // so that the newly created remote tab child has the correct usercontextid. + // remoteType and sameProcessAsFrameLoader are passed through to + // updateBrowserRemoteness as part of an options object, which itself defaults + // to an empty object. So defaulting them to undefined here will cause the + // default behavior in updateBrowserRemoteness if they don't get set. + let isRemote = gMultiProcessBrowser; + let remoteType; + let sameProcessAsFrameLoader; if (window.arguments) { - let tabToOpen = window.arguments[0]; - if (tabToOpen instanceof XULElement && tabToOpen.hasAttribute("usercontextid")) { - initBrowser.setAttribute("usercontextid", tabToOpen.getAttribute("usercontextid")); + let argToLoad = window.arguments[0]; + if (argToLoad instanceof XULElement) { + // The window's first argument is a tab if and only if we are swapping tabs. + // We must set the browser's usercontextid before updateBrowserRemoteness(), + // so that the newly created remote tab child has the correct usercontextid. + if (argToLoad.hasAttribute("usercontextid")) { + initBrowser.setAttribute("usercontextid", + argToLoad.getAttribute("usercontextid")); + } + + let linkedBrowser = argToLoad.linkedBrowser; + if (linkedBrowser) { + remoteType = linkedBrowser.remoteType; + isRemote = remoteType != E10SUtils.NOT_REMOTE; + sameProcessAsFrameLoader = linkedBrowser.frameLoader; + } + } else if (argToLoad instanceof String) { + // argToLoad is String, so should be a URL. + remoteType = E10SUtils.getRemoteTypeForURI(argToLoad, gMultiProcessBrowser); + isRemote = remoteType != E10SUtils.NOT_REMOTE; + } else if (argToLoad instanceof Ci.nsIArray) { + // argToLoad is nsIArray, so should be an array of URLs, set the remote + // type for the initial browser to match the first one. + let urisstring = argToLoad.queryElementAt(0, Ci.nsISupportsString); + remoteType = E10SUtils.getRemoteTypeForURI(urisstring.data, + gMultiProcessBrowser); + isRemote = remoteType != E10SUtils.NOT_REMOTE; } } - gBrowser.updateBrowserRemoteness(initBrowser, gMultiProcessBrowser); + gBrowser.updateBrowserRemoteness(initBrowser, isRemote, { + remoteType, sameProcessAsFrameLoader + }); }); let _resolveDelayedStartup; diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 672fafecda29..4e00f176219e 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1753,10 +1753,13 @@ } // NB: This works with the hack in the browser constructor that - // turns this normal property into a field. When switching remote - // type copying related browser would stop the switch and the - // previously related browser will no longer be relevant. - if (!aShouldBeRemote || currentRemoteType == aOptions.remoteType) { + // turns this normal property into a field. + if (aOptions.sameProcessAsFrameLoader) { + // Always set sameProcessAsFrameLoader when passed in aOptions. + aBrowser.sameProcessAsFrameLoader = aOptions.sameProcessAsFrameLoader; + } else if (!aShouldBeRemote || currentRemoteType == aOptions.remoteType) { + // Only copy existing sameProcessAsFrameLoader when not switching + // remote type otherwise it would stop the switch. aBrowser.sameProcessAsFrameLoader = sameProcessAsFrameLoader; } From f4b667085ded3eb635ff0009a0af950a492eb249 Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Thu, 18 May 2017 12:08:57 +0100 Subject: [PATCH 08/35] Bug 1351358 Part 5: Enable pref allowLinkedWebInFileUriProcess to allow related http(s) content to top level load in file content process. r=Gijs --- modules/libpref/init/all.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 9b4f9ceeb27d..3b77f5905522 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -3143,7 +3143,7 @@ pref("browser.tabs.remote.separateFileUriProcess", false); // This has been added in case breaking any window references between these // sorts of pages, which we have to do when we run them in the normal web // content process, causes compatibility issues. -pref("browser.tabs.remote.allowLinkedWebInFileUriProcess", false); +pref("browser.tabs.remote.allowLinkedWebInFileUriProcess", true); // Enable caching of Moz2D Path objects for SVG geometry elements pref("svg.path-caching.enabled", true); From ab8c1ce4b13948b2a8005981ccf6d049c584e5c0 Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Thu, 18 May 2017 12:08:57 +0100 Subject: [PATCH 09/35] Bug 1351358 Part 6 prologue: Allow BrowserTestUtils.waitForNewTab to optionally wait for the page in the new tab to load. r=Gijs --- .../BrowserTestUtils/BrowserTestUtils.jsm | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm index 0edcdadf7742..336b7724427e 100644 --- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm +++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm @@ -359,26 +359,48 @@ this.BrowserTestUtils = { * The tabbrowser to look for the next new tab in. * @param {string} url * A string URL to look for in the new tab. If null, allows any non-blank URL. + * @param {boolean} waitForLoad + * True to wait for the page in the new tab to load. Defaults to false. * * @return {Promise} - * @resolves With the {xul:tab} when a tab is opened and its location changes to the given URL. + * @resolves With the {xul:tab} when a tab is opened and its location changes + * to the given URL and optionally that browser has loaded. * * NB: this method will not work if you open a new tab with e.g. BrowserOpenTab * and the tab does not load a URL, because no onLocationChange will fire. */ - waitForNewTab(tabbrowser, url) { + waitForNewTab(tabbrowser, url, waitForLoad = false) { + let urlMatches = url ? (urlToMatch) => urlToMatch == url + : (urlToMatch) => urlToMatch != "about:blank"; return new Promise((resolve, reject) => { tabbrowser.tabContainer.addEventListener("TabOpen", function(openEvent) { + let newTab = openEvent.target; + let newBrowser = newTab.linkedBrowser; + let result; + if (waitForLoad) { + // If waiting for load, resolve with promise for that, which when load + // completes resolves to the new tab. + result = BrowserTestUtils.browserLoaded(newBrowser, false, urlMatches) + .then(() => newTab); + } else { + // If not waiting for load, just resolve with the new tab. + result = newTab; + } + let progressListener = { onLocationChange(aBrowser) { - if (aBrowser != openEvent.target.linkedBrowser || - (url && aBrowser.currentURI.spec != url) || - (!url && aBrowser.currentURI.spec == "about:blank")) { + // Only interested in location changes on our browser. + if (aBrowser != newBrowser) { + return; + } + + // Check that new location is the URL we want. + if (!urlMatches(aBrowser.currentURI.spec)) { return; } tabbrowser.removeTabsProgressListener(progressListener); - resolve(openEvent.target); + resolve(result); }, }; tabbrowser.addTabsProgressListener(progressListener); From 170184d1c2efe060b720a520f10b96309b423bb5 Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Thu, 18 May 2017 12:08:57 +0100 Subject: [PATCH 10/35] Bug 1351358 Part 6: Include more tests cases for allowLinkedWebInFileUriProcess pref. r=Gijs --- ...rowser_new_web_tab_in_file_process_pref.js | 121 ++++++++++++++++-- 1 file changed, 110 insertions(+), 11 deletions(-) diff --git a/browser/base/content/test/tabs/browser_new_web_tab_in_file_process_pref.js b/browser/base/content/test/tabs/browser_new_web_tab_in_file_process_pref.js index f42c239da9ab..87b60271d94f 100644 --- a/browser/base/content/test/tabs/browser_new_web_tab_in_file_process_pref.js +++ b/browser/base/content/test/tabs/browser_new_web_tab_in_file_process_pref.js @@ -3,6 +3,19 @@ const TEST_FILE = "dummy_page.html"; const TEST_HTTP = "http://example.org/"; +const TEST_CROSS_ORIGIN = "http://example.com/"; + +function CheckBrowserInPid(browser, expectedPid, message) { + return ContentTask.spawn(browser, { expectedPid, message }, (arg) => { + is(Services.appinfo.processID, arg.expectedPid, arg.message); + }); +} + +function CheckBrowserNotInPid(browser, unExpectedPid, message) { + return ContentTask.spawn(browser, { unExpectedPid, message }, (arg) => { + isnot(Services.appinfo.processID, arg.unExpectedPid, arg.message); + }); +} // Test for bug 1343184. add_task(async function() { @@ -11,29 +24,115 @@ add_task(async function() { dir.append(TEST_FILE); const uriString = Services.io.newFileURI(dir).spec; await BrowserTestUtils.withNewTab(uriString, async function(fileBrowser) { - // Set prefs to ensure file content process and to allow linked web content - // in file URI process. + // Set prefs to ensure file content process, to allow linked web content + // in file URI process and allow more that one file content process. await SpecialPowers.pushPrefEnv( {set: [["browser.tabs.remote.separateFileUriProcess", true], - ["browser.tabs.remote.allowLinkedWebInFileUriProcess", true]]}); + ["browser.tabs.remote.allowLinkedWebInFileUriProcess", true], + ["dom.ipc.processCount.file", 2]]}); - // Open new http tab from JavaScript in file:// page. - let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, TEST_HTTP); + // Get the file:// URI pid for comparison later. + let filePid = await ContentTask.spawn(fileBrowser, null, () => { + return Services.appinfo.processID; + }); + + // Check that http tab opened from JS in file:// page is in same process. + let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, TEST_HTTP, true); await ContentTask.spawn(fileBrowser, TEST_HTTP, uri => { content.open(uri, "_blank"); }); let httpTab = await promiseTabOpened; + let httpBrowser = httpTab.linkedBrowser; registerCleanupFunction(async function() { await BrowserTestUtils.removeTab(httpTab); }); + await CheckBrowserInPid(httpBrowser, filePid, + "Check that new http tab opened from file loaded in file content process."); - // Ensure that http browser is running in the same process as the file:// one. - let filePid = await ContentTask.spawn(fileBrowser, null, () => { + // Check that reload doesn't break the file content process affinity. + if (httpTab != gBrowser.selectedTab) { + httpTab = await BrowserTestUtils.switchTab(gBrowser, httpTab); + httpBrowser = httpTab.linkedBrowser; + } + let promiseLoad = BrowserTestUtils.browserLoaded(httpBrowser); + document.getElementById("reload-button").doCommand(); + await promiseLoad; + await CheckBrowserInPid(httpBrowser, filePid, + "Check that http tab still in file content process after reload."); + + // Check that same-origin load doesn't break the affinity. + promiseLoad = BrowserTestUtils.browserLoaded(httpBrowser); + httpBrowser.loadURI(TEST_HTTP + "foo"); + await promiseLoad; + await CheckBrowserInPid(httpBrowser, filePid, + "Check that http tab still in file content process after same origin load."); + + // Check that history back doesn't break the affinity. + let promiseLocation = + BrowserTestUtils.waitForLocationChange(gBrowser, TEST_HTTP); + httpBrowser.goBack(); + await promiseLocation; + await CheckBrowserInPid(httpBrowser, filePid, + "Check that http tab still in file content process after history back."); + + // Check that history forward doesn't break the affinity. + promiseLocation = + BrowserTestUtils.waitForLocationChange(gBrowser, TEST_HTTP + "foo"); + promiseLoad = BrowserTestUtils.browserLoaded(httpBrowser); + httpBrowser.goForward(); + await promiseLocation; + await CheckBrowserInPid(httpBrowser, filePid, + "Check that http tab still in file content process after history forward."); + + // Check that goto history index doesn't break the affinity. + promiseLocation = BrowserTestUtils.waitForLocationChange(gBrowser, TEST_HTTP); + httpBrowser.gotoIndex(0); + await promiseLocation; + await CheckBrowserInPid(httpBrowser, filePid, + "Check that http tab still in file content process after history gotoIndex."); + + // Check that file:// URI load doesn't break the affinity. + promiseLoad = BrowserTestUtils.browserLoaded(httpBrowser); + httpBrowser.loadURI(uriString); + await promiseLoad; + await CheckBrowserInPid(httpBrowser, filePid, + "Check that http tab still in file content process after file:// load."); + + // Check that location change doesn't break the affinity. + promiseLoad = BrowserTestUtils.browserLoaded(httpBrowser); + await ContentTask.spawn(httpBrowser, TEST_HTTP, uri => { + content.location = uri; + }); + await promiseLoad; + await CheckBrowserInPid(httpBrowser, filePid, + "Check that http tab still in file content process after location change."); + + // Check that cross-origin load does break the affinity. + promiseLoad = BrowserTestUtils.browserLoaded(httpBrowser); + httpBrowser.loadURI(TEST_CROSS_ORIGIN); + await promiseLoad; + await CheckBrowserNotInPid(httpBrowser, filePid, + "Check that http tab not in file content process after cross origin load."); + is(httpBrowser.remoteType, E10SUtils.WEB_REMOTE_TYPE, + "Check that tab now has web remote type."); + + // Check that history back now remains in the web content process. + let httpPid = await ContentTask.spawn(httpBrowser, null, () => { return Services.appinfo.processID; }); - await ContentTask.spawn(httpTab.linkedBrowser, filePid, (expectedPid) => { - is(Services.appinfo.processID, expectedPid, - "Check that new http page opened from file loaded in file content process."); - }); + promiseLocation = BrowserTestUtils.waitForLocationChange(gBrowser, TEST_HTTP); + httpBrowser.goBack(); + await promiseLocation; + await CheckBrowserInPid(httpBrowser, httpPid, + "Check that http tab still in web content process after process switch and history back."); + + // Check that history back to file:// URI switches to file content process. + promiseLocation = BrowserTestUtils.waitForLocationChange(gBrowser, uriString); + httpBrowser.goBack(); + await promiseLocation; + await CheckBrowserNotInPid(httpBrowser, httpPid, + "Check that history back to file:// URI switches to file content process."); + is(httpBrowser.remoteType, E10SUtils.FILE_REMOTE_TYPE, + "Check that tab now has file remote type."); }); }); From 5f86d7bfd2c1ed1af2d78e3ecf859d882320830e Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Thu, 18 May 2017 12:08:57 +0100 Subject: [PATCH 11/35] Bug 1351358 Part 7: Check that we can form post from file:// URL to http:// URL. r=bz --- dom/html/test/browser.ini | 4 + .../browser_form_post_from_file_to_http.js | 105 ++++++++++++++++++ dom/html/test/form_data_file.bin | 1 + dom/html/test/form_data_file.txt | 1 + 4 files changed, 111 insertions(+) create mode 100644 dom/html/test/browser_form_post_from_file_to_http.js create mode 100644 dom/html/test/form_data_file.bin create mode 100644 dom/html/test/form_data_file.txt diff --git a/dom/html/test/browser.ini b/dom/html/test/browser.ini index 49caea726c08..34a32b833652 100644 --- a/dom/html/test/browser.ini +++ b/dom/html/test/browser.ini @@ -6,6 +6,9 @@ support-files = file_bug649778.html^headers^ file_fullscreen-api-keys.html file_content_contextmenu.html + form_data_file.bin + form_data_file.txt + form_submit_server.sjs head.js [browser_bug592641.js] @@ -19,6 +22,7 @@ support-files = file_bug1108547-3.html [browser_content_contextmenu_userinput.js] [browser_DOMDocElementInserted.js] +[browser_form_post_from_file_to_http.js] [browser_fullscreen-api-keys.js] tags = fullscreen [browser_fullscreen-contextmenu-esc.js] diff --git a/dom/html/test/browser_form_post_from_file_to_http.js b/dom/html/test/browser_form_post_from_file_to_http.js new file mode 100644 index 000000000000..c6c3826b4285 --- /dev/null +++ b/dom/html/test/browser_form_post_from_file_to_http.js @@ -0,0 +1,105 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ + +const TEST_HTTP_POST = + "http://example.org/browser/dom/html/test/form_submit_server.sjs"; + +// Test for bug 1351358. +add_task(async function() { + // Create file URI and test data file paths. + let testFile = getChromeDir(getResolvedURI(gTestPath)); + testFile.append("dummy_page.html"); + const fileUriString = Services.io.newFileURI(testFile).spec; + let filePaths = []; + testFile.leafName = "form_data_file.txt"; + filePaths.push(testFile.path); + testFile.leafName = "form_data_file.bin"; + filePaths.push(testFile.path); + + // Open file:// page tab in which to run the test. + await BrowserTestUtils.withNewTab(fileUriString, async function(fileBrowser) { + + // Create a form to post to server that writes posted data into body as JSON. + let promiseLoad = BrowserTestUtils.browserLoaded(fileBrowser, false, + TEST_HTTP_POST); + await ContentTask.spawn(fileBrowser, [TEST_HTTP_POST, filePaths], + ([actionUri, filePaths]) => { + Components.utils.importGlobalProperties(["File"]); + + let doc = content.document; + let form = doc.body.appendChild(doc.createElement("form")); + form.action = actionUri; + form.method = "POST"; + form.enctype = "multipart/form-data"; + + let inputText = form.appendChild(doc.createElement("input")); + inputText.type = "text"; + inputText.name = "text"; + inputText.value = "posted"; + + let inputCheckboxOn = form.appendChild(doc.createElement("input")); + inputCheckboxOn.type = "checkbox"; + inputCheckboxOn.name = "checked"; + inputCheckboxOn.checked = true; + + let inputCheckboxOff = form.appendChild(doc.createElement("input")); + inputCheckboxOff.type = "checkbox"; + inputCheckboxOff.name = "unchecked"; + inputCheckboxOff.checked = false; + + let inputFile = form.appendChild(doc.createElement("input")); + inputFile.type = "file"; + inputFile.name = "file"; + let fileList = []; + let promises = []; + for (let path of filePaths) { + promises.push(File.createFromFileName(path).then(file => { + fileList.push(file); + })); + } + + Promise.all(promises).then(() => { + inputFile.mozSetFileArray(fileList); + form.submit(); + }); + }); + + let href = await promiseLoad; + is(href, TEST_HTTP_POST, + "Check that the loaded page is the one to which we posted."); + + let binContentType; + if (AppConstants.platform == "macosx") { + binContentType = "application/macbinary"; + } else { + binContentType = "application/octet-stream"; + } + await ContentTask.spawn(fileBrowser, { binContentType }, (args) => { + let data = JSON.parse(content.document.body.textContent); + is(data[0].headers["Content-Disposition"], "form-data; name=\"text\"", + "Check text input Content-Disposition"); + is(data[0].body, "posted", "Check text input body"); + + is(data[1].headers["Content-Disposition"], "form-data; name=\"checked\"", + "Check checkbox input Content-Disposition"); + is(data[1].body, "on", "Check checkbox input body"); + + // Note that unchecked checkbox details are not sent. + + is(data[2].headers["Content-Disposition"], + "form-data; name=\"file\"; filename=\"form_data_file.txt\"", + "Check text file input Content-Disposition"); + is(data[2].headers["Content-Type"], "text/plain", + "Check text file input Content-Type"); + is(data[2].body, "1234\n", "Check text file input body"); + + is(data[3].headers["Content-Disposition"], + "form-data; name=\"file\"; filename=\"form_data_file.bin\"", + "Check binary file input Content-Disposition"); + is(data[3].headers["Content-Type"], args.binContentType, + "Check binary file input Content-Type"); + is(data[3].body, "\u0001\u0002\u0003\u0004\n", + "Check binary file input body"); + }); + }); +}); diff --git a/dom/html/test/form_data_file.bin b/dom/html/test/form_data_file.bin new file mode 100644 index 000000000000..744bde3558fc --- /dev/null +++ b/dom/html/test/form_data_file.bin @@ -0,0 +1 @@ + diff --git a/dom/html/test/form_data_file.txt b/dom/html/test/form_data_file.txt new file mode 100644 index 000000000000..81c545efebe5 --- /dev/null +++ b/dom/html/test/form_data_file.txt @@ -0,0 +1 @@ +1234 From 746d38b25fbfe849d4a24b92a30aad3ed3e383d6 Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Thu, 18 May 2017 12:08:58 +0100 Subject: [PATCH 12/35] Bug 1351358 Part 8: Check that reload doesn't cause new process when large allocation browser in tab group. r=mystor --- dom/tests/browser/helper_largeAllocation.js | 51 +++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/dom/tests/browser/helper_largeAllocation.js b/dom/tests/browser/helper_largeAllocation.js index 8cbc55d66869..0f6cbc8237ff 100644 --- a/dom/tests/browser/helper_largeAllocation.js +++ b/dom/tests/browser/helper_largeAllocation.js @@ -345,6 +345,57 @@ function* largeAllocSuccessTests() { }); }); + // Opening a window from the large-allocation window should prevent the + // process switch with reload. + yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) { + info("Starting test 6a"); + let pid1 = yield getPID(aBrowser); + is(false, yield getInLAProc(aBrowser)); + + let ready = Promise.all([expectProcessCreated(), + BrowserTestUtils.browserLoaded(aBrowser)]); + + yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => { + content.document.location = TEST_URI; + }); + + yield ready; + + let pid2 = yield getPID(aBrowser); + + isnot(pid1, pid2, "PIDs 1 and 2 should not match"); + is(true, yield getInLAProc(aBrowser)); + + let stopExpectNoProcess = expectNoProcess(); + + let firstTab = gBrowser.selectedTab; + let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:blank"); + yield ContentTask.spawn(aBrowser, null, () => { + this.__newWindow = content.window.open("about:blank"); + }); + yield promiseTabOpened; + + if (firstTab != gBrowser.selectedTab) { + firstTab = yield BrowserTestUtils.switchTab(gBrowser, firstTab); + aBrowser = firstTab.linkedBrowser; + } + let promiseLoad = BrowserTestUtils.browserLoaded(aBrowser); + document.getElementById("reload-button").doCommand(); + yield promiseLoad; + + let pid3 = yield getPID(aBrowser); + + is(pid3, pid2, "PIDs 2 and 3 should match"); + is(true, yield getInLAProc(aBrowser)); + + stopExpectNoProcess(); + + yield ContentTask.spawn(aBrowser, null, () => { + ok(this.__newWindow, "The window should have been stored"); + this.__newWindow.close(); + }); + }); + // Try dragging the tab into a new window when not at the maximum number of // Large-Allocation processes. yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) { From 0b1e5ad3da3922fd5024137a6c5986e537765be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Thu, 18 May 2017 13:23:09 +0200 Subject: [PATCH 13/35] Bug 1365683 - Increase horizontal margin around the location bar and the search bar. r=dale MozReview-Commit-ID: 5mRPP1oMBgq --HG-- rename : browser/themes/shared/location-search-bar.inc.css => browser/themes/shared/urlbar-searchbar.inc.css --- browser/themes/linux/browser.css | 15 +-------------- browser/themes/osx/browser.css | 16 ++-------------- ...ch-bar.inc.css => urlbar-searchbar.inc.css} | 18 +++++++++++++++++- browser/themes/windows/browser.css | 16 +--------------- 4 files changed, 21 insertions(+), 44 deletions(-) rename browser/themes/shared/{location-search-bar.inc.css => urlbar-searchbar.inc.css} (76%) diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css index 73cd8ff5ddca..6fd562b639a0 100644 --- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -277,7 +277,7 @@ menuitem.bookmark-item { %ifdef MOZ_PHOTON_THEME -%include ../shared/location-search-bar.inc.css +%include ../shared/urlbar-searchbar.inc.css #urlbar[focused="true"], .searchbar-textbox[focused="true"] { @@ -347,10 +347,6 @@ menuitem.bookmark-item { opacity: 0; } -#urlbar-container { - -moz-box-align: center; -} - %ifndef MOZ_PHOTON_THEME @conditionalForwardWithUrlbar@ > #urlbar { border-inline-start: none; @@ -435,15 +431,6 @@ menuitem.bookmark-item { background-color: var(--arrowpanel-dimmed-further); } -#urlbar-search-splitter { - /* The splitter width should equal the location and search bars' combined - neighboring margin and border width. */ - width: 8px; - margin: 0 -4px; - position: relative; - -moz-appearance: none; -} - #urlbar-display-box { margin-top: -1px; margin-bottom: -1px; diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css index 906c1b888377..25f529fb6428 100644 --- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -520,6 +520,8 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p /* ::::: nav-bar-inner ::::: */ +%include ../shared/urlbar-searchbar.inc.css + %ifdef MOZ_PHOTON_THEME #main-window { @@ -528,8 +530,6 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p --urlbar-background-color: -moz-field; } -%include ../shared/location-search-bar.inc.css - #urlbar[focused="true"], .searchbar-textbox[focused="true"] { border-color: -moz-mac-focusring; @@ -583,10 +583,6 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p box-shadow: var(--focus-ring-box-shadow); } -#urlbar-container { - -moz-box-align: center; -} - #urlbar { border-radius: var(--toolbarbutton-border-radius); } @@ -755,14 +751,6 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p background-color: var(--arrowpanel-dimmed-further); } -#urlbar-search-splitter { - min-width: 8px; - width: 8px; - background-image: none; - margin: 0 -4px; - position: relative; -} - #search-container { min-width: calc(54px + 11ch); } diff --git a/browser/themes/shared/location-search-bar.inc.css b/browser/themes/shared/urlbar-searchbar.inc.css similarity index 76% rename from browser/themes/shared/location-search-bar.inc.css rename to browser/themes/shared/urlbar-searchbar.inc.css index d3feb2a01f6c..4a3053f10272 100644 --- a/browser/themes/shared/location-search-bar.inc.css +++ b/browser/themes/shared/urlbar-searchbar.inc.css @@ -2,6 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +%ifdef MOZ_PHOTON_THEME + #urlbar, .searchbar-textbox { -moz-appearance: none; @@ -11,7 +13,7 @@ border-radius: var(--toolbarbutton-border-radius); box-shadow: 0 1px 4px hsla(0, 0%, 0%, .05); padding: 0; - margin: 0 2px; + margin: 0 5px; min-height: 30px; } @@ -34,6 +36,20 @@ box-shadow: 0 1px 6px hsla(0, 0%, 0%, .1), 0 0 1px 0 rgba(0, 0, 0, .1); } +%endif + #urlbar-container { -moz-box-align: center; } + +#urlbar-search-splitter { + /* The splitter width should equal the location and search bars' combined + neighboring margin and border width. */ + min-width: 12px; + margin: 0 -6px; + position: relative; + border: none; + background: transparent; + -moz-appearance: none; +} + diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index d2cf1ea17807..9b0139104b3e 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -672,7 +672,7 @@ toolbar[brighttext] #close-button { } } -%include ../shared/location-search-bar.inc.css +%include ../shared/urlbar-searchbar.inc.css #urlbar[focused="true"], .searchbar-textbox[focused="true"] { @@ -827,10 +827,6 @@ html|*.urlbar-input:-moz-lwtheme::placeholder, color: #777; } -#urlbar-container { - -moz-box-align: center; -} - .urlbar-textbox-container { -moz-box-align: stretch; } @@ -903,16 +899,6 @@ html|*.urlbar-input:-moz-lwtheme::placeholder, background-color: var(--arrowpanel-dimmed-further); } -#urlbar-search-splitter { - /* The splitter width should equal the location and search bars' combined - neighboring margin and border width. */ - min-width: 8px; - margin: 0 -4px; - position: relative; - border: none; - background: transparent; -} - .urlbar-display { margin-top: 0; margin-bottom: 0; From 421cbf7a04a26e2078655772d6d64d35cb3d6187 Mon Sep 17 00:00:00 2001 From: Petr Sumbera Date: Wed, 17 May 2017 08:03:02 -0700 Subject: [PATCH 14/35] Bug 1365620 - Option -rpath-link is not supported by Solaris linker. r=glandium --- js/src/old-configure.in | 4 ++++ old-configure.in | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/js/src/old-configure.in b/js/src/old-configure.in index 1ff24a5408f4..9a8880918584 100644 --- a/js/src/old-configure.in +++ b/js/src/old-configure.in @@ -879,6 +879,10 @@ case "$target" in fi ;; +*-solaris*) + MOZ_FIX_LINK_PATHS= + ;; + esac dnl Only one oddball right now (QNX), but this gives us flexibility diff --git a/old-configure.in b/old-configure.in index 5b3d7eed8180..13cf311325e8 100644 --- a/old-configure.in +++ b/old-configure.in @@ -1191,6 +1191,10 @@ case "$target" in fi ;; +*-solaris*) + MOZ_FIX_LINK_PATHS= + ;; + esac AC_SUBST_LIST(MMX_FLAGS) From 3bc1dfc2997e0fb2fa5094382d409055e84c1ef9 Mon Sep 17 00:00:00 2001 From: Petr Sumbera Date: Thu, 11 May 2017 06:14:57 -0700 Subject: [PATCH 15/35] Bug 1364066 - Re-enable bundled NSS on Solaris. r=glandium --- old-configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/old-configure.in b/old-configure.in index 13cf311325e8..67b75c1abc60 100644 --- a/old-configure.in +++ b/old-configure.in @@ -2023,7 +2023,7 @@ else NSS_CFLAGS="-I${DIST}/include/nss" case "${OS_ARCH}" in # Only few platforms have been tested with GYP - WINNT|Darwin|Linux|DragonFly|FreeBSD|NetBSD|OpenBSD) + WINNT|Darwin|Linux|DragonFly|FreeBSD|NetBSD|OpenBSD|SunOS) ;; *) AC_MSG_ERROR([building in-tree NSS is not supported on this platform. Use --with-system-nss]) From d01f9cd1f7055d78dfaa0fb13f3f42e656abfd52 Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Thu, 18 May 2017 13:31:44 +0100 Subject: [PATCH 16/35] Bug 1175267 Part 1: Handle file:// URI xpi files in chrome code directly instead of routing through content browser. r=mossop --- browser/base/content/browser.js | 59 +++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 8b104738b51e..14293b25c590 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -996,6 +996,35 @@ function serializeInputStream(aStream) { return data; } +/** + * Handles URIs when we want to deal with them in chrome code rather than pass + * them down to a content browser. This can avoid unnecessary process switching + * for the browser. + * @param aBrowser the browser that is attempting to load the URI + * @param aUri the nsIURI that is being loaded + * @returns true if the URI is handled, otherwise false + */ +function handleUriInChrome(aBrowser, aUri) { + if (aUri.scheme == "file") { + try { + let mimeType = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService) + .getTypeFromURI(aUri); + if (mimeType == "application/x-xpinstall") { + let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal(); + AddonManager.getInstallForURL(aUri.spec, install => { + AddonManager.installAddonFromWebpage(mimeType, aBrowser, systemPrincipal, + install); + }, mimeType); + return true; + } + } catch (e) { + return false; + } + } + + return false; +} + // A shared function used by both remote and non-remote browser XBL bindings to // load a URI or redirect it to the correct process. function _loadURIWithFlags(browser, uri, params) { @@ -1016,9 +1045,33 @@ function _loadURIWithFlags(browser, uri, params) { let postData = params.postData; let currentRemoteType = browser.remoteType; - let requiredRemoteType = - E10SUtils.getRemoteTypeForURI(uri, gMultiProcessBrowser, currentRemoteType, - browser.currentURI); + let requiredRemoteType; + try { + let fixupFlags = Ci.nsIURIFixup.FIXUP_FLAG_NONE; + if (flags & Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) { + fixupFlags |= Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP; + } + if (flags & Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS) { + fixupFlags |= Ci.nsIURIFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS; + } + let uriObject = Services.uriFixup.createFixupURI(uri, fixupFlags); + if (handleUriInChrome(browser, uriObject)) { + // If we've handled the URI in Chrome then just return here. + return; + } + + // Note that I had thought that we could set uri = uriObject.spec here, to + // save on fixup later on, but that changes behavior and breaks tests. + requiredRemoteType = + E10SUtils.getRemoteTypeForURIObject(uriObject, gMultiProcessBrowser, + currentRemoteType, browser.currentURI); + } catch (e) { + // createFixupURI throws if it can't create a URI. If that's the case then + // we still need to pass down the uri because docshell handles this case. + requiredRemoteType = gMultiProcessBrowser ? E10SUtils.DEFAULT_REMOTE_TYPE + : E10SUtils.NOT_REMOTE; + } + let mustChangeProcess = requiredRemoteType != currentRemoteType; // !requiredRemoteType means we're loading in the parent/this process. From a86639fa9d28a01372eede6688f06b57d64564b2 Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Thu, 18 May 2017 13:31:44 +0100 Subject: [PATCH 17/35] Bug 1175267 Part 2: Ensure that process does not switch for file:// XPI installs. r=mossop --- .../extensions/test/browser/browser.ini | 1 + .../browser_file_xpi_no_process_switch.js | 101 ++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 toolkit/mozapps/extensions/test/browser/browser_file_xpi_no_process_switch.js diff --git a/toolkit/mozapps/extensions/test/browser/browser.ini b/toolkit/mozapps/extensions/test/browser/browser.ini index 14a5de191aec..4593a874206c 100644 --- a/toolkit/mozapps/extensions/test/browser/browser.ini +++ b/toolkit/mozapps/extensions/test/browser/browser.ini @@ -57,6 +57,7 @@ support-files = [browser_discovery_install.js] [browser_eula.js] skip-if = buildapp == 'mulet' +[browser_file_xpi_no_process_switch.js] [browser_getmorethemes.js] [browser_gmpProvider.js] [browser_hotfix.js] diff --git a/toolkit/mozapps/extensions/test/browser/browser_file_xpi_no_process_switch.js b/toolkit/mozapps/extensions/test/browser/browser_file_xpi_no_process_switch.js new file mode 100644 index 000000000000..22c1a1abb444 --- /dev/null +++ b/toolkit/mozapps/extensions/test/browser/browser_file_xpi_no_process_switch.js @@ -0,0 +1,101 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ + +const ADDON_INSTALL_ID = "addon-install-confirmation"; + +let fileurl1 = get_addon_file_url("browser_dragdrop1.xpi"); +let fileurl2 = get_addon_file_url("browser_dragdrop2.xpi"); + +function promiseInstallNotification(aBrowser) { + return new Promise(resolve => { + function popupshown(event) { + if (event.target.getAttribute("popupid") != ADDON_INSTALL_ID) { + return; + } + + let notification = + PopupNotifications.getNotification(ADDON_INSTALL_ID, aBrowser); + if (!notification) { + return; + } + + PopupNotifications.panel.removeEventListener("popupshown", popupshown); + ok(true, `Got ${ADDON_INSTALL_ID} popup for browser`); + notification.remove(); + resolve(); + } + + PopupNotifications.panel.addEventListener("popupshown", popupshown); + }); +} + +function waitForAnyNewTabAndInstallNotification() { + return new Promise((resolve) => { + gBrowser.tabContainer.addEventListener("TabOpen", function(openEvent) { + let newTab = openEvent.target; + resolve([newTab, promiseInstallNotification(newTab.linkedBrowser)]); + }, {once: true}); + }); +} + +function CheckBrowserInPid(browser, expectedPid, message) { + return ContentTask.spawn(browser, { expectedPid, message }, (arg) => { + is(Services.appinfo.processID, arg.expectedPid, arg.message); + }); +} + +async function testOpenedAndDraggedXPI(aBrowser) { + // Get the current pid for browser for comparison later. + let browserPid = await ContentTask.spawn(aBrowser, null, () => { + return Services.appinfo.processID; + }); + + // No process switch for XPI file:// URI in the urlbar. + let promiseNotification = promiseInstallNotification(aBrowser); + let urlbar = document.getElementById("urlbar"); + urlbar.value = fileurl1.spec; + urlbar.focus(); + EventUtils.synthesizeKey("KEY_Enter", { code: "Enter" }); + await promiseNotification; + await CheckBrowserInPid(aBrowser, browserPid, + "Check that browser has not switched process."); + + // No process switch for XPI file:// URI dragged to tab. + let tab = gBrowser.getTabForBrowser(aBrowser); + promiseNotification = promiseInstallNotification(aBrowser); + let effect = EventUtils.synthesizeDrop(tab, tab, + [[{type: "text/uri-list", data: fileurl1.spec}]], + "move"); + is(effect, "move", "Drag should be accepted"); + await promiseNotification; + await CheckBrowserInPid(aBrowser, browserPid, + "Check that browser has not switched process."); + + // No process switch for two XPI file:// URIs dragged to tab. + promiseNotification = promiseInstallNotification(aBrowser); + let promiseTabAndNotification = waitForAnyNewTabAndInstallNotification(); + effect = EventUtils.synthesizeDrop(tab, tab, + [[{type: "text/uri-list", data: fileurl1.spec}], + [{type: "text/uri-list", data: fileurl2.spec}]], + "move"); + is(effect, "move", "Drag should be accepted"); + let [newTab, newTabInstallNotification] = await promiseTabAndNotification; + await promiseNotification; + if (gBrowser.selectedTab != newTab) { + await BrowserTestUtils.switchTab(gBrowser, newTab); + } + await newTabInstallNotification; + await BrowserTestUtils.removeTab(newTab); + await CheckBrowserInPid(aBrowser, browserPid, + "Check that browser has not switched process."); +} + +// Test for bug 1175267. +add_task(async function() { + await SpecialPowers.pushPrefEnv({ + set: [["xpinstall.customConfirmationUI", true]] + }); + + await BrowserTestUtils.withNewTab("http://example.com", testOpenedAndDraggedXPI); + await BrowserTestUtils.withNewTab("about:robots", testOpenedAndDraggedXPI); +}); From 9df25fcb322d0eae43610df81631d684b58fee51 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Thu, 18 May 2017 14:12:15 +0000 Subject: [PATCH 18/35] Bug 1364118 - List JS bytecode cache preferences in all.js. r=mrbkap --- .../test/test_script_loader_js_cache.html | 6 ++--- dom/script/ScriptLoader.cpp | 26 ++++++++++++------- dom/script/ScriptLoader.h | 3 +++ modules/libpref/init/all.js | 10 +++++++ 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/dom/base/test/test_script_loader_js_cache.html b/dom/base/test/test_script_loader_js_cache.html index e3f881109dbb..81ece7ee40a8 100644 --- a/dom/base/test/test_script_loader_js_cache.html +++ b/dom/base/test/test_script_loader_js_cache.html @@ -116,14 +116,14 @@ // trace these and resolve a promise with the path taken by the // script loader. // - // Setting dom.script_loader.force_bytecode_cache causes the + // Setting dom.script_loader.bytecode_cache.eager causes the // nsScriptLoadRequest to force all the conditions necessary to make a // script be saved as bytecode in the alternate data storage provided // by the channel (necko cache). await SpecialPowers.pushPrefEnv({set: [ ['dom.script_loader.bytecode_cache.enabled', true], ['dom.expose_test_interfaces', true], - ['dom.script_loader.force_bytecode_cache', true] + ['dom.script_loader.bytecode_cache.eager', true] ]}); // Load the test page, and verify that the code path taken by the @@ -174,7 +174,7 @@ await SpecialPowers.pushPrefEnv({set: [ ['dom.script_loader.bytecode_cache.enabled', true], ['dom.expose_test_interfaces', true], - ['dom.script_loader.force_bytecode_cache', true] + ['dom.script_loader.bytecode_cache.eager', true] ]}); // The test page add a new script which generate a "ping" event, which diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index c4926958b4be..0b405c5445e3 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -818,6 +818,21 @@ ScriptLoader::IsBytecodeCacheEnabled() return sExposeTestInterfaceEnabled; } +/* static */ bool +ScriptLoader::IsEagerBytecodeCache() +{ + // When testing, we want to force use of the bytecode cache. + static bool sEagerBytecodeCache = false; + static bool sForceBytecodeCachePrefCached = false; + if (!sForceBytecodeCachePrefCached) { + sForceBytecodeCachePrefCached = true; + Preferences::AddBoolVarCache(&sEagerBytecodeCache, + "dom.script_loader.bytecode_cache.eager", + false); + } + return sEagerBytecodeCache; +} + nsresult ScriptLoader::RestartLoad(ScriptLoadRequest* aRequest) { @@ -1864,19 +1879,10 @@ ScriptLoader::FillCompileOptionsForRequest(const AutoJSAPI&jsapi, aOptions->setElement(&elementVal.toObject()); } - // When testing, we want to force use of the bytecode cache. - static bool sForceBytecodeCacheEnabled = false; - static bool sForceBytecodeCachePrefCached = false; - if (!sForceBytecodeCachePrefCached) { - sForceBytecodeCachePrefCached = true; - Preferences::AddBoolVarCache(&sForceBytecodeCacheEnabled, - "dom.script_loader.force_bytecode_cache", - false); - } // At the moment, the bytecode cache is only triggered if a script is large // enough to be parsed out of the main thread. Thus, for testing purposes, we // force parsing any script out of the main thread. - if (sForceBytecodeCacheEnabled) { + if (IsEagerBytecodeCache()) { aOptions->forceAsync = true; } diff --git a/dom/script/ScriptLoader.h b/dom/script/ScriptLoader.h index 27c402c7521f..2c695d4ae3b4 100644 --- a/dom/script/ScriptLoader.h +++ b/dom/script/ScriptLoader.h @@ -471,6 +471,9 @@ private: static bool IsBytecodeCacheEnabled(); + static bool + IsEagerBytecodeCache(); + nsresult CreateModuleScript(ModuleLoadRequest* aRequest); nsresult ProcessFetchedModuleSource(ModuleLoadRequest* aRequest); void ProcessLoadedModuleTree(ModuleLoadRequest* aRequest); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 3b77f5905522..388f5e9626fe 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -209,6 +209,16 @@ pref("dom.keyboardevent.dispatch_during_composition", false); // significantly increase the number of compartments in the system. pref("dom.compartment_per_addon", true); +// Whether to enable the JavaScript start-up cache. This causes one of the first +// execution to record the bytecode of the JavaScript function used, and save it +// in the existing cache entry. On the following loads of the same script, the +// bytecode would be loaded from the cache instead of being generated once more. +pref("dom.script_loader.bytecode_cache.enabled", false); // Not tuned yet. + +// Ignore the heuristics of the bytecode cache, and always record on the first +// visit. (used for testing purposes). +pref("dom.script_loader.bytecode_cache.eager", false); + // Fastback caching - if this pref is negative, then we calculate the number // of content viewers to cache based on the amount of available memory. pref("browser.sessionhistory.max_total_viewers", -1); From c6ca6e9675fd8fef22377a8b26cab3c166c59ea4 Mon Sep 17 00:00:00 2001 From: Michelangelo De Simone Date: Thu, 18 May 2017 09:51:02 -0500 Subject: [PATCH 19/35] Bug 1357911 - Baldr: update names section format (r=luke) MozReview-Commit-ID: 5xIiHiB6ico --HG-- extra : rebase_source : 23999118a544fbb2b54f346bd499a7c750ee566d --- js/src/jit-test/lib/wasm-binary.js | 5 + js/src/jit-test/tests/wasm/binary.js | 73 +++++++++----- js/src/wasm/WasmBinaryConstants.h | 11 ++- js/src/wasm/WasmCode.h | 4 +- js/src/wasm/WasmValidate.cpp | 142 +++++++++++++++++++-------- js/src/wasm/WasmValidate.h | 5 + 6 files changed, 169 insertions(+), 71 deletions(-) diff --git a/js/src/jit-test/lib/wasm-binary.js b/js/src/jit-test/lib/wasm-binary.js index a463e45202ab..3be70031858f 100644 --- a/js/src/jit-test/lib/wasm-binary.js +++ b/js/src/jit-test/lib/wasm-binary.js @@ -28,6 +28,11 @@ const dataId = 11; // User-defined section names const nameName = "name"; +// Name section name types +const nameTypeModule = 0; +const nameTypeFunction = 1; +const nameTypeLocal = 2; + // Type codes const I32Code = 0x7f; const I64Code = 0x7e; diff --git a/js/src/jit-test/tests/wasm/binary.js b/js/src/jit-test/tests/wasm/binary.js index 93690ef01a19..ba8da09b2c52 100644 --- a/js/src/jit-test/tests/wasm/binary.js +++ b/js/src/jit-test/tests/wasm/binary.js @@ -232,20 +232,35 @@ function elemSection(elemArrays) { return { name: elemId, body }; } -function nameSection(elems) { +function nameSection(moduleName, funcNames) { var body = []; body.push(...string(nameName)); - body.push(...varU32(elems.length)); - for (let fn of elems) { - body.push(...encodedString(fn.name, fn.nameLen)); - if (!fn.locals) { - body.push(...varU32(0)); - continue; - } - body.push(...varU32(fn.locals.length)); - for (let local of fn.locals) - body.push(...encodedString(local.name, local.nameLen)); + + if (moduleName) { + body.push(...varU32(nameTypeModule)); + + var subsection = encodedString(moduleName); + + body.push(...varU32(subsection.length)); + body.push(...subsection); } + + if (funcNames) { + body.push(...varU32(nameTypeFunction)); + + var subsection = varU32(funcNames.length); + + var funcIndex = 0; + for (let f of funcNames) { + subsection.push(...varU32(f.index ? f.index : funcIndex)); + subsection.push(...encodedString(f.name, f.nameLen)); + funcIndex++; + } + + body.push(...varU32(subsection.length)); + body.push(...subsection); + } + return { name: userDefinedId, body }; } @@ -380,7 +395,7 @@ wasmEval(moduleWithSections([tooBigNameSection])); var customDefSec = customSection("wee", 42, 13); var declSec = declSection([0]); var bodySec = bodySection([v2vBody]); -var nameSec = nameSection([{name:'hi'}]); +var nameSec = nameSection(null, [{name:'hi'}]); wasmEval(moduleWithSections([customDefSec, v2vSigSection, declSec, bodySec])); wasmEval(moduleWithSections([v2vSigSection, customDefSec, declSec, bodySec])); wasmEval(moduleWithSections([v2vSigSection, declSec, customDefSec, bodySec])); @@ -429,7 +444,7 @@ for (var i = FirstInvalidOpcode; i <= 0xff; i++) { } // Checking stack trace. -function runStackTraceTest(namesContent, expectedName) { +function runStackTraceTest(moduleName, funcNames, expectedName) { var sections = [ sigSection([v2vSig]), importSection([{sigIndex:0, module:"env", func:"callback"}]), @@ -439,8 +454,8 @@ function runStackTraceTest(namesContent, expectedName) { customSection("whoa"), customSection("wee", 42), ]; - if (namesContent) - sections.push(nameSection(namesContent)); + if (moduleName || funcNames) + sections.push(nameSection(moduleName, funcNames)); sections.push(customSection("yay", 13)); var result = ""; @@ -452,16 +467,20 @@ function runStackTraceTest(namesContent, expectedName) { assertEq(result, expectedName); }; -runStackTraceTest(null, 'wasm-function[1]'); -runStackTraceTest([{name:'blah'}, {name: 'test'}], 'test'); -runStackTraceTest([{name:'blah'}, {name: 'test', locals: [{name: 'var1'}, {name: 'var2'}]}], 'test'); -runStackTraceTest([{name:'blah'}, {name: 'test', locals: [{name: 'var1'}, {name: 'var2'}]}], 'test'); -runStackTraceTest([{name:'blah'}, {name: 'test1'}, {name: 'test2'}], 'test1'); -runStackTraceTest([{name:'blah'}, {name: 'test☃'}], 'test☃'); -runStackTraceTest([{name:'blah'}, {name: 'te\xE0\xFF'}], 'te\xE0\xFF'); -runStackTraceTest([{name:'blah'}], 'wasm-function[1]'); -runStackTraceTest([], 'wasm-function[1]'); +runStackTraceTest(null, null, 'wasm-function[1]'); +runStackTraceTest(null, [{name:'blah'}, {name:'test'}], 'test'); +runStackTraceTest(null, [{name:'test', index:1}], 'test'); +runStackTraceTest(null, [{name:'blah'}, {name:'test', locals: [{name: 'var1'}, {name: 'var2'}]}], 'test'); +runStackTraceTest(null, [{name:'blah'}, {name:'test', locals: [{name: 'var1'}, {name: 'var2'}]}], 'test'); +runStackTraceTest(null, [{name:'blah'}, {name:'test1'}], 'test1'); +runStackTraceTest(null, [{name:'blah'}, {name:'test☃'}], 'test☃'); +runStackTraceTest(null, [{name:'blah'}, {name:'te\xE0\xFF'}], 'te\xE0\xFF'); +runStackTraceTest(null, [{name:'blah'}], 'wasm-function[1]'); +runStackTraceTest(null, [], 'wasm-function[1]'); +runStackTraceTest("", [{name:'blah'}, {name:'test'}], 'test'); +runStackTraceTest("a", [{name:'blah'}, {name:'test'}], 'test'); // Notice that invalid names section content shall not fail the parsing -runStackTraceTest([{name:'blah'}, {nameLen: 100, name: 'test'}], 'wasm-function[1]'); // invalid name size -runStackTraceTest([{name:'blah'}, {name: 'test', locals: [{nameLen: 40, name: 'var1'}]}], 'wasm-function[1]'); // invalid variable name size -runStackTraceTest([{name:'blah'}, {name: ''}], 'wasm-function[1]'); // empty name +runStackTraceTest(null, [{name:'blah'}, {name:'test', index: 2}], 'wasm-function[1]'); // invalid index +runStackTraceTest(null, [{name:'blah'}, {name:'test', index: 100000}], 'wasm-function[1]'); // invalid index +runStackTraceTest(null, [{name:'blah'}, {name:'test', nameLen: 100}], 'wasm-function[1]'); // invalid name size +runStackTraceTest(null, [{name:'blah'}, {name:''}], 'wasm-function[1]'); // empty name diff --git a/js/src/wasm/WasmBinaryConstants.h b/js/src/wasm/WasmBinaryConstants.h index 53f34048619a..3e61ab4edf97 100644 --- a/js/src/wasm/WasmBinaryConstants.h +++ b/js/src/wasm/WasmBinaryConstants.h @@ -27,8 +27,6 @@ namespace wasm { static const uint32_t MagicNumber = 0x6d736100; // "\0asm" static const uint32_t EncodingVersion = 0x01; -static const char NameSectionName[] = "name"; - enum class SectionId { Custom = 0, @@ -431,6 +429,15 @@ enum class Op Limit }; +static const char NameSectionName[] = "name"; + +enum class NameType +{ + Module = 0, + Function = 1, + Local = 2 +}; + // Telemetry sample values for the JS_AOT_USAGE key, indicating whether asm.js // or WebAssembly is used. diff --git a/js/src/wasm/WasmCode.h b/js/src/wasm/WasmCode.h index e8449fcc5353..305ca9ad32dc 100644 --- a/js/src/wasm/WasmCode.h +++ b/js/src/wasm/WasmCode.h @@ -274,7 +274,9 @@ struct NameInBytecode uint32_t offset; uint32_t length; - NameInBytecode() = default; + NameInBytecode() + : offset(UINT32_MAX), length(0) + {} NameInBytecode(uint32_t offset, uint32_t length) : offset(offset), length(length) {} diff --git a/js/src/wasm/WasmValidate.cpp b/js/src/wasm/WasmValidate.cpp index 9f3556d461c1..06d7d7952f24 100644 --- a/js/src/wasm/WasmValidate.cpp +++ b/js/src/wasm/WasmValidate.cpp @@ -202,6 +202,36 @@ Decoder::skipCustomSection(ModuleEnvironment* env) return true; } +bool +Decoder::startNameSubsection(NameType nameType, uint32_t* endOffset) +{ + const uint8_t* initialPosition = cur_; + + uint32_t nameTypeValue; + if (!readVarU32(&nameTypeValue)) + return false; + + if (nameTypeValue != uint8_t(nameType)) { + cur_ = initialPosition; + *endOffset = NotStarted; + return true; + } + + uint32_t payloadLength; + if (!readVarU32(&payloadLength) || payloadLength > bytesRemain()) + return false; + + *endOffset = (cur_ - beg_) + payloadLength; + return true; +} + +bool +Decoder::finishNameSubsection(uint32_t endOffset) +{ + MOZ_ASSERT(endOffset != NotStarted); + return endOffset == uint32_t(cur_ - beg_); +} + // Misc helpers. bool @@ -1560,60 +1590,81 @@ DecodeDataSection(Decoder& d, ModuleEnvironment* env) return true; } -static void -MaybeDecodeNameSectionBody(Decoder& d, ModuleEnvironment* env) +static bool +DecodeModuleNameSubsection(Decoder& d, ModuleEnvironment* env) { - // For simplicity, ignore all failures, even OOM. Failure will simply result - // in the names section not being included for this module. + uint32_t endOffset; + if (!d.startNameSubsection(NameType::Module, &endOffset)) + return false; + if (endOffset == Decoder::NotStarted) + return true; - uint32_t numFuncNames; - if (!d.readVarU32(&numFuncNames)) - return; + // Don't use NameInBytecode for module name; instead store a copy of the + // string. This way supplying a module name doesn't need to save the whole + // bytecode. While function names are likely to be stripped in practice, + // module names aren't necessarily. - if (numFuncNames > MaxFuncs) - return; + uint32_t nameLength; + if (!d.readVarU32(&nameLength)) + return false; + + const uint8_t* bytes; + if (!d.readBytes(nameLength, &bytes)) + return false; + + // Do nothing with module name for now; a future patch will incorporate the + // module name into the callstack format. + + return d.finishNameSubsection(endOffset); +} + +static bool +DecodeFunctionNameSubsection(Decoder& d, ModuleEnvironment* env) +{ + uint32_t endOffset; + if (!d.startNameSubsection(NameType::Function, &endOffset)) + return false; + if (endOffset == Decoder::NotStarted) + return true; + + uint32_t nameCount = 0; + if (!d.readVarU32(&nameCount) || nameCount > MaxFuncs) + return false; - // Use a local vector (and not env->funcNames) since it could result in a - // partially initialized result in case of failure in the middle. NameInBytecodeVector funcNames; - if (!funcNames.resize(numFuncNames)) - return; - for (uint32_t i = 0; i < numFuncNames; i++) { - uint32_t numBytes; - if (!d.readVarU32(&numBytes)) - return; - if (numBytes > MaxStringLength) - return; + for (uint32_t i = 0; i < nameCount; ++i) { + uint32_t funcIndex = 0; + if (!d.readVarU32(&funcIndex)) + return false; - NameInBytecode name; - name.offset = d.currentOffset(); - name.length = numBytes; - funcNames[i] = name; + // Names must refer to real functions and be given in ascending order. + if (funcIndex >= env->numFuncs() || funcIndex < funcNames.length()) + return false; - if (!d.readBytes(numBytes)) - return; + if (!funcNames.resize(funcIndex + 1)) + return false; - // Skip local names for a function. - uint32_t numLocals; - if (!d.readVarU32(&numLocals)) - return; - if (numLocals > MaxLocals) - return; + uint32_t nameLength = 0; + if (!d.readVarU32(&nameLength) || nameLength > MaxStringLength) + return false; - for (uint32_t j = 0; j < numLocals; j++) { - uint32_t numBytes; - if (!d.readVarU32(&numBytes)) - return; - if (numBytes > MaxStringLength) - return; + NameInBytecode func; + func.offset = d.currentOffset(); + func.length = nameLength; + funcNames[funcIndex] = func; - if (!d.readBytes(numBytes)) - return; - } + if (!d.readBytes(nameLength)) + return false; } + if (!d.finishNameSubsection(endOffset)) + return false; + + // To encourage fully valid function names subsections; only save names if + // the entire subsection decoded correctly. env->funcNames = Move(funcNames); + return true; } static bool @@ -1627,8 +1678,17 @@ DecodeNameSection(Decoder& d, ModuleEnvironment* env) // Once started, custom sections do not report validation errors. - MaybeDecodeNameSectionBody(d, env); + if (!DecodeModuleNameSubsection(d, env)) + goto finish; + if (!DecodeFunctionNameSubsection(d, env)) + goto finish; + + // The names we care about have already been extracted into 'env' so don't + // bother decoding the rest of the name section. finishCustomSection() will + // skip to the end of the name section (as it would for any other error). + + finish: d.finishCustomSection(sectionStart, sectionSize); return true; } diff --git a/js/src/wasm/WasmValidate.h b/js/src/wasm/WasmValidate.h index ba3cbd36cc7e..b16c25f81cbd 100644 --- a/js/src/wasm/WasmValidate.h +++ b/js/src/wasm/WasmValidate.h @@ -555,6 +555,11 @@ class Decoder void finishCustomSection(uint32_t sectionStart, uint32_t sectionSize); MOZ_MUST_USE bool skipCustomSection(ModuleEnvironment* env); + // The Name section has its own subsections. Like startSection, NotStart is + // returned as the endOffset if the given name subsection wasn't present. + + MOZ_MUST_USE bool startNameSubsection(NameType nameType, uint32_t* endOffset); + MOZ_MUST_USE bool finishNameSubsection(uint32_t endOffset); // The infallible "unchecked" decoding functions can be used when we are // sure that the bytes are well-formed (by construction or due to previous From e85f5ed76c8e58199cef296cc6544047006239f3 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Thu, 18 May 2017 16:22:00 +0100 Subject: [PATCH 20/35] Bug 1365654 - Add a move constructor to HashTable::Enum r=luke --- js/public/HashTable.h | 14 +++++++-- js/src/jsapi-tests/testHashTable.cpp | 44 ++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/js/public/HashTable.h b/js/public/HashTable.h index 9011eeac1a74..29a3956fd3b9 100644 --- a/js/public/HashTable.h +++ b/js/public/HashTable.h @@ -1070,13 +1070,21 @@ class HashTable : private AllocPolicy bool rekeyed; bool removed; - /* Not copyable. */ + // Enum is movable but not copyable. Enum(const Enum&) = delete; void operator=(const Enum&) = delete; public: - template explicit - Enum(Map& map) : Range(map.all()), table_(map.impl), rekeyed(false), removed(false) {} + template + explicit Enum(Map& map) + : Range(map.all()), table_(map.impl), rekeyed(false), removed(false) {} + + MOZ_IMPLICIT Enum(Enum&& other) + : Range(other), table_(other.table_), rekeyed(other.rekeyed), removed(other.removed) + { + other.rekeyed = false; + other.removed = false; + } // Removes the |front()| element from the table, leaving |front()| // invalid until the next call to |popFront()|. For example: diff --git a/js/src/jsapi-tests/testHashTable.cpp b/js/src/jsapi-tests/testHashTable.cpp index 0f01165bf2f2..6229a584a5b7 100644 --- a/js/src/jsapi-tests/testHashTable.cpp +++ b/js/src/jsapi-tests/testHashTable.cpp @@ -6,6 +6,8 @@ #include "js/Utility.h" #include "jsapi-tests/tests.h" +#include "mozilla/Move.h" + //#define FUZZ typedef js::HashMap, js::SystemAllocPolicy> IntMap; @@ -388,3 +390,45 @@ BEGIN_TEST(testHashMapLookupWithDefaultOOM) END_TEST(testHashMapLookupWithDefaultOOM) #endif // defined(DEBUG) + +BEGIN_TEST(testHashTableMovableEnum) +{ + CHECK(set.init()); + + // Exercise returning a hash table Enum object from a function. + + CHECK(set.put(1)); + for (auto e = enumerateSet(); !e.empty(); e.popFront()) + e.removeFront(); + CHECK(set.count() == 0); + + // Test moving an Enum object explicitly. + + CHECK(set.put(1)); + CHECK(set.put(2)); + CHECK(set.put(3)); + CHECK(set.count() == 3); + { + auto e1 = IntSet::Enum(set); + CHECK(!e1.empty()); + e1.removeFront(); + e1.popFront(); + + auto e2 = mozilla::Move(e1); + CHECK(!e2.empty()); + e2.removeFront(); + e2.popFront(); + } + + CHECK(set.count() == 1); + return true; +} + +IntSet set; + +IntSet::Enum enumerateSet() +{ + return IntSet::Enum(set); +} + +END_TEST(testHashTableMovableEnum) From 82f369c7bd747c56ccd03f94bf26dd4ec20ea22c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Thu, 18 May 2017 17:26:09 +0200 Subject: [PATCH 21/35] Bug 1365683 followup: move urlbar-searchbar.inc.css include to the right place so that it works for non-Photon MozReview-Commit-ID: GFTtOBlSLnj --- browser/themes/linux/browser.css | 4 ++-- browser/themes/windows/browser.css | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css index 6fd562b639a0..87d6bf9e9563 100644 --- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -275,10 +275,10 @@ menuitem.bookmark-item { --urlbar-border-color: rgba(0,0,0,.3); } -%ifdef MOZ_PHOTON_THEME - %include ../shared/urlbar-searchbar.inc.css +%ifdef MOZ_PHOTON_THEME + #urlbar[focused="true"], .searchbar-textbox[focused="true"] { border-color: Highlight; diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index 9b0139104b3e..7c025bd70725 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -662,6 +662,8 @@ toolbar[brighttext] #close-button { --urlbar-border-color: var(--toolbarbutton-hover-bordercolor); } +%include ../shared/urlbar-searchbar.inc.css + %ifdef MOZ_PHOTON_THEME @media (-moz-windows-default-theme) { @@ -672,8 +674,6 @@ toolbar[brighttext] #close-button { } } -%include ../shared/urlbar-searchbar.inc.css - #urlbar[focused="true"], .searchbar-textbox[focused="true"] { border-color: Highlight; From 043815b0526febb6251374939179dd4ccc6194a7 Mon Sep 17 00:00:00 2001 From: Dale Harvey Date: Thu, 18 May 2017 17:34:40 +0200 Subject: [PATCH 22/35] Bug 1365906 - Increase URL bar and the search bar text size for all Windows versions. r=dao MozReview-Commit-ID: FMu4bta3lgV --- browser/themes/windows/browser.css | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index 7c025bd70725..6a9bbc4a2c92 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -674,6 +674,11 @@ toolbar[brighttext] #close-button { } } +#urlbar, +.searchbar-textbox { + font-size: 1.15em; +} + #urlbar[focused="true"], .searchbar-textbox[focused="true"] { border-color: Highlight; @@ -752,9 +757,7 @@ toolbar[brighttext] #close-button { #urlbar, .searchbar-textbox { font-size: 1.15em; -%ifndef MOZ_PHOTON_THEME min-height: 28px; -%endif } :root { From ebfe17400792dd719f43c542c59c7971a954218e Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Thu, 18 May 2017 16:47:53 +0100 Subject: [PATCH 23/35] Bug 1365654 - Fix style bustage r=me --- js/src/jsapi-tests/testHashTable.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/js/src/jsapi-tests/testHashTable.cpp b/js/src/jsapi-tests/testHashTable.cpp index 6229a584a5b7..9634754fa4f2 100644 --- a/js/src/jsapi-tests/testHashTable.cpp +++ b/js/src/jsapi-tests/testHashTable.cpp @@ -2,11 +2,12 @@ * 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/Move.h" + #include "js/HashTable.h" #include "js/Utility.h" -#include "jsapi-tests/tests.h" -#include "mozilla/Move.h" +#include "jsapi-tests/tests.h" //#define FUZZ From 024b5bad85fd74d87c52eeba9b6465e69888b28c Mon Sep 17 00:00:00 2001 From: Matt Howell Date: Fri, 5 May 2017 15:33:18 -0700 Subject: [PATCH 24/35] Bug 1361326 - Delay-load DLL's used by the 7-zip self-extractor. r=rstrong MozReview-Commit-ID: 7O0NJBVxaLQ --HG-- extra : source : dc853c57ba1fdb220a3731c9d00d0b60bbbf18f2 --- other-licenses/7zstub/firefox/7zSD.sfx | Bin 123904 -> 231936 bytes .../src/7zip/Bundles/SFXSetup-moz/Main.cpp | 14 ++++++++++++++ .../Bundles/SFXSetup-moz/SFXSetup-moz.dsp | 6 +++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/other-licenses/7zstub/firefox/7zSD.sfx b/other-licenses/7zstub/firefox/7zSD.sfx index 2a6f60a6e3e0e70ca045206ef0c26a02dc14a1a9..5ee166eb294354167db6c7bda901e0f7908f323a 100644 GIT binary patch literal 231936 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 OibH;2h~@w1|NaM{>bn&H literal 123904 zcmeFae|S{I^*??$dy`zq!Y;B%l&DdoMuQp+)x(v)fpy8&ww zgPTBZhD+)DqgLyW(pIfjt<);6)?z{^3t}bwsDe-%6?N8)8Y*v$HRXO^XYSq22C#j< z`+Pso^Znz?^N^i8cjnBQGiS~@bLPxk?@b#dt0YM>{_DCVh44%N3dP@l{ihGF$Da4< zSn0)4e?C8Cnf2%M=PvvHip=_kJ8o;Z<%gNyyXE%V@9<~dx+Jq9aC_$WZ_k`DyCU<4 zcPw6V#h5XpvrNz@?iv+*HT|~U*#EzzF7JH>&js@LdtbosUs8YA`#bUbz21;0x3u>{ z{N6S3I+6dmtE#sR&&}uF()$Pe&iVfLmJzHN=03M1&9cbSKfb)aI+oWb*)3_76iK?m zB1tb8x%&A?JCH8KFZG_%Nd{ez67-?pSmx`=N%R{3P>{;Tf6YhA!oSIPSfq87*nrjbRZ2cTJc`N@A6a1bzRZ0qTzdZCHjg!OZ9m0pHi+6x&Qya{|`{0iXGL; zWPTY?$=j)UR;guvwNzG16}8kBE_4vbge7Hqk+j{j%+fKIZLi%bOQ^)pp&DIb&mv-e z)iYQ1%u_u#hdn+A3Zqz0Rh`1R^LE}82dDZRVJRDL>txlJRr_itl4@zXTAHDjW`+x$ z>Ed~^TAHhtPEqUgRbRGRKT9nwR7+>5rKPHGYHd$wUsjRCC)zF2>V64aRZ+zbR%_nZ z_Uo8}$jc!XilzD4KWUcK48l|7M&dF?YHe;H)s$wcWmc z574hm=l4D;N$p+p{bPfRWc0WRaM(aJDerjuaW!znpTbhAbf2ubH!?3*JqmN%RF8wX z9jeF4+|Igm08i4U?qI1Fw~dv^H8! zbzNtHbWJ;U0*MwyZSLdsNNU;)yg?P2{{rx|c6yJ;WM38EI2x1MCHc?Oie;bi#>bz- zR6l;;?5-1C9qu%HTNlKgHggp}>k0g>@Us-4GD={s1TU6a?_MVPi^4DJZmqKfFYeK+P^6dyAS>i>q#HiubsM{r5M z)UMmt=b>rQ!+ut1V{!*8bq4b-tiaxWJ9r?tO19YB7gGvU4K8s=%ciszN!4gb;YAB2 zsjbU@#j;gkSpLYv0<$9mvrz&wmTF?A3EC^X1H@T2g8*>ZsGL{lEBjG_OOn=^+S<1%$1&qWR0gVVTr+-cDDy-x-?{4z<)7mI{sf zT|QvIN26}A*&+G!>lX3PewKi4nm-MM{0@Y=!I)@{m__05jD_kBIJB~P{7obUTiM%g z1kJ!)25S)`&0OU}@v%G#qzz`m0L7R|3||`N37ff`tIMba^(A$<1i(htV|0T{xU_7p zNnbmEzs96*7eoc0nuxGN(Dy5_mKb@>%K_2OvjvX4z02O_JUx!%c}W~c`!EJE968}A z3mgFjFq31w(B1va-S2Yu+1LH~pWt}G;@V8v$8QBMZ?{^&Uf?UAoYz(0?o-`IRQG<+ z$iBW2CXnjx6+AXTTt|F1rGt5U8RmCB3$~%>OtvsdtL&e%Y+Wg&gF@tV>f_KNaLi`2 zox!hl``U}qr}mwV>Fl0#<|wcB>2qvskjm)x_678A1jZ~S=80>&#Uvmv+3=14#vorGcw(qpBe~bE9l39>zZ+jid>A^0= zwWnzyvYGM)Yp1sF#F$#jA#((mCDJ}FUTpl6jgSyR{Gfn^cfwR?T{1*+*{4rX3L&Ar z8+oZMF0jMyA)qI1u@p+J5hsQs4r?5LcqFW8r-e0y(KUrXU_lc|(HGd(s5Q7lZ|`cd zvDukyMz_pnmqssUvvaApEp}Eir6AY861|ks%VVObeVv17;&p0rHz@4m)j(2rANDZ! zVa$A&`+)u4KSMa_FE!y0F;ieb4{Tr-=msTWs6PHV`XELqnZJv)#}(*P0|WMT4}&Ws zqo|Fo(G3O%CF|v=YKrCw{28h`OuI5jr&DQX8`0l0(cj4HKx-sx#eq)bRU?6Z4b%5j zpf6*h4ihmQK-pK3HNZ8YV`CCJW)VP5#3%8;KQ;pK`(tfjs1G7N3^@akwrA{#yp}@Ox^^sL~ zdz3!>1ee+*t<1r%2dp4Tma=?)Q5Vur=AVNskfrs>dP;>;UK6$CXN}5_;0N7}(W^&L z3#Klp1f`tW10vKpgm~8jHa;j@q~JDNdl%HAThV-SGy`v&ApGg=yYaW@h_?#c;rr^~ zq@5SO!73fBN1eyb=f_XqVs9>Vf!eL5;CK2o{ z);Qe>3_q(Ia97c9^XRsZ2zKOgkgdNZ?Q??BF8sK_EfQv zeDYK$@yQwg73UL-O|aiqQB}>FZEQCWqYK(>AMb?8!D9O z9089O&>q<|Y}hsJ$1zK2t*BtJ`V=))H{4V>B@?nNxKu_f3SaRD8W@sZ+59%7wX#yt z@&b`whIA62P3iRyg8%Sn<+CtV(0Vw<0XT_*EaD&1^^>QNA!>KSYBxn~sE-gjs7o>7 zYJf)f!TAy&1az&;XgCY-VhE)pZIB|H+mJS-h{7eLN6?~z-JGRVRad}0a2Wt3*8Ec8W6&iuYvc1g4=4L6Xvl~A)buOlo2h-Q0 zmMOKwP{)Wqubha}C!9hK1tAJAAmEUL;HgvsHiD-{UyORIMZM}enSIUkG!&ubB32Va z5}0B7n(?e6=4UO3{1taNFN!C`<^;FiRxdEy%M zEf&|H^ll<7<@mc7M!p-Nc^?>l7s@3fZX8Nr4DyKy#ux=t#$=4op{qwQ#t3UH?oOLU zEKjI>MdcH?a&-bX%2GuoTb>1rjC~C2!;~}bis9kS4gDStJPkE3xLh2T~{?_z4O%_qHT-0>1 zgJ>AKu2`{d_wg3gY(L(T6I`5?E~#KN7O+80TK!qsP?fug?f7lLAvhx|-J#FQas=L6 zSj&297R(1K!R1-99Qnm@ycQ|TS4K*SXl9-g`3$MhYeZz%Hxhh%>0r8wyh8;w&Z=s) zX2^a&d{7}Q&s{z0k^$A7ZnBUJR|)yBENV@kT64sHpL=3!^F#@YRLr)*Ce`gIsBr|+ zQIX`At0%^Jd0ml4XkpF!CZ7*>2)p0(V@ycm=5@JX$h!OO!Ot+?2v{Hm{LSRmprZ;$ z+mFQ(amwo82|aLCl~28B!F3;(44cOQI z00R$2>(gc{e25H^=n);h0C&0`=o9kER%g*P8KwBah_2_6*CIu&*{0U)02N&B{dVo2 zWEFW2XqDUMu&JxzIZ)lNv63v+-32f{vVR?H!1blyW55ykpAhRBL2ZLtseI=KF%Wod z;m;v`%3!hWk)3F&E}#E(EzRT|s`rR%7s@OCQG5bu%??189%?_N*7O@#Ixx=G3X&FX z1v#U%dIw7l4w85&k=gowB8R+M4}{QrJ&<1RtAh=;i$endR{J4;wc-6r5LF3XaGl(R z(L5!g3!nZt-i2YI#Cjqr=tA%Y-G6o*w!Q6_qz_3MD3PC1@*b$8Qb^1?2a{9bp5py$ z$j+SaKf`b#D^LK7N%O}$Y>V}@PGY;z74$ZZU;8qsmC2t!wes8%m7TH5ntK~GX&o(V z0KmpeHUpV82Z&4$Y{OqY;6(lyAcaW)S0hY8VwQUpW=pcJN9QFrH%T7<03@Nr8g15r zsyXG=CimRPVI&L$PryY>rY7_D*Cp}sSVFS_IL?JLHO)UWfmCk7VN_xUc_L3g7SDrf z!;^Pxgyw8w&2Ix`Z)+p1W|00aBv+rXV3ihFNa$I(f7D3yp8cP|jLDV+R&k6#&mJv@ z2D}5kMt{=&Ly&)jVKdxg2T8CF*c!%Uvhd|(cNo&Hy({vnFwpw>Q7|_7TVcDJ1MpwO z>g1G214;{MAmUmXy5WC509TMjCd8hFzk{?e6TRuYZV-;HVl=OLoxBDQnpZ&OzJ=z^P;;#xleOaN@13-i>cO(Fl6Mzjut0a*Aehg|1 zv1tj2?Ljf-J`(*=NGGTSl2Ck=YMUtjR-_FSe<_N;h8-3Q=7_s%oEzen=N*dt;~yiM zyEvh_-AKpia4YIZ)_)D+qjP$+y>(UUt_}%{>%zy$qoliBCR;ft+2+hApLiC+u?nSiS~Eh#nZ?Z?vLa5R44%zY04Wi=+ZzzA)~ANJq)IUHmgD zZ#V|Q6v75)saUkCDbS!`39Ur&@$od!U;$4iseMlt*^t0CR7jm(anv9dcqlM%G83WW z1|RWV9i(0q^cyC-V4Gq0+_#vqM^QzXu~<{VjO~=IK3;%+k{$b5+>Q<1N?3e004%Uc ze6%|65b)4iY-+8no(Gdst<9>VIc^E3+=rp$tM8FuAVSru^W_9>35G3ZAXpAT?x2PYOpG41dz#V;E!9$HC=Pa(vCH;`tYEU}1; z!+?qbG@{OELN1eaz?txGSJ z*jpW*Z2mqlhvOw(3auj)e)k1XFo?&>oD8Giy+nHaMZNI-;^$tW+!|nmd zSImWe4CDZ7Nyio$;X8*_WmpG+y!9QX%z=^P6t>j_n8E`4;qLTA7qD8)gMMwMjgJCm z!I_F4Y#xvT7X+L8r9cM5R(oMRn;L4pXPOik(|XVKQXtkYYs{i%$>owRCO^!I{$YBv z?LyqfUF0;`X-}^#(^aZ8S5U9Y99EF#yY(;$TUP=8*w$5mudveew9nr52t|jksu-^%8gpUp?e?^eN&{t@qp@ z`5m+dEmXehEhw3cH9<6KlZBOK9Y4UvP3p?}XRyoaE7#{xkLOX3Ga)C4XV7aG7FU-T z#zT^t4nqALgo<8`#H&~g62;E<oFPY8^gKVWLsfms8VE0KD0Bl=k1}8m){I@MDA8w8h+40K*k!=1f<-&&|$g=Ww@%X0kn8X{! zW5{^4@Z0bx43PC3k;UiXQOu0D1h z&p~OSl4YKRBy_Jp=Qzv@t!1hxZHtvo;a?!z)aCoBgy~t`fwVvmp4HcoHgWqMl}_}5 z^#r@KBM&1N8t5nom8CjGTSHyS+qv-7473y9=u%fXREM^PD1hLin3gwliA)R4Xd}F& z$R_hmAKsi?ny5Z9kHkG@G&a@}V8fZCpk!DgJZ{=@QY=%Jur3OnWL~JHv5+QTzFME1 z5YIF#TsW1;rIyZBOXsPjH>q|Z{*mP z1~U?M2XCgxb4sCl8r}~hpmcvGJgv}#@S)TUEmdpwBj5;9g9U^p zWUE`zrQnItOx|E$OFO0ZwqH=r=T`ss>CeJ4|J*WvdYQdlLI?Bb724ar!h5|wJJN$+ zwp#c`oX&FLjZLaP7{b60=Vdu z{CPL{uhi=&6KbDR&IJZcn_jPU7EX_jF&>?Iy`!_R*vEea{IDvf;&-Db`0c0};uj)` zEx)wTf@{CU*moG48b3LJ`rUm=;Or?5R$iKitz}2vUWBBsSHmn$MxI^Hz zqcxO;LfZOG+d&R4c@5~3d&kPXQSya#Wr5=;pve0ga%I}5Bj%vQ`N@MlrL`0c@lEhb^TEXp%)?j(#yn@68pKbg+iXSwo%~bda zIG$qFr=L&v1&;d%GZlwhRZ6X34_*h6fthlh!5t42ttt7%J z)Nv7@X5o1)hLD1mNk@|1AY^yU4Xun6RH4@h>}molsBY}SVb!!`AkhX;AOamc6Fm`3 z(pvB>cp3qg`6#Cu68y<#N|JQr4~ry;G`>M3`zYBik|ernBX=PUDbDYtJc!uui{EU1 zi};<&=Zjw-FBiXyYa^vdgnFq5uYY7{Mks)A3P~pF4bR4VQ$~+La)kc)&sQctg9M?K zNvlV&>?t1CHYJK3u(=U1~6 zaLK7~!HazieM&xW0!U-mVtyb3pV3f774L5X@#BlBXJM(t7gJusz+6mWV4N7H_`9oy z--V9Q7`Toki~-4hF$RxA{h4E+A~|9VXs}1X{A)wJz0e@R{N%#~n8f4)rf(SLcTR>G zy6jTadw#fH!Y-+T!ZBSw1VI?nZ$tNSN z-HO|)dVIsMLDUM0%I?2x1rW1dCC=!r2e z;x9pHZGrwuV`F&=UbbRCTr8QYwH6tQplcOq*kZ#v6PsQLSD>8|EMA2Xc9d_yz+h$I zfbz4cV=2YS5ZmsNEwwO&Kx7dmQfFfa`Ez%W8k+C7RcnD`Y!JIt3u!g4t&qvl@}nbI zhKSv_T51BOkXoF+Ah<}8RLFN5j6e#`Ad8A@i+TlZ+4x;(9l11?WGizKZS7Swx9;QD z01sNK+d2Yvhq4yf({jgXy%0+ya;VVaC3>87qtCLc7!D!L?Wpq6BHsaRN#H*GCHSqA#P^M!gWkg3Plap37H7ApU}E;Cf*}z~;|@b%bDOfcCzi2x0REn$tuUm|G2OJ%NYb#_plrn%E-d;Wj{(`ny~=iR}uvafGc zN>I;i`kPqx)tp*UZ9$y9sQZXfcM{co2%K>TnjV!hs!9f!pkCN?AQ6NZc8o){PxT(- zzkvYG(_P`fA_(>0x2ny9>NEiE^y+N~{a0aLq6r^wLn(B4szA;=_z)ixZ^1pN^hQg9 z&4YzaZ$!_sZ|<>g-dTEKH{1PC_xo1<(R-3)Nf}p#2rsdAHGFjCV#Fy#7eLP`a1X5< z&D?|6x2NW~T>?MeTn?uosQGae_?)nx`jNXcftWxBiN$)<4-a z6OZQU)z!iy0v}PfS~i)DFZWgGa~!U)z1;&o)(~I)7HsnUbt%3Iz7mbmo5^^S*9A{a zia(_=axR`aXC?2yu7jq0U`%0fFs12pY`7-lh1&e4+T0s13?zi!CY)W8smcmQKyaU#5pl84LNz@_l zi&pv#c}@>EHlqrwgmHL8vhHEtBWh(ItL*D^j=t_EqFb|yf?uaJk)y3nQd=^E;gtDs zD2_i^Uaq!etF@Dvqk=-eu>qPk5hmGD9cn2DSIo*g# z2kso9Rwfb9{YVlRDet%^`m7NyQw*dLaO2QgGOOWux7JB@5?T4^-9;=FGt-GtWF>M1 zdrS39=A#W>AB{);_+(`&zugXfW&kl8O3W2nva3*CDAv?X<}oKBPK;r4x_`b zHHyKlJD3K~_$VMsq9qEbkdlytj}XtEeTaB=QAL&JK3vTX`ml=S_n>CpaUADqe<}$? zLBzvuekZITJ#d&53Q`3Dir`iQ2Vm&i*E}hhIfRgZ%^TuXpvD%`hH4;0)`#ZaM1f21 zUkrxD9kT&&G)9Rt%lBd8x&%$5xII*_oz7N|cP%@|qgkVDh zmL7PPDw{?4wciH-k20TPlKfCV#ik0d2cJP>%s~e&I-TQ+;IZi3nq}{({L;R8M~BCb zf;eArKJzIqpSPQndVY@;X9vMk?*4*6f5SE$f6!)U=aAWtXru=T>IqArkb=yQ zG}f}}jJ%!gd+DqIPDS|Bis(ed8#pVlQ*YndWJe76k%sr8*BXNTY6$i!e(_=O>qU9+ z%{QJ0IZ0T2h}}QTl{54Jd0V)Q5+#VIem}44Btc;65LgxFe7MA@nT`2?HHOQr1d?4| zn}1wdw|^Yw#zSJd`1eLrv7EGG=!7XDtV~uzdwI=DMK#tld^ZdvU@#q~0bc-<2*1_* z86*%%q_mPRq=DlP&?juF?Af3 zuRvuQOT^h>;3&R~gvJz@62-3f8s23!4zpmgC>XfYlo*#lU5^f=2b+f^qPG(qA_cCakdG$_iCx@HKVD}Qzu!pkDfk6KXHr-NKl~y1(Ce&| zc*(oEUKaS&Bn12kyGSB+yiFlX4^mI7@(#gSSy7h;VZ$#+%hcm5Ap&p$i$vVp*bk7-jFYx4eD{>2Z87z60U08(Jlnt{e5 z<9Gpo52YZo$hpH`xB#t*;|k=G%A<`?VMgmDWV}cCX3Pp#PvECu-+_kL&F1)RXbf*F z>TJA`^g3`{1)yjIE8oVuSP57GLDmSdq4Rf;J^K)B4M!@rBfU1n^U7?L^-h`QRb_uu zf3Rz672QD%3%>lLMaM<04CLoi0 z$q#FaVzs8r2xCGN1-aI7%7UD?8e*5cM_}9Z3YRtr&0oO$q5!7Ayu6)6>~o0-m50%P z*a02BxvX*rb9O?w4X8d}!v`@>RRmhzplUDwE7FkybPTY0#F!Fom*P2kF6Qjnu_~Jr zs{97&lQ*qMOV`P@th-JbaiF#yM>}Y}9X~~j&@UXMEfiTXuXBBVxG>xBPZ!2c(vs&n z1Eo4j=hpT_egGZ`apL4Hn6AP8(uyjWU>MhGKAEtAI>iiJsqmHbjU} zFC(NPI5;MdR}kQVD-gH0QfYZxl!SBg1C%7y{3^580=!#LZ#_Y^!XsBY%=Qk zGlPSpjMSNFZVnOW9pEp~eA({rj5LczYz6g7V4?vZ;tDZp{KXh3?EBfEBv8V>{wEk% zvEahKmW^d}xZd;MLEAlt3)MiM>i`(Du_)MlWR!`pz;wTlZ^r&EVd@Z=B7_cWtNr}7 zeHcIYevA7sl+ieMA2E4Nf5Yp>oM5&N?s`wxbMV^hm{i#}2WJvcLqx{Yq`^L;ecc_% z5DlJ16v86SAfnrB@aN!Dwou92>v|7efQizAZ8aOK$5#FW!LKb8w}ueJ4Xvx@N&&0I zt604K(_QKa~m_@S+NsAN20f>fgNh#O-u@d z`@0P8?*dBT{%48%EhN=ngc4u}T<_W2UjRfway8&+xsC9`nw-u@KE&h)gM*epMr+Gl z$$ut9q>pa`DfC7kLk!T_SWZT-Z0~B!p-?Z(b4RC)(MKVMLJVpf^rb_V>HLfRL@-BL z;2psg59b{hP9<*_e;BI-V>Ug@e~ff=9P!39;*Ez$Vs3{eZxVA5VKNxKwg|*-)E5JA zQv!f8q*+gN3Tt+PLj#vVmd>OsU{FlIHT|ylTAUPe6ZtF6+4L%23Cv~&`cF~TM$`_u z08C~5BN3z%xP?}ISQiX7BnJnTl^+DTTNM3Op^8um9=jMC{*F7K|2J;`6l-AJLD5DV%LYga{Rn-5l>w!9u0QMLaU) z!Qj#Hd0_$A1VkaEtA_01;){`0($?yKNqIbq~Eu;)qwmqg#G$>EexjKD72N*w133% z0+qz-;&r2z3vQQy9h5TY*b0t{UP28oL&Gsc<0YgK$UMf79aexY3w$YPiv2SAGKj!G zyhS1~t19m}Y^MP=uwSehPSPdN$XaFZ9NaMg-Rk4Fqb*3OR5lQy)n_a0<_e+O#V!@; zk~cpyB+Iijm-d4B)JmIu{YE@tLLfMU)f{FkZJin8*^~#`4*4$?JPpVPzYr8zC7%0z ze5die3=2=_O)P~G0DVRvCD`ng{MR5o-b_zI+8M~D{J>>xhdOGf`!A$0t}&iDXzQkWSM!LXJV? z^7*d;a4Ld>t!`y}Gb|+NFRlh`*lxG~cmr6-C@PS6>;MtS=+Yu;W4QTp(WF5Yw@4e! z7l^b$)Bpa6q8j6rKNqjW_#1;{$D`l?V>~51o+1|=nAkWijrsLDErg+Lpnjom)MhNU zoCq|=q8ONt=6ljf9K4D2D7@pB-UN;^SHoCO!|MJRiX8@LJF%kcZTnjCA2p<1kEeU= zYX{M7$}FMGQP^*`ubqk{E!%HD6XbL9-_1vf?eqOpEd#V7^UTBX_cR4}QWVw1#p09}Y`?!)0NnwWHXK>HbJIKOzV z|7`sw!j-J;yL~>s7un<bUe5$R!WOZx-) zmLpG(2HZ1|HzKEJIz_lXh))2^cQFw=e?j|ohB6CM+ob|xMXjqkx42XhROzfV$pdd$P^ zfL9$Z>m4W~&gaSuCk{uIb5;${o$$qzN`sCu1mO@FN?WT?+CB7DFFEo<@8m!Pt^|P9 z{rMIWfiRY_BE)qx{k)nEQiz2zUD*M1FbSr0Ag7>)2QJo1R?+(KQ%G4_8*N4b)TFHQ zQH!#EMOg^2sx66j4}biSb*61bg` zB-M$W8+a4txi^p~C*d06KM)xqvND0Ki~&rxx5I^p^%|_zz0|ljq^%`62B+_aasxIF z@A~k6D*k8FL0;-nHRSM{@Yx?Hlo09a&qqzblX+t;LD_(`mDf>P;77~foAG4eo`MEo z1w`&j9qu$WdYVo@|B2axy$unvgr8`|k*Muev4O{sUhm`Y>;!S4>Ty=KWJc2_ zZT4dRGqeTC1S>djZ49$U>EZszF~e9xG)H~}I$#`J zvQyJugFNH!ra^M!HK;tAUv~ydzeEw2cueNw=Hk&iFh8)bLi_PXK&%_ZbD*nX@%qPB z)TJ1iJe{%tczi)k7v+yO^Cjd{A0Ou*ZXw2eoETG>W{(4W8h--VQa#vUgir;Q{A>@1 z?i~pH9Tl}j#OS}Ky6uz{e2wf`l)XV@Q8i>bS#EqTQM~#I)Z-k5ou%EuMky87SXg{f+wTxbE$3eXbEP<1vHS@OX3Z; z&j8(UaJ}Z)Mz|X`T6A zl6rQ3I*JK@NdKggk&Uy zUJ&w`c;MLrc}m8Sr-vBwb@&_jYybY75I zR>Gx{0KQa|je{=`U<~PUxkww}&lhO}`~*=Y20j%}26#JxPag?BQc0bOgMXJEP3+$i zX#@OLkv70@7FA;4pTd&?{u~1T${=As)fj~_e9Pexow-|7jkmU3Ks7qENTiL{=8Ck@ zTB)cKYwbEb8Lf?@)@GtLe$&V{CW;F2HWUHNXk!TKIc9|nh_unh5lYAT;B7n^Z9ut8 zkx$^%HRidPDj`vJmeD~Qe_A}oTl*P3n)F*Q(ngQ&6=|cjMo}f!qh)w9T04tcyGyhd z7cwn}Ml|jcRpX6M7LW19&!I=Nak4~WT}>8gqw%jR61y73lhODjYJ3RBYJ!BbjBI?1 zs2XqldGQ!;d?P)Yjo&BIM$bD$+GxBs>B)(;>l?IOlo|ikX-S`A38Xq@hVX@-gt?4j5nT7k7nbOMcQaQ zQ>2Z?ouW#t@f186jZdV;FGS;pGO6jOiwL{}_BHKz!74XFE#_w+p1#9=U-xypI!dSd zr!Z`b&%3^3-ZWO(!EA3}?*=(+?i;uiHG@@d!X-Oh$^vukHr)E4JnSzv+HWb?nf;mn0^s$= z8AyqS4&idP+8HeqtC6wkH4w*xL$D88SLGJFYG*V>=r)TUY*HIA9wU1KKBXD~gKlto zmXQ4nFm6}W_3n*TO?W*K&y+W|C@iJ(Quo;8$xEWc_5?71Tgc~hzpT&IR6c_HH zBHSM9GvFqhxb5O|k(u&Xr8GfoN*U_1-Z;~b8u=1 zyFw+=kE+emzd$}g35?gVpYFLCafdEjs3Ex|xMUE)M#q@@n9H-gDHyp6{6VXuLB8=F zfQorR5F2+4{;QV}7M?K~?u@693ii*S4S#F}9z}O<#u5pEV%ox0>e^Q7D-On4D9> z85{g@K%@QptuR5glDq0G6mI!rGxIrQ=37O4;Q-H0Y?o883BL)gnY-n`F{}ItRf1jx zCS5NK!1L$unkw>7u}4R&xMlD){F!s061HKC?uoC;_`f z;m^_AvTSBi*OE&Fo*#8Bc|))={Hhm+?u^Cq01v=97AKW~@~=0eSWlY!Z~&ngkvGW{ zZ$2!oOb&jdH;fj0A(5An$x_M@F^Mn`SpK~f9Y(jo+S?z&D-~gIG^@4=rzrx;HSae5 zI9`CL6tpr2jUtGFb{G~Ac<+FY9R|$A0X`jg!+}6JnhW|Pd=LC=C*f!Xj;$2p=H8~k zJ0$K7=I5cD@PYGf&%-${yh=5jpp}y-ga&kaT(lcl6m0Ib1V$C$+K;y1fF^jH7^n(1 z@0S9wW;dav;XxYdbn1uS0J}~z3)~_9>|k@qa?e~e&4+}G<25Ap+$#Raez5jWaO(+L zwc=*mg~X6Yxc$@~6uksGiE?uEZ6lG4J&G%+LLYw*aN=D?paISeIZ&Y*{Bm1AULlTw znn;2Bkyp*aPzx`)g&cRSrxLc%K=Bn+q6hOroRD_4 z$o}iw^@_%etNGP=5CMZ#6bnztLvWwNJq=#}huaTvp3t zxZMY%dSNZK0+m2Ax`KDS3_^I14aXiLrp`l+ZT~&Rg;&>Q;OeSX)K=ovRg=&ZQxN;4 zj%B4o*d_AO?PZ5Ba{xSkh1FyO_V9^&1ngBIEb%B`BLkpEW@DGq-CIjlXOWI4krB+o zVpLoy9=eI}m?rS3n}=FQi-`D`6(di|mkRDTPRhTsS#&3U+@3-NZznKcy+GZmD+QR} zoE}VF0+@!=fN3Q#n*C^S{0T;DS>w0Kg^(-7D!Z z&(`qRssCc&ziQG4?{V)6q}vCE8FJWQh|a?c^$+wUTVeMjs}Sh5t!B zPsNE4dQRdNZ~`mA&e$>4eWqW-525UQby_r8owgJKt+lujPh5%yM!ZX*m}&M^&Yf}@ zqCIkPj-X`}3qO>zb)O{W=e);ytb^73dr%Jsq!>pB zFBaE12A3j=d!@{$qX3&oI3*z%qNr&4^rcXMM-kk*5Zq%(Ix-wd?q5s&!EJ|Bm#&1I zk5UMhIh!n;-@v5+c!;?|$+*Ta+4gi-yZsf$&q-Iiy#yE|uXa0K#m7&r7&)aP$!&4? zy}Hw5zizrca6Uqj5#+iPUem2PfA4grT;~XkDJgV)-SFim3+9$0UELM^D)a;5iMr>& zUt$r^MdAf<2Dmr|#PBM0h(|yegAGv$2q-p>bi?c_Qq&@wdY3~TN5>F6nDDu3%M`UL zORLR)Ra}i;EUUQ8wb&Le7GJ1P$7?OK5VovBq?FjgB}V2%&<+ABnRmfLLK+tHW|5-n zW1o$ueu^NXRv?cZJZ$;_dvYINgt4iU^Y&_G%jc<8i`1&cYSl96 zWJCL^M~3K?XmkPzd}|k6t%*p7CXXLx`A3- z^AzR#uoP5O-|+W07NL&%z-xFH>(Xa18jLP|C{om=gGNeQPeAxCJe{sv@)a}zpE2e< zdZd-j!eUOQDrAY^DrL~j$}be@EFs`0inIf13r{1k9+@mA*rVn}jO-fHUEgvaNhph2 zTud!u1%cZ!5Aq4u0m$HdoW4Z_;R!_1`0Z8rM}7Dn-h^RSbP+_Hw=Z7AKPiZncAWsK z&^|aCW|n`N%6~%+gXDs4(IU`rJq@oQ-C=q(xPWdqqQMtoy2*Sm-k1@) zFH@nG<>LATVjGd2!Z#tC=nS&lkECgHxM(IuEQ=)EtuzTIfoUlLfqU*XJA-rv1^{c=|h%^ z(8mWm0HozY)=GUjeOXbY>92^>DO{%K>J!pIXfUKWE{QXWn&=^){0wAAqoR58eVWJN zBcB6L2Tb%y^UMlv&%!x8e^xrD>1dv2qyu`ii1{UK69i21pWzpNgl1>s3_7-j+X^$8 z{0H#jS#<05TNoGH)8G(su2GxOJ?M*$p@TtyaO^3_EdiqyXq32`oekYsanSj|(6}qX zLS4V6=^{KEd5Ap1fq1&EIP8(1BAHcYQ(F{%@q=Q;Sa$OQ9NVhk+ApxL7y}UG6eUlYpC(S z1eA{5r8cnVB3a^nPZEhoUa5}(#1Tgwddj1S`|IbTZWaI0cOWPbH;5$%D_TXtBDg(~ zPNmv9=!Yj`mg0aVz2LOF(bh#_hA55sDL8F}=(!l@UqdTt2(AYT<`9ShNJM`#^%f!w z;LsOhFeZ9}u^&~3E!7E`$f7W#;1=RjspY$H3t$bpW&<9&KTNXr&;`T^W!V;bWBulI zvXIWCxd&Wg_BOrD#MLV798*{V`F((t8hnyaf`Fr;5l}`OZ%CS&cNe@nl?u=(pIji0 z{V+Hh7*r&t5#sYvs@zf!MG;OVeljKF1IRXI=iQ>j&7uTxp~z0jJc*VKQ6erq;pJZe)s^*H zJ7wh^Lc53w;tNtoG#9|e4(?E5RW`PkMvcwt98!xGn+W0jv#~`e#1~)qYl@oeUmb3v zhD}nxGz_p>9Li?170ur{1X){N;am3Orb1liVoWkMWudF5cEQLw)=P7&=~w1x2>kh6 z!Ybi&0%F4CzyM90F#IU#JSd*BCQL3xQ11f_56wQzC%*TT*SI5}cg0G;c*6j;ENU4C zwa9ECeld&&D6U0KU*Jr6Xond8U()zH=mrQ@w#rrJ#FeYKGaunEhKS(XP#~|XqJ3vI z#dEBG8gz+TX?~S8v5eKYyi1$ft<7A{zkz)~HG?-ujaSkUHL;R*_xtGOeqz6quRk++ zAhlu7>Yi2_7?`OsAos{p|5ZJ$PI6qlh?D#zHUCT1bUy{ zvWQ{b3`;v>kc~84)e@E9Dx=Mx_n zcEr>sEGY_Fd@a)igd+li4G`uMgc`yM;gUFrG7RB}sgVI>FG!0q^)>xT%p-{AxGgWv zt07c+_}eCDk+Va5+hof25uY~s^psDVyt$D`9sjgRc{yz;u*=DgB0EdSMVMMMAo|xl z0y-h(#GO2jYBIO}g2kVVmC)3JmQ@w(91_`Bu~PR#tZ)&`gndAHRnup6@WShxpCvMH zCKC){l7?loZIG7a&^vR}^DXyWVa3OuV~Pyko8O zjx^Er$%VMS*KU^iC_0MHNnZ$e@_FI}=S0@n#8P6#1D_PZDTb4r{fJdgCGm`#<&}kO z`+{h4s9F*?v<|aRCdrs;c0JKhZ*=PK=#A<6J0v;2p&_B~1i^I*UN{PqB%HDhN(f#v zvD9smxl%1mm1?alo%F@m2ps@}hErZ7U6tS^i`g0E7)|(2&KZ}-?F<78(!e;3L8Ir> z-@%U@T$+v?y1;ih#|hiBC*;Tle$ht>Khudx>kk@yJ{R^2bsQ?H07;+}?{H7(VygFh zQP22dz>>)*lIRk~5$%8RHG}hE0MaasgCuR1Pe5xEks)<0kO@zTBl9ehBajhCNdBX% zPrp}#iG<5ucISYErV})N_31Qp+PE|&G>Z`X6PP$oC9*UGVpj|!HvVM9Lbp;qP1IYq z4QvQSbUih7s?Yi?%rPQ183f521#J>Z^PhL|OvDgT7yByYZZWL^1U|~0_)cVdmo}R|gyBy$SH&2I5qJ0+%JxZwp2Z!eE2!bPTWjQ}6{<(R zyAMh_Ue7iBF7!u2&JE<~4rJP26i#R%R7w^RvZ`khD_N{Ucb6966U4vSn8t;R}}a&cG$xoqug$i*XkZ>kV+rc*;^YrF6U zL@*YTiIbK1d5F+n|FdDVX*CO{f{I%(7)do5{yah-Ssr;fPl2l|?)TP890uBd{1hf! zNfvHrH4uJIJmIIRCRXm%2;e1dh$zlBBOiNBH?!;6d)V#1Nm z^jQRJ!oP(@{xtr>IIR9hPS?jp1ZyW&9skVYFT>Ln07C+8e;wPOxw}y*@ajmm(!upI){9?mOyq2@YkCHBaY_j-q90Q%g z9_JD9lfIv*w(mUf9PY=(2Z15ygfpYgv9NmCBc(-HMnCXVu(kNY+IaXjpj1?gF?S++ zF!t5UmWxvL@lq5CPv2pyt6nxFUR9p-N{P;{UUsv1b=^s?Y|$yz%lgHu^G{;b|ie%Em8}CTm3qH|FgP^A9x^!{bjJow6eJcmcUr}!0^>i_@>T) z@i!5@(md%^=vIpO>?jP3Bv>lsb%}i^FM}0kh=q*sGvKb{j70r^5d^H^X9zt=%$k8V z4R9rvx;%yUAT8)<>X#3A$UM zE3M~Y11D23ssktfzF9E|rCYO7v8eo};n+#9;h?)g1aUYxhYAE!6f~2c5kgLgKvHS%BAcWe+#f^Z$=0W@EHa-em`{v5f*Gn z<3{6w^P}W(u&=q7x=pL%3|zZmP^a5=q$&rM35%_S+4L(FILMq+*3Bd-+?WGuIHlb4!ZWbDx=fspx*1AjDnVo zhA3g7mSwD59ltJmDB-$jwIw6^F%|Lm83&F(2|Y`5WbnVl93kc?!(BQ4?}7Qs$!!co z<~Z;*UJn8?R$NF?US;X6S=hSsq!+ZLH++tAKo{#0LKtrH^I%t#cY|bUBQ8d1trVO& zhX5cT$mGmovcPH0&l;UXIs_9T(#Mb|Itew<`5XaLxTfhaJY~gZ-gX*KsJRj5vLV+o zhVUenWeN$Li=8kYy^n@+h+Dx*2}7BgIFy+QLn*$Yb_+lV-=CiCjaKBag9JMx^ z*UyFb3rEv_VoVJSJ^@gch1Dn#%pf+x?LCumBpbJM_4u&|IynkoNtSDeJ=h3lL^fb{ zWGFfkDB_vi(62WV1OgyNA-anpCd)G!r*d<;U4satq2a40rh~u`doVWM<1K(AC zLvP>Pl(tmOsG45rDwmsjA&&r!t}P_a#fHV#n(b;Bz%E>*$_8@&jx+ZcFnfiTadl3Q zHbYACY4UvhPq^Le(v|W8R1llThNXb%>mkXAJMEcnJQb)Fmt4BvEe zAQ}me{JL|fgR~AdZk4CC3+-)~F`!{MWo)2}wtfRtp!VBLSi^aTLK6Mx>@VuQg+uUp zCY1!Y%C0S?fYsQq7WJwYwJ29DS_I!i@K1%}%Vv8JF!un2y;M_o1N+DP)mtSjBfMWe z6Bwv#(6(fzL1BOS48mQv^rJZ>*&2eTt#-=eyCbaQm4y}nN`{gwj}s$Qnf7-qzWOQ%1&U#oJkdVt@f%7 zGxMSN%156_~5pFI;@TfYoQ=Imf=mg9HQ*1Wwq@!H!k zmK46yn2LM0Anp#aHH+|;j%~NmC!B(VC*Uf^M|>|qOHk{SR0PM1>}~5%gsmYG zuq{LaxRux%qDz4&L%yapf>_N%*z@dML*(mlA8Yvzo$Sj6W>EjE+>sM5b!}}(vTkML zI^`rEe+Xdp_0)=i4!(`z>bYl^*5bg9YuSy|&VjV8YUSZTO6!tADLMh{$!0rBUEKiE z&Fr|%q{BT(7_NB%1f)w)?Crlr%D%bg7!y3idM<1}fR51z8pGxInvphZ%_3ei%CJ`u z?1nGQ9ZPM1LZL5GHJ0Zxaz4;zO#hxR+=vNwFVvT|@AXK(EL z#2VIYh28IuwyqZ}R00}CrzBP<9I)f~JB8|8_Qq*zEdw#o8xV`G+G;^BK#m~3EY}%) z9L7D!8LT`ki7-~c($+n;2o-#sz9WSzVC?OW6Az0QG`)NrpOA)4M$HDYa9mYew`-15opHz%7Yn(CU2MV_m4oi;M!vaIHtP4S)jzV0QdPe{>Wg;c*F+3Rg7t zd69<1M^Fjwf$`+yl<<0*S6n4t8te?dhK5Q+J1+u9xXt0g=KxP^gGz8w(r&Dnp1jhm zvnf_bD>)GylpCyd)_J(*1o0;o+Pd6KOn&|g3?jW+$ab*aJ(1uCmhT4NNZR*7aBwVQ zK~|iZ(_3DlwLOpldEnz8kA@4=Jrw;;uzRQ+4*n+e7(9Wj3T-{d9OXK|;SpDt;S!Ue z;7UF6e}FJL2C=O+A8`%BUUt)0`otg&@fN(rmwRt8m}i)C488=>FLf)pQ;)_iXAsJw zS&{5(t6*|PQ`9ZQ_Tt)}WC)Vzl|+d(G`EN_##D3nt74K7p~dv-bRX*wb1QyZjBrUJ zlxHvp@vpNBW@OnK#)=O|@@rPz$5SC_1!Lm%f zE_opv>nf8Q=-$YG;zcyM(^7<()v_%6=C1Ddm9!nR)v~O>yKs$?m5UEv2}ii-1g;n@ z%Yy2tgm-+;7pyxc+|AF^eENm&o#5_MZL(KtTrW!l)Z%^py2TO4lhVX%IxGK-^JCbbOL6B?@S`S6fywt1Y>|vCf(@p#@Kcw9}$+G$% z26BTdE@jgYk1UWIC*6)V#nWjzb~> zzn=at5HQKuNkdeUi5sBQk{Bhi*Z}BXcov^o-2ZeOd{qwMPy_$LQ+3p-q;X_tW z>G-Nys{1hR!Zkw0a4jybsIcPN2ITQ_+&wE6vN4*Lo z$8-(>FCWKCav|U(02?sACU#Qv1(vwNiGbU}N3*f?1(ABxVb_qgfk`2W-lUt~TO!jh zzMgebNJSsf<67uW`3ASeCtuB=m+_ z>A2?|eDR}Mna+0X`vBADM)jwp>tQhMv~SMc_c1>EVST$W_;qH}z#MJ1jpGV#3L{As zU$%%|f$I>apg1`N$lQk__!#5pkhIxT_~aXKzv=;e_2}>V;62Io%|YQWuwi!)*1iyU zFzZDe${6edXwLJjW%!_!|2!X`0A++Vr|rB#gtqSM)8xYLzR^DZ5!9HPf_3WdLPP=W z>%&*n7M8->h;ZDL+6C~okoAvXmnv42n^Tnk5Ei)*|JFMT$CuAgb88XOi0Bs-F&1O^ zvR*#_%QR}q%%qGk*0v%>hQdv$WcyxxW2|jwM+e!MxDRl&+CluoZj{vyVyQaZ*X#(l zk}8A2sPn6Uq<|<^&()Br4EOD9?dECr=Pja$Z$<|(V-0GH0k~Em* zaoy1k#7@;6-AgkOu9}5w8%uL~a|TIL0=R_{pq#C$_4B&@{tqby2DolQ*`&00AZvh#a_z2txmUg}ZR=5BPGVjo(mCB`+L&f_( z6n4U>nqy=t45+ff7O)42^Di5=V!|%qjc@uHRt&2gA|3mIlGfY3M8UmqLpV-|8+K*; zx|N_XxC0IyC^|%Q!^A(9Xfj|U&z83{>>eb&PPZrHGjF&z5?_hLb{Z<+gsg~>MQ72q z%0X?)1_WrJQ!FLcEnzO;mORt&*|TI&9UDEL`egfmXnP;{sEcd=J0VM0U?HmnjhZTI z^wL5t71ThY4N5|^Ko!Y-mE7~I5WS+>$%+S*%fY1Nk7(w4UV*|w-j zLkU_gMx_;%Dr&0pHK_(g2$izW`!nC~ZUWlhbD!URJ-#IK-828r%$zxM=FFKhC%1_k zCG1>gA(WL5tZRaS+4-Ci8xCS^oDmyrlOzjv+z>!)I7V}EMG+K68Kb{}VuJC(Cn|e%@@rw=&U#S~`H_#0_ z-&my8`QW6BqsND@8lJfM{n*9$M2oh+Z@VObJ<%c^I;;fy9z=!+S?fdT!F9VD!!5@fV_3WQ9xe9$t5d0^_?H3wRVE{&el6C*#jP z`z8ns2Z2a|tmJF2L>W--C;r)2@pX93)MtBtszLt~PJ@ScI0enGWVbCDk$wD$XxH$= zPq)n(frI%DC&K>tiVNmy)>ha?mHWsQ{y&>eHp#C-=UueA+sOMBVOuI1}7sEEZB zpWZKBW*6R3cHwnyBo3!kcC9s?HY)tL#Ij0xlYY?s`Ip3J8r=B%T)-a_cioM=1}rtE@}i08*IHM>caTIHiq(_ue#Q{fT&6`&-ul}{8~iAPk1webB0MUdD0Co#(Q zO{MsgiQAtI<`sOoz~WbuYF!t~!mQs)z_)52)KNU{+ms*s+C~bCeQgteB?UI$FpjV= zYaQc_5P>OoWCABU6J5kjnJUClHNp^gShK2Qi~6jvm)AN4;q=_{!c=)72*tBYX8Kl) zF{gn@OM6~R5s_T;TJJAwLmO%PfCC!9fY9(yi0iZ;`T|HE865CwN@V4cV_$XcGdYEn%dmt z#}&*yw4B~aBem{Q7pZ$lAn+&G=T^i%nfGviJiFud0wEqWb)=z#R8GW8mc2e$7R$!f zX~%)!Xwtd~rwt~Q1(vPGCY;*7{?)-`V+NC5K(hGkiS$Km_C)6{aEs@ZT;*Gl$H2XQ ziHlmc(&UgN@8N^79AQO*Q%Ueg5_ow1)#g+&OxO+=DO-@z-8-1>p}}r4gKQ!aJ&xV=brZ9uOKf%4E`zzp{q3|0M?fW zqveG@jplvm9T;%uJf?Dz_`7f5jR z^!w{|2r|)p+lH0q>o#mKcPjk4X30|L&3CD1r1K<1l+3!ADgWAP4uy0Bh5fJH1r&+y zT_3zdcLL^x_t`rZdRu{_%F64o4(Ze#k{~$ON9Gtq4#Sr z*|AfUJlT_`rMLQxGJMAQtlGxAq5ea-Pd>Jr9%Km!bbE^E`iR(`Ik` zmwrwPfw{+OOgY%IN^ZkjbMULv7m=(CsQ^$|6 zw;5;8wznBYuUIbXB*2zIJ4=p-CvlaBTa5KlrUN%2ieWfS zJ0YUx_P)V0yRY`AA+#e`GYX zyT&Y~^4>ap52GPQJFN)%AVY3F=&L^1Ig2rQZjf=fpwV^Oy`Pi zDfaF={Xi-W2se1j1FXBVQGS;Eq3QCe&w%2;C8lL1N{5x)>T4>5BI0MnKUE(uJiET6 zu!i=cK&YrFsqwAZuT%O}zLLL$N0zZjt?7*J(q&b9(ffM!<0~uejAJrbx7j(F*|5jV zr_VJT?qk8O)lGy!ie9a(KRz+*E|R+wX^;7mm$l&oS!vJF_+`CxBC_JRii&zvz<3D{ znbXPA+TQfWq|4$@J$<~IcQra+(~b>-igTf~KJKqCLEzT0;}hXgWn~;Y>T+%l4I)WZ z{CP?)nR-}ciOQ##LRoE`fZ5?TR!_*5N?+H%$MLO+)@1DtaVuS&h4qsd(6k}^9hgh zDoXUYZ_OLfiAcm4(uqEn(y@%ENF>7ftBYk+wyC;QJHF*!u z$a{F-)c0BI%N_(BHq_H&7DTXM+&`Mb&QCIeP&(-xDKN5gn< zy&hvWs~*qvn67)r$6m=qtmklW%q>CjNnVLjKS8v;M|8&W4*2TE9%E7-sE?Ph{~2~w zeSFGQ^>EMAYs|+Pl4@AGDdIzp`d0*R8OOnUq}wz1amzZSvfdu#-Y;4Kf-COD=fJv~ zOG~m`);$3lis!D+Yx9@QZh0bdcnH@{ebW36F^p5>6EE6dQB!4ZlYExGWq*Np%l?%3 z*!2N_Tc9|7$Np`$Ew`u$?%4ZcSF{aYu}^N+bXxTlmu2g*X?H^D9hsh39dF(AnZkDJ z;5v2PvG`hF$@}4JN}BrC1QN65_$6eHKTa%b#HF*j$j}q2#>Oa^8n5o>)fz7Cc{g&p z5Parwp=c6S;glU)gqhl&cynIxUl3Ll0g zp_)W#)(NP}d#y#aYPN2UWsntJm2@sU5!;BcZK6W3#pGoOvTV8FzCGrGL1a&mMxYq$ zU*;rd16Qc_JjVExHf{y9JQF$0k~rvRAbCJ7?1Mi?`$~}>tIaZmk*HMf>^!;tQ*Ca9 zXKSys1-^r8JM($gB=L1QAFAd%j<*~?ZZ>%)&$|l3%&~GCCF#C?tiGF#!WbdfPQN6j1OL~Vj@(B2Ta|2Pm-(_{|<*APy zw0D2V6wx_o+~-IrFHiE)n9aW5e1W}#d{KRlQ1V@Et-AGQTIQ^0v=k*Do2keSQ*6Ve znWCh413I6#>WnUFN}cMH&r_v+pL4l2JRkGfw;bo>-IN zTDcjAZ406kHd37bX)Ce5FuSdeqyC_oNxyNdkl+0N<%o}*79kz20O3?_@1N7N>8td$ z2($(Jr4OA=>tQ6N6T9i<#7z7vD2vVwz9=o<7=% zu?Wxl`8W&3aS|fWZb8fa=y^R$?8dyb%x%Wl9N=cXCEf1QF=yqpG;>%UA1gaW79L3~ zr%q+S$2GD!sj6-)z@Ne&QGuLrKj$1R$uf;yNZHY@0QdWJZy?*u;IT>od2%#Qr)vtE7L$#7X_@j!Vn6at1RY zzO%!O`gqnvnQ6>nTI*PZ6YU4s>O2 z($2quOSrY2$g*s(I6It=GVGeh>;+hTyFVr&UM*lB><1#@Eq=3a`py2x1<~X3KeKbt z|IBfJ-VfRnzE)UjR!)Dlfwgd!nO*}p(_cGPmbZDo?1HQggB*sqzPw+`oxrMq=Uz;d z{~U4A9r>}P+}rm&i^XRK;~33d?VmKK$j@QY{P?cCUm}v{{gSH$IZx*NVTe5*!=gRQ zLfVL!z%`LjS;#;=NWEye)d1Jdj~RqwpEJZyYAeP81FvkX%}e+`8PaYXjZEsJx3Ev5 zIK%Rj<)A9aYS_xy{dRMlZi{e*sk7l5$uce|p(Uy9OkHB(XMlZe=a4K(W!a=GSK2H) zl;xLR77KsNwRf`I0P*g*k^38y&bv7JRzFWL}7XAyHFIl8n^=Q4=C|hN2i<(cs zvR!Tp6BQrjmbqCbKW8$?ImT|S)6~aY=p7l7|NXtc*4>Vm*6d6tdhl)fjdI7S+|MyThZqODZ>y5h zawPW8m8;Lflsx~+`uN55(c??PWBB`t@QBK5u7O3pStjL>`G!@`iT@1RB50fb=EJlG zVQXys^aO5{KIpb6Tf243b&AzL_ugQa8N17MZBO5!1lXc0HuRVu@GHG+JZGbN%-ln^ z%T6#obuZ;&fn(-{XGP8M@YIiM1rJ|P86IC*8P=^?Ox=Q5w4)UWP41mE_#@bRwhY~M zI8Uq&Uf}l(Lpt%q^xca1De|Tf$a|!}!|+%(cJ@{vw;HZI#gJc$sZe3>u#4Mu%)>HP z89uWz9E_R~7KibwiARzY6!i{QFkxFZov8KfG08YQSSV&I-9>(5Z%u0aj+xxG)yFPA zSWnI-MI;9w7OQE~{ zX5jBAOw0FcWpL%V2%{&8lw@Zr84n2~Uq@Fj;p9$2D7UsYQ)?()XFGL5uUK(hMb7GlZDt5R7C6g{B*N~hX!d^Gr1G31I$lRcWQhHf%|eF zvA3K?kDuyB$$#b9z@QF>*OGp6wYqZO=jBtO2*#?z)&Pc0x$KVWV+C%{hA&ODb|dlF z>fG`d4wlbGfbtcu@^7V&DE|h?^}3U~{0H;|GoCKDGw@xCvb(m0j;Rhe(oCOcA# ziQATKl~S8D7IUr>Sr=r;I(#X+bbl4kjz|1eUU}CJme=T&mwjS+y$eq2{PRG1Nat6P zeEmKEuJ}HMQ3EUY>Sb}SP}^^Q;_=)c5YpM=R)c2`F4Gm0%goSI)i3T0b*0M3MV<(j zw+9+>`9k`-M_f0z28F`)G=Y=i0n>mbsP6NuOIdQY)|d7^k0&juilHRpv4aO({w;Pu zr?WyeUoIwqqhL2dYW#9)5LF%7*ZVOZ9Xdou@p6lu!htx~tH$3%0{IQ@H;?MUyu-Ci zG22mr$t@a)&SfWa_7`|~=wx)H=%QrvDEMecu!5rKgcDf%$ta#ehSd1ggZN#T!Ec?0 zktq?*;CAj%+|yWOBRxG^7U*glz@QnIygEK|^i_Ot2MLd7ZYe-)A~td5o^r z-z4{|;ZH7A!>_MGxYuL6<~2i;c(;OqK>av7z6j&aYmV1Pi#S zZC`sq;$}TVd3N+@PGoF-^tNF$A|rm2?MMrVnn@-8ZI2~JIBv*v{*uyVeWzD8jrk<_ zu`zCaNCRb6RhK`)XZTt$;1*kpJ)bK#iEt$tTpgKEq<9A~5= z?d-)yo9`-_6W2s3-`HV%Te2ggOpAaQzu0@LEoqg0!M$h?EE-`>yS93&CG8D$tk z526nWTukyRDth0VbTET%Qx5IV;JHhvO`$xWxxn2auitn0#WP=24aYmpsnpT(Vr^u7 zNu+Ph55*1lo8wGaMx}OvImFio_~IbZh~hmy6oia8%xcNd$Op*i97{&#`|Olq(^D-; zwPlhIA*uf+1-r%X@Yf}&FS}T{Bo&+nN!_wUBy|%f4N0>MGb6T%mUS$F16A`zDn}fA zHGa%;Bionv0&2ctcNRmYVahRn<^hf6d$<{+3VP3WUn4XUPj|;|V0QOF%jRx|2!tz% zP0ZaDGLx`>VFY<-)E)2cH#JIgcbQ(Qi05J%;j#ic2ARs3ZN&2mv)Muj!@e|~@9uQI z-;l5On(vsA16SG`Ot30n`tN7!4#FVm+6bgr|8Y{|*$jeq}~%i^(NP%p@p@RSb6 z>R}IeWQW29^hL;ALqCOVUnDIZnBH-AX}lOX-96C)_cMc2PuHvpjYWyalG+S1z3&tM=u!d`)FG*`}>r3$k2hEYK~llJE!;mtGY)YO!_xNxDhp z{hY46?P-nDDx(8*pg3ZAJ6j!IB4ba_9lW?sKx*c@1NDF%lXiE)HZ!zL*z1_jOxSQx ztHVNm)Us3lF14gCqbRH3y%*&fl)q1f3^OC7poz5e?K4G6&l};Sl_t2#O}JYLhntsB za_~?%-y_WXa2sBktc{J(lW;1F!b4^;49yjz{N_4NcqrF&lT;qmciC{CX}4jINfJhP z1U=V)gA^qFjC4dFT+R20Rb7f;e|MTHzK00;uNL!vRZ3gBl+Ec<7X81J60CtbELCI# zi>quQc5$@UO{wwsr%M|_X}bC*MU$>bp!LuinxFn>{)<+`2Owr`f%0W@zKqSm&fIJ- zSF%M!`(TZT7R?O@#8!nrWHa$cx{f(3$<~agG}zA!#1P$3Mxkd+3osbJ?>E`K=h?Zv z|FBKOBTR#dmf1vk-|AG3Nw24;@({(hRX{Xffi=Rm?4_MpNSX7|&>hCor!nhzdt2_} zyiM&GdEYH`hg)=mc8*Pb5$Z7)E@0EZ2RMPKxzNS4LVQfC0Ql9R7i@$(fd1)H)+*O} zk_n;z{2K`MK0=Dy(CffYXANDQpH|XkUed3S^x}5Q{|2cD*I^_n^^(jX$@`aMzMlS{~enMl62Tvy9-Fw+ByHs52qoZJN(Bh=#=h2jPDrW`KB zv!^~&91M@Rvbi+KsZetfm#OFbta-`@kKNCQPCRi|n^A_!E?JVTPB@a^s_y>wB2h;^ zrDRU!QSWj8Dt{`9jm(++%kG&xCkVJZhG=>hipd;^HSxdrjQJ`Zz##GSm{C7+9r;MS z>A>1k(b&gF=ImM@ID=ECvXY%^-lB}=qRUt}4{$QeVJmH{G4C?TC3}MSNyj_wLi26z zCu2Ea+u~{~enmDm;A)x%=oU5FJ2LvVjY5M#nJ0_1u=2es@cSPq@Y)j#EY=F*6S zIPXIXOe^%Hg^4Q{ik7^blMZt-+81opB!h)7jL)0~>7En6dP?*3iz26U_YOnRTeB~U z@Z9*YFv@lrD0N=kj;0&u3o_-glLxBbVyWIbMv(2yJfFP=wXUy>^+rVbCweB)+73^b zgr6F}mGs=ujOE%aZTPU!eKDsa@tG-D-X#_6 zf7nLH+bce+(aTCC?xAC(9#yv>n8k9McCIx(W}Q;%g* zR(l)IMPk_QuWAG8*bw)XM|MYcTBpnmD0 z`hu6!OX6Vt(z;t#ueX%AnJ!o9<}!65-2OwNezVz)YQB{VRKuMoAV$^~?ozUO$O1v+ zbaDZb7pUimWGNhnLf>yTyl+>%p5xXj2>XJ8u54e$%q%M~YTmK+7 zLA3RDqYV{~2=f(+2h+aP_@p;2EBwq9mNKF zGmd+v{uu7x#kjp|A>376MEBX7n2U}ja5Y~(fF-EP-r@BFPeYwpIEnMQ1cJwIE^Ag& z*c13dkaS_CM(u}#=7%&DIZ@x~e;kZtHS;6i+z7RhrJ)%}V-5}~mE&pUaJ|I*aKark zvvAO(Tgmd`!_HEa>3XGw;fL7s!;frS$rR1?xpbs0E1J!c}f`jxffQO3MAHUdxY_;6h2nrkmEkqtQc;fNt?&8FzZ)5)SHCA_dGV*@<^({krPzv^^dt zBGJ{pVQcd_(Lx0l3Died4_gvR>n)*$aZ2PGw%qML z+4V(_E(w>)tnV_+`reA4=34tjkIoo0_IqoHa&1j8u{wJ?N1o&?y=_S(dp65Tjcj>0 z{>5FhWy`xt!xM2A;#ZjdQcBrG-yBNMY`9q|H$M$6Y>^^9*#>KL?p6xY?0@*>~*u3WAn zjbCj~n6TT8oXTx`e>ebq6du_8y7KArV{)N59338Ijxke+ADxA2 za}$lzQJDXv`0>O);>M59#AAJ^_j};|cDqJhWhC3^CM&ba;`W)3GVF_OI$zd&Lo+xd z$BZNIk?iQexZBT<4g{M%js&|VFFG({Ek8%DMW7wM_Kf+cooFR=#_Psw4n_w~S43Il zjr!=hOzPg(X`r3Zzo4VB{~X^K)S+L{yumQm%P?aHmsGv2l)1wuLQkT8qIql|H*3sy za@NI|S5<$Q9oS#?*%jq6tOv62sKVbwMXIE{Z_TJO#yw;nCLZN6JIz4)JS+*y@ja7v zMUQ5$-Hp8$+~5$fcTT}eDDK!F0G$_hSd=j&Ws$DQQZ(+M|{`f+ItQy zmCnUH4!cP^r55|yXLmgN%n~N2*O)=!g|>97EuWp8v8vbb*d-%K2bBll&LMq>O(PBE zr^fGt+p%`!%91Y7uxaVcEUF7kX5leYDMmZLA{x-=%^u2&=pO|jC-Hgdo((0ImN9%I%K)_R90R6j&CjCKT^rv_3f zBp{Zx5bbBhPE@jP2h48ZTz-S^4UaG4T^o3`Y@>2raX?;*$Q5nM&si}dnVpr>+*>Rq zq%9)f79qLEymLgQ$fMb4 zNmzq>z>GQizrk9~S1G0cL#)*UeXLym>cn!?jsqcAI)c~#!>W(CG&8&`tGO3d6PC;> z`F&8QZD(L($XP66lw8-G;WgM*#M)c%??0ZpeYou{itc@Ge-GVSZ|wO=&sqYtmd`c%F(jCP+oX*Cw0_(Hya0d6@f(4 z050eXPN~LLfbFJS11<}n!Ua(ztcF=PFagZ;Sx`Q#8{sZDr=t~od}zX^KG9ZDXeH3~ z9ZR3kZou3H9izJP1ybmO##(16cAH>n0}l${uK-EQz;)doue8`lcy<}q6aLg^YNl7? zBhP`h|D*H#%c4sISxZ}=2@i{nvQLT(Xexf` zDtEHiB-Z+BFrP_{KOpG{9RW7tuC&OdB#tVrhrkF&g#~erAo3CWxp~GNL!LNHjo+_4 zn(IT>&U_w*abs+!O4{a_1uC%09gqe^x$9mPG&j=^NWtKlJueuo#x;bCrX=DI4n3C@ z82KPi1nse=h>U#D1kjE!^0_Au{H10bG4L@Bd%iP=eFUDSTN*kErsNx|p3py$4D{fTsyi1uR zm1WkH)AhA}l*3~0#@LHf4_{TXN6%x8t*)-BGTXI^;1>&f0V$n$9czSnGLxK?bv9*n zmHFleQqK079}G!~NDKy9;Q;SwRhcg+Iqs{uoG)9w%$lu{_dMUD&|^(}4!)zndc3SJ zvFsbpMnb4Db+)3lJ`Xt30xKCFOcFMQ-8+DTai6~ZVwv(x(v)0QJ_|Pn{ zEHyJ$99tTcapcm1*wVrwjkba;3uI~@`b!^%d-MEVxvJg#f*z|jw^N7blGmQ`oRCgc z8d-VVPABBNlx(|0>YON6L46pn8_QhmNQp-0>l!OPU0;+*s5_~4@4?iX=7^< z-thyYG)3aEc97ktg>re6B1^7sU^|#I+7*a3*?U8b%Dm-xG#7f@s#T|5U}uU{Y$?{| zAZrP`?3(!dalG2G!sVY)(!ENSqAIzDMivaZkeyXDk%p$55{!3E>Rcv~ZRrJCzdz>+ zN-`&I%7?o2%n~r5Ki-mpwNy>3Oj&IDxPA#mGM4$?9I5NSLe?*~Uuk=HWx{ zj(swrqp}|6Fe_8Q_)&kRw}!tZN?XHEP#R;QV~nBNVOKPCl@(tkB|9Mai|fj?twlJ| zsj^4(!F`fTgh-Z)$kZq5x%#N#oqMdfa!Gh}v?<_=gj36}?Y&WEqmD1UdV5pL3k8-PSnoxt<=XV5mY140 z^!sd_SM!_0iK6U~xl5qYspWH&ehxoajrF;h64NJaDZn&!YI{a>b`L7GwO%coe7%m6 znj(QDHO1_D(ylDB$zPqe$)_W*$xp6VlgHq(1g*9#Fq?13;)FZIWsbIJ@w=MoBl-v8 z%xis1-{lAt=z1sMHbAf4n2_m)2U_jxErmoGb2jHN2uyyipgX@*mR#5JAj^aVAZfXS(C5gdD!6)A_ln z$SYkz;icz>aIibYT(A$CnSG2K?jby@n1NFDV?g$rl{-XD`xBqJNk`&2Cef*={ibi1 zSkhrN;T3(dDWZ#)4e8UN z!_Dv(KUsWqi0suF@8~M?wE&G?>(6SwHkj2_7FeUSS=Z}tnQtLMXIHlUHPil*Q2bj= z3Xt*}gAMy}psxAwZ4k2a9iak25|YjC{V4Ib7K_p_cj|4D6qTwh<> zN4ZYb(`LV^GCxbR<_ijj)YX2NU}U6y3pwMv<)B$c&t;yY*xKcG8hIo)=hHKpxQ&Bx z&H%^mA4u?(!34>j>g8LD2YdO%KAcySSX)rzt-U4}g(VsbIVfz*j&>{$#ZSR1I9@SP z1O^NA97LxsGXbj5{Hf(0M^~=x)e76IrXGwm)EUmvncOEF-eYtTmn>bmC!g2EL@QP} z)|9+ZwCf#Ie!@oem!+U+_Fsi6Zdq1jKo%-LYa@ru$F1@B z3Aei%RX;|Na)a!Lo@x=KFjy z{9{1S)mEm51xEuaE>FdIcYr$J4Ih37CO?ShG&DR6)MDHvrdA+K5bDBFH;dyR9tKUr(1fo%<)M7q|4svI5wetPi=F`f8 z5G;o2zmqKFwadr6fH@n?*5fH^?x8l^_Rl`It%9K*oQWWp+@ON~-9zACzsI7{Hyy|U zNEsk>ic57vRa?;AqWDdo?>;c9@bfx>d4nt`y)W__Oy$I9LXumAM9)+)ghI?UJk9o} zeG&x|^Ru}$TJndrUz*fW8Rr2TCin7-BFpec$c1vNp{#xkyr!t(IF~54D^5>*-w|vy z)qj|LeVMEGGWq%#Izc=OJY^0kMo(6=K#Z5>LR2Jzp-T%qDKJAR)=YdF?X6G03l zU3ySF^stnJAJM}_4f0!NCh`-(si&li_*|Tc?K0t6j1Ie4+S+dm&7UST`%Aj=TGoS) zbPE+hwVlVeGgy_%nT3qvF6_Gc|~@mUK1g@rQW@dQ-1b>Ktu4poH1S zL_-Byy!__dI=91waX6JzN3HG2oOFBBDBj$qRQt@!%)TKgj#Q1fe2LzmF(`V^r8cAxiQf4qir$_?AnU^Sf6F?%G{7O;Fv^58@@k`*&zyiU zp1>8YtBq-zS)cucW)@C=8VgJST=DbZO4ILwl$EAi55sQLk~I8Rya$c%W#+ra%F?4; zSsHD?WH?j}^Tv)&fkt=mOuyP(OeeC#iVv7S!DVXI`XQ1x^ByzF%u<3CLYvV0{Y63# z|2j2X4N66dphe1Ld*HfrnVNzOTMeDcTZ!yAammzBinPjr!w*uYlyotf@>(CUgNt{2 zn-Ff5SQJ=Lqv6a(8>b@H*_eK8a}$gD7g&jyGDt8ibqc%Hh0~k*xdlK42K^L%oAP;S z=H>8ZE{$amk~;-RS{MrOtEEE#oxLT|CX>Cg;{PHu)Oq5Zn2;rQ2cr|O!;H_&TPsmZ zVpmS629|IoMw&R)I1(GHCzzI4(pMk5+p@CmYIE#ip}nIq2PWj2 z*$;3()wO;{A!6p)Vs=R3+iBvK>2<4(=61eoEq_A_-EaPi@oew;IyjPd0CO!ev{&K? z^H=K7UH>`Cf?;GlQiOHT{D#RQnA~cL^Xq|_KE?`{Ui&T62yi)OzjaH7$qRRS5j+}? zst@~A`{1ama}U87{s*1wPJ3(~F5>d?q~nO(_lwe>o+L^yfY71j*_=ar3^mW5MZ1et zM>t^byKoN8-1P37G^C{iw$Q~iQS&p})Rz?*&JMKqbNB=TFGm2H0Wka}vrInS~>W6mV3l}cpD4E1F#FvEcNzL87{ z|5Ggf)_T^vyVydX5zlT1%tAtiL>=Fhe}<}3nyRC5yF&mxAJjXgFl zh0=TMv%U)L@vIM4uN9z_n0I|+p1L8E;2*bv_}oK@ishNN)4R%oc4v_#M`T^O`ze>L z`7RsylLSL$PbvG@M$hrK(VqZCcbkn`-#(yLNUW$(oA6UIvD#!OLhQ3cL*6_S^Eq*o z?WsLENC@aC&h8~~CX!^x+@p3+QN;n|lAUFXvrQFigLTZD95bCBTl`Ua%yt=_VkK%l z@!i)oLQS&-kz;;?)L=i^%ki07b-l@@g48M;#AwEItaF%#JLMA?vpZD;_Q{?oJ7~CEdn-BX zupaHh`}qNUpj*yuMzeE80!@_f1C4WGi{ha;077VH@r>zp&u}^J7A|iMoP|5rdp2-swow1=bel zMEUlRc?-FZXUx=qktX+7s&l*0G}loGio9AmDOiI|j9O`PY#+=q%Iw`XnBx=Vm|ZLV z-hZc?{#1XuM<|n*y!n}m>@veGqI=Ch?HoT~-XnCeMLX7S4u4cscG4^@v?eIhl_n}1 z<|iui`RC`Ko*oJp6`I|2zI*5?S9Bb!iykpx9~SSP^aqa4SoT-ZJSK)!_-+t&-Hq}j z^9G+b1IlBvk!6Gp6XJDuQIWMP_SRfkh|Zgs ziq4zNKP2|?o6(nZl6!GuPOM@Iv0Os|l3G>F`JsCNfFlZBbh!SdR7!T}(sPx$)<0le z?Vbk99;v#mg6ArA&MQ+L*G;{5Xi`<9*FB>1ir|w4s3m!Q%yhw^tMsO_ImlP2F{>dL z=9pT=vBN!J0GoEz`0nil9i&c-iEP_siUq@YzN2WfSzE(d>yhHj2-l%w zex5}ObJGkPhbr?b97`fC_74ZlS$xNFIWoGue_13KFOE(9SJgCLZ%vU7m|tHYai+g& z0dG!$4QyiRwD9$b)+OW$#XG+XyqLS;E&zR6$T&You4-Xp&M&P^!}^G>aCPMpaLfR7 zZ7*wPYf$gnFI@JUFH;JuYHaB=^94R@U56D6cSg3hw(gnXUU~)tuc6j6Bl1D!kDi+q zy$*dXwr^J3a62zrjmz8mhwP_KG&?&w-^YgbmbX{n+wc}Aa~8W0tL2eq&H0?Mx;KuS z{P>4F7`~8uZ`q7c^ndNVj3DrV}KlYy7GBKJyV0 zMBg8=>~ppnG!yRSC-ZI9A#U=4DR9Q%6iDt68C`UqL^L)auq9WG8(qnih21plv*WbS zOxvp753;F5tqZ&47)I9(*a=$Y=F@Om>>+#5wS-IsnRVo}9UzU9e3cm^6d(IkdRz3Rrv4kCS<7duwX#SyhT$_1_8%ca zfuuAsy{A!xj%))dv}}9WI!&;3YoePx-6wjSbdKmT zP|X2S4YHOo>z313oYAFVQF7yTq0W>s=EMH>JY;ztg1z`S(-iw$V{YT<)cW&!LGT?g zxDyrWQqq2L2G#69o6D^tFqavT`%1XEoZ@KlDBDg)Ooazd<>aRmHI~)z#Fkdp$Lcnq zk4-GvkkL%a&9P<7f@pW1?TxbGa4% z1&xTD6qPO}@>}xswbk+HW8FR2XSP+8+B)x9&}gr9s?MBr;_xYnx%UVOa|(4j>~>OA zbI<4qSC;GU&-)h6JiYjX&w`UW58QR)K3HL1D@wu?{ycl=$9znFdEb}y7aU_1(^wUG z1g>JSJ}VgL?pQ^!xobiy^|-bWv8Ha$eyCq}%<0#2C6m_e!M2*j&knRY=)s*_dy>O#)fTjK{F1ZE>Kh%wMT(Yu04`N-c|t8E;@z%z}q{3LC7$W^tb)BA1V& z&7*&|2;G#rq;C+RuQ2ye>!<}o&MsXH=Xe;-@-QqNiXoG?+w71eVqG5$Gh6JpUCYeV zPeVTkVLW<*VqRsHdGjNxCa7v+1>E#4PbTISh0MwaGTrg=f4SZ9oDH{{rwB9MF-Dcu z45_SSNM+Yk89tskER7jhv0}Oo;;tX`dtyeD_diO$*vQ55;Q2e z`PwP+)h-!kXW5i&E2qPmc*YeWh}}+jx?Z*(%BRF%E4fy%^G>Zg*}bQvF6kc}zHMxe zUHjTgr!TGKaq4-q5*5=zRA~w1aF=Q;FxkJPD5hD$&+qxNQp^pREog6{?}F4NAsWf9(wq-6_MpZ&1Gbb% zRFc}P%thx~w@b24s{oKdHm*hWl7#i)o1eO5p_l(PCMVIudgb<9O1im?n!Zx=u?0`j z9?7l$1aInHAkh|mQRB#vIeM7F+-&_qSwY9 z^TlB3+{nCcFtpu#Lg{s&=`Z;cxpKoJ%pYlvckr=G$i&Kbm;%aZ7>HV_+tmLE=5w#d z3P!k0A(HDhShr9ML8VH|ApiDruNv-l?-7fpiZicS_LEY;tIJ37!6*u~fqJYe z)bN{;Gqb9yNb9#&YFgP7<%JEqud$Z(h20n6qxSjJ;S#J@LvH*xHmLu)8_V4d|BX*s zC8wdBQD)$Ws$YzCr1=8%BW)Us*e+0@|Kglnd|Gf}2r}jiz&H;#M6;ra_4LcUf|K-|r&?%{g$~PTKabsjMa9}m1Y;ZF%S60J zL}Fe8gf!f6isY?@Hc@US(PJb6*RI^fYV?o;`Of5>Liz>Fdt`#av6scw*Oj}Qlwb_r zCnCL)qBt1k0pG@KD$S0qonkKIBs(#$jGx2YEugBp=j5ukW#fGT&w&gPo(0)@W~XlU zww|5XP-ebeF*qQKYwxO<01aiK32Cc11IsUDV&q{K0Bg*TCDfo%()+N9%+hd-$~!yJ z{P?mdt}9o+!y~HAUj`EaPgImK5&WW$ied&hFEcH|zG!_hTjPMaUpd?Y+)94{9c5HX zcMg`99ERZkp=;TM4`0i!8IreGqp)yXhVI;vO3b^-J5V(@($v($R?In7`51CD158q@ z=9p{%EJn4~3@>vAs%7X6@u5`(=4Bh=|5R*bl|2j7syt|NH)I7@&x%R7UJ~e$HYSIT ze7MVtb*aqylaot9_kmMSAet-KdqC@K&rTxfnO_ifnSsRMY>2o*O)labGFeIXu{cuk)S5ixjx9 z-)_Y-okKgcCtufB$eLFleV_*k8jm!wx&6PrgDCt4KKeQ!>tylWpCwuq&j2()pqU?) z;*SHh_0jE3uM)i^oX2i{bbVJ#lTt6qn#~JyKIY6vn+C+Txb2$Qwi`s`v`bXBG)^-K6*pnVC{22CCV`ICUe1x``B{me4)Bw zKoyKvq+H!bhAr)^Y3KpQ?sf`5tKUfVj%rBC8)x^_)NCSVDK)3fQUtH-D;=eC3BCQx zR#2M*^4$Ep6upvobmviq;>r5hy*v1L_h@UvX;k#ly#R-C!{|ryIG7rrMI_=uLPE;2n3Yv(V)usl6}xvKf6e9Bf&y}Otd9Tp z#_IN&NiJk#td9RT#p>=hXSk4-SRMbjv4b`vfTYIvQMjx^Ox<@OU0ln%A`tRfKfd$6 zLlVwI|9LM=Ay&7AO18x6w#MqVnJ1Ph`4-P1smb5UPbB>-zUPMT|0k3-`D>D~4zPzc zsk`arehO{Gfj#N2Bp#xfVitejR^dJ;)>fV}-=Hq(T4RL8b;y6=X_;(0%SUIM%RtnY z(NCGeZke!Cp&b;eaIGpYvX!^*EZ>jg^|#Gh7lGErDkU+E2;=i=o~WAL*3U|p^;wnm zkYWal+o<9mvRPZxCAQV2!oHp5sqEB4YJj=PR@rW@PbY}XYdCbL?QOG(L36trMKXv# zNBy>$f47;2Abz0*95836O9_uw5AQ60n=ad5lSr&4XY5}0?;3sAHJj<6oM;s+)F2+z zAbymJY%wzE(XPVo;v05=O_dql%}(XTtDIdU=-KT;Fp*DA)BkNtu@A=T_&*t|+iq^H zlN_!}@>>2=vn+AF5}p~*gwrR4X)86~f?}xWe7By>wx0Wt^}HN8W=h?l)I}$!*3fZj&`a$S zrBYw3)Q7)itMmr=vs4TMgMCt*Yu6OS z?K!2}y>Z_*>jVEOvQ58t32DPs>aBT=MJ}+dhoX=#B!f?LWr2E0ORi&1LjJaoyXIkEbpPFatd3kA7o` z^CFixw}EJtdx;f_ybn=h>S0<~(P(F8jD1=oc=eu{X!TQMSNU!TUw5MWIdk8_%sE^U zN+ilu+dn^xTu6MFQw>h=cmLD;``z>-AYM08!#=B@@aKDC_ZF$F)(QL#T#ZR|-CHOS zV|ANjb@!Wb6q6{whbrD;E+`2*KE6~3z%3#3v$v(H3g4gx)MDy6ytwYIFiz$A8#&H6 z&~Cf)9uxTwPGIAq$2ltq-MuoS>i7_&FpZ% zw>?(J|5DSMmuZ5$3WA5{a+4D<9~hegdX9sT2D}R~eF5?A~pB#qNDje_Q3b zGmPy`cVcc!$ZUe;B+6SThDmUiEoMjTUO5=kjjq^3JNP;u_mcM>u$i~>H({$lPHRTM zi9Wgu(1hDBbgB0yOTCroztp^=-dsFLSZfu!K;sXoDDzh@?$4RH>2=9SRl{OC8V{Ns zOhdd(y6wAz4F&xyy9EeWuJw+$YvI3}%$D_eE67A!{S!~PD@X-xmr1E5RYo9W-mFof zd~E(517B#XYB%5BXrXW(w;NvWanYaQp?I-StZm4*$q$$tmE0@S!|&%d#{uIfC4*2K zKCN+r7}@3pDw&>sFl0WqS>F$KwJxUlv+Sg8&8H&U!q~k_fX42<*8aWT{%zqe;-ucB ziQT(_zvd$pL2{=0&32_YV7>z{s7q*Rz^An>oeIH0P2g^-vjq%xNkL z8y25_glQGtKTWoc&ll)0v%a=A_0Vela4ZYhVqURfi+SFL8_m-;>^4s*d`L2@d5lkZ zXuQ*&99oaD_I06ozP8+qSFKm5M_cdYceL;NG1z%p5^Xfh4Zoluj=)z^)Crz%JCj?L zewEU5Gh^zDRSnHx-k4L>nBBlfGkPP`o^5sS;;5eFMPVvxARkmL0BCbELV9b?OhUS9uuix88wd1lGMygSxWGOxA;3+mS$9i1H+ zG5l@-y#f6i7P)b_j?FNuDLJ^}b2)#AbNkB2UCR(khB=8+f60**<&RC10NS+gfd?L_ zf8hQ^WM9sc)uH)Dyo6L2M%%}tZVZ_V=}s)B?Lo77;s^9G3bQVPizUlQ+_y#K+#$@I4=pZiWDV#|CJ3MRn$%DZ*FO*%hqYmS*W9@M&;*D@M5pi`?$ zHfR|BLjCg5`xjjZ9L<&49`^wT4$ktvwwJc97_+Vk)o?H??`wbLXHxH`qQ{-IbABk% za!EI99qhf^$aY7i|C}^iQ*c z_imnaB)iEZS}s>zxD#(x7+utd`qAD-rgx}j>Wj|#jL`IDc%SI(&BeFru#4@L5H(@X zuQZZ}mdV5;quPYtjJkUT#{#w9kj7tqsQ3=Hswq@HSRjpMuu&axnM{-T9&SuxvW_ z9fnV`{(JYZPU92v^H$ZlOmirMRJ^14S|3MeIzZ!EBx>WdUy+QxW&u)9XR<)m;$lC| zM!IqZx6EW0W8>*rf6nI#<X zJNOd}(^Pg1RE~?A_ z27iKHT>3|*21EELO)^P$O}jIMPJBL<GyjC>qdNkfN@9JjN(ibwvahl;jk*nm6g?Fjy^FsAP6$!!9(jd2s*b#3d(AHt zbJTzSBd_EuR(DKS|NUxNbc|Y-ijJ{uON|czjE~s`EXL_c z(Ei$CfBi6DKeF!I%`bC49WebP%UK8_frhVB85V*>*P8dB(1wZ4pismgbAX>eY*+sB zO&GtT(%H@rcbg!YbTeFN0vQ`7C`*>Ih*3s&6#cMB)lG>-HZ)&xUSviEAM0~xbcTJU z=SAKT$TtKuVbwb*s1oJ(Q{1t#nOTR+yR*ZmU~jOucWm<&6ISh0+TQmXM=j7U1u?l(K%eP3aZGd`uT-x;GYA(6_tNKx+R zTmJySTL1ETrgCRuuHX6FaitGBeH6ek!M`&E9&o-Xcu4TuGjQj>jsgq|*l2;&>D)I~oSeGzox9VY6P*N~$t_A*MoJs+ zcYb^LxcfQL`9=D((z!4F+2EW*kKI3KmY z^PO`&c$J0kc0TQeR}m(+&IOml176$ICzA#47WicgcE{i6Jk2=U?hZRoF=e>m=wy>r zVXsIaFpn%-$)a|?5ulJMg8Zi~MEw!_n(d>7g0^_+Qg@MVwIC~OI$J@&`LeBmp)Yd2 zz-RJ)r3krsx&rHm=7>olD$m#pxNu-MbTs#hR!+BURND zeLinLXX$f~KFPet4(L73WA=BO^RP`4R+67t&^G5jFX{Je)MoL8VZ)Srhw~`A`$S^0 zQeTCNKm|s;)L-(#8witugxGNGFyw`J7qjJ+d10{^PVvHt zgvqi+bl!{adbF^^sr-~O^*MPKnQhLQ&|qTjHs}2jZuqVj_IlxKgvm`R zuskp+Xfsgp#hYE&e!*_?V0A7ms~VV|WKp`SU09J|vw&$1-L5(+1-Tf=?%hgvuAA;A zK?}Wf&q8hsoa~D?$A4Kd7ltuhaj&4ao4-E1T%u|$-ri>2NtLS_PY#h zs$f$D`*8+#j$jJ~`(_6AjrY8T#OK_Z{tP+~vBX)likzROKZ~6oq(A34oA^v_R#CHU zQI^jxukdo+p4DM~#IP2>_>{oJ$;=gX= zRerKOgZGo~48?nW)lj@QQ#8pvyuWFG^*QUk@D9S{HWhq{3YJ`ZLnWZS0_piajbESE z%WF`Y+s`G=!?psT%bi)akNVbiw5T7Zxuve_kYdXD&NZr;xO?;~mj1O_zec4~KB!+` zO2^rCav8sp#kC;t!+sF(>VNX>O#PP**1yC3^y+^Q@y6C@>pwkRs;z&d{nhVmv}yXC zHp1j4)l#Bb;JK`k4{ZaO6!1a|Oe7|XjZ7LoZ%3S=JY=qgk*6Km-r--)QaYXyA7X0i16b`HQ60e|F`({H|E%Smn# zbc37U=~BAgg4|})i6(v{SifNPnY@eKy!oF48UiY8j%kdG1-T-Vx8W)eubZ6P>{vrH z%^IB<^KFDT4?Cd2ZhMP@vKQ{eY?BpnWyRtv5 z?6&=%xD!~9U_Z*hUK|F@zXaIVGq5hfCJT032KH_%u#jN&8Q9B$g#}xXh6#&#Y1}3{ zSEWBgPI3Bkq4RM*liQT_uu3Pl3HF#9AH=D$^J_2OI0u@DpC;J%ElgwkTsE++f~~hOafVyh*tMWr z&@fQRclRsrAwfQ$DQ}}-1GZPOTM7h$sS^Tgfh#q~H3+PHRSs~6sH!+p*@ zHhj?8WW(Lg1~0yaFxjj!uJp<<&T_%-7wn=;xzBF`wo9-xJ=g)KQtA2x%gv;lCRp$b zz}|V&ZDWr!L9l6pz2w1+6A)~%U{7XX1Gl+-&`+3bQQR-QxIU-sE?`>(`_Bw)vS7Of zyVHX`=$wuqnd}#AMW)=%Ub%Ox+`{XLtIou=cyVhKSE;xeUL5o<*iC|6n1L-6Y@=YO zW?*HujD1eA;_Q7uI~H0T z_c?QvzF2XiGI58{0VbCScK8jqjY(%|huXByDYW@^JNY)e-#MMKHFke~Q{iss7#r_unR3rw7Sry-|x&(`n`%9<;C?l#WrrAGevP(UnK6Wzqxb({R>tk*b5%aIDWw@ z1>5PtHaOd?etCz}M>|Dto9*v=oV#t6TwS6$N5nj+Tg^xDY!=kFH*sj z;LHbPOA0u*rV|!BZ(dHqf-jM9G6`+GpNWvEe_lFifpb;*v&bnY)9<`Q&sr?Y^NL66I z-<)%9I3Tws@B9C*@4MdXT^;7^?(FPrn=P~FKIikoGTlX4gfO&w;fP;Xu zfV+Ta0P;XOfEA!2pdDZ!z!NYH5Cqr*I19KBNCxBrN_(17c|dJ|0niWN2Ji#S0|Wtf z0Zsz017ZNlfLuUXFEgqLs0nBcXb0#6Z~;sO%mf4hb^(q9A^_)oAS>Kq;LU(#fa!p- z00+PTKxaTJz)yhMfGU8m0Hpz$-l!|Uc|aIoA7CqBEno>?7GNr1G{6qf9Uu<3HvMq% z!cU&7XkjIRGe@HS01p8Xfa8EYfQ^79fB?WqfCHdEz#7mPPzCTGKrui%%4KwB<`%_@ ze^9J=3DuPnS5kE!iThA>%5$m825M|&pa{T^A^#jVF5QxPePy7(DjKNQ2Jq1Q<@*|_ z+A0Gz1&l-7wbl7?HlY8{{d~XBo`gRMd5a8*U#c>AS255>uMIQ-xYIHN*#akK8ORyf zdZ~dvsbHWH03X2pCE&5xKrW!01egbyh4BAaw)|IMlt26%13g;*c6wvHzI59%6dU7h?oHqy7MXQ27B^6CGC->E^qzxEFXy4b`(ZGSY- z#zv42@Hgt>An+N$J-}rR|J!m4eb1i_)Z!-txdI0P8_RP7eltM1<_6l`%s}D5BLEG4 z$)_=vLF#2tjDfOU479;HKfcanwQbGD7$|0}fm)4H%XK-01ck}GrClyDGOT3w{V%mo*Ae-Ky|pi0%z=tXR-`56kzsR)eWBm z_fE%HK$z%;48Z9}209RHpj&CGerSmJtq2n+oYz0~%a4DwD6Z-{ci@tD4fG3OngIO9 zvV={-9e04S?tXzjJjTeET9Ub0OWFh4a6lh~zc24~&?{%M4O9hXOgU(v+W^ru{Q>23 z(0_ql5H|$)*Ea_G3Oq-HrZ4=3=|4WC(t99I=mpLJ-bF8Y2^i@e%lxR$7?u6!`Z;XuXPnQpRT|{_W`i* z`2@HO@E5>B*9TbSvC+aUfrYMy7VoIVdusl%n%^H-=;{N13EWhR57gq9X#Ulje;csS z@72P?wD1eSmc{3OiDsvrQ|hgw8m9%(N%*BaQ5s*NxnGDz zXoV(IM?;+o@-fnst*Xi=e5#nBsiC8>HbG<1(Kwl)=>-~b1AryJVOkn=VVY$k%~&ms zzm{eWu=JzlT3V^AlP1#c(b9%$Y0qnEZ|Kre=c4N+L8B2lo0*`YYWa0H*aS^E9nEAD zG&OWI8%@wa48=7At;o~{Sn8p(hWl!GkcNi=3!OVKR0xgM@MJA~I`D@Gp9hSWW@#C4 zX<%`ZZmmYQRl|F9@zS>xLfxM^H4Qo+EFVm418{c?*s3IID}3cyS_66llaf*W^tHmhfCHt@M)w6KTs<&*xvG ztI9`Vru`8rNXkty!EYi`G#1*E( zPRbXoqp59z<}7H$T>&-&z6UJwKLwUCoB=HDBzmyB2_9u@s65I83y;dc!lN#*@c0o} z+NzBf-WgbU%A7FI1W!aMZZK$trvtE*Nej+Hy|Kp-C;3TULR+%C%3tz~2Y(4Gk1(Sv z%tzwKYO+n%@N{6KD@-rpN1Gaq(F%@$U-(F#Qdjcqr{yVr8LtvHOOs8?ZjEP=JE)Ls zL0aA$fu){=CRRrSui}hy8L=^3@aP{6d0xp^!1C98er542VfBHHPWYVF@|3v2x;Q<} zeI3nY6Ew*>nvEuCihZZ%T^d;O`!BG_URDkYe5`dewN20r z(9v`+f=2j;y<;x-ER<#z{F1kuj_<}oG{RG8CWA)sbPdnb(nvoJH<4x|Xv76;X$}EP z|2Uzgl{%xIMc2n|Ep3!WE9shEuejjmd8g&V=zH8T}NYQf+h$w;x+-Dbq;Cb7NOM-l_pk|?`#PE^6Ew*>nqqy6 zE@!biYTJlxHB8Wy*U@w~K~ozv;u-=AuNJ^EW;+5)S$hM^I2sHreL=>xn+aY%pb_T> zEWG{(7G8^ih1UjP;k6go$m_5PUROXP?l!RSdIT)Il7NL*7O?P;v0Dii<7w52ujkhYX^HpR1q`{?qOd=hjrZPdkqRB=Kh4@N99i3}oB`3(k% z%5$}Je#uMny|0rYT$3SNmyhr*{e6Cas*S6jg`M!~g?bQYltbD}oW$9gh?D%Cz^{8D zo&oR+A4^@nYYWi`Z=q?Zqd9AW##%>{V1j0Vj;1WGnih7Fmz$2Jp$VGFI+}h(&~J zXldLuTB!rFDY{IPHQMPq+S(>)gEShEqn`dg2aUL7 zElnn{Qig^qE(t8W1q&}(%Y>QWRS`7es%W&rr}PjtOo`C`Nc%R`Xbl>+1{V76nqRQ= zp?<)!jvB1_1U~nWynB)>!5LiH6Gq%NP>< zwzW_iBk$T8jnsXV2^xcrrr6MeWis;VrK71~g2n+f;@mVm8d&$| zXMhQsjT+xzV4*vtrBTXnBFz;Y-zXC_u^Qh5jc=-kv$eD$cd=nb*Gt(ZYIu2IX$vW@ z!31q>jkd9l*2x5|wMHZ5Tx5b~fR5&{2^u#YO_T|m$vT>14n>!95on~Z25GcX&Kf3Y z_vrZbGC^||G~%uROBru#I0{(WGagv#Jz2w>dtQ#HI4u=xK2EcIRrv_2;Cssb8u zwKZCiJ=O%RL8G+>7P-0u3w=Lek!!Gq9e`zCbJP4j8ukM=&TC~I)qFvg=S1dtpb@uB zqZR(1CTN2-8j&T)1kFi}-(?-YlO||mHQE=z!t*t-7>Ga-u)Xv9s@@JwJS-(p~C!&O@N zCe6P`!$&oI8d%P!E&+=SH-U|9m~0}?c#TH#v2!ZA9>w>*=})p3^lN;a9^&lM&BGC-t&cN7LB^O^A-h%>+%j zj%J<-nkXGjhzXih(1^>{Xr&JBo1iV*T&)8sUrE=u%NOD*F{+#OC!P(s_pJ+y_cEiB z2s22W=E{5H-gZkfYN`usj4(fh)z*czLYODQ4!h;2y^gSA2rJ6rZit_R|KEH2|7YFX zZ{4j+>t3CI_`a^4lM}w8L~ZPy>^<#UyE}S1+S)kvw72zibakQ1<{exRV&mjE`mMNz z)Y{3_!`|A)#m>o|W?J;MarA8G>fXi6$EdeV<)q2h5*8hEMXXk%khN=HM?3r04mR#I&%*fF%QY{a z{*?5cZ9F{Ny1To&WA{XoQD?^??l$hDU^i~*Zr{$)$-bqVqesiGJv&fu^Ukg|c3OO2 zqH+283td+mXW0ske>)Vk1CSQgt(4>yq4u0@JiDM2Hp3)MYQYfDzY;Ee9bN2Py*=7E zx}$6`XoEO}ezLc6*WcBoPcO2s7Y3~8ruig#?=aZ;&XV;PTdC5U} zs&E+SI%zGar|Vic40MyU9;IYYlXbqn6mH?<;py&VFY3`zByZ>C?9@8H^$gr zj?VV9)}k*)q0+~wti-f+vFkQerR=8Vp$sRK>R0p5_BJEm8k)3-+DbNshcorD=;>r{ z??yv4J~D97-N5NnrG8zVoiUs{JG$8SrKb|o$KKroilD6zenrr{i;Y_!M|*Egm|nW_ zBE}6P7<@Xl?a{Suo?d~Dz(DKm=4S7XxF-2N)TWE@SNn2jS0$~VIZ9g~WCgW>@^Ky3 z%GIa4jk^uj3o8qS^rZ`CGX8p^hYOQ%ov$xV&F9z)%?26#3x+_BI5Ta;A(Or|TH>W0 z&_+s~v~e2N0mBNnIuPSxmp?jBqmJ!V@o2AECr2kIHQ5~Vo(`_wYB6B%Fz@c_p&QeS zF%r=ET}NpIy?eIpVbsDOn)P(BcXDd$V{ePGDs|q|x@~7;Wp?Y_wq@^LM%oTEXi#e( zpLVDeP0hIzeuNo8(5x z@_+{;-`*W^E=Nkc;a(n|gNE8Uj+Ad}4;th)Xpk*>yQ>|_>#F*Ps=f&-he7DlgQP%2 z`w6rzXf&sxsJZ>pZ+m*$U`}%DfU=@f{a^QwYQP5Xan0L?cV+P2-wME0z)zpFq^^L% z&J$;r7G`GpV7y6WCR+-lD+YfBqX$^F8G2`CS?WWo(m~~2*Ba*@;Aw;)JTC&&g;pAk zRO(-qrs?RzJE~WNtw8YCYFHkh7A^akW-PXZzB*v&V%nV(dj5c)DLJfxeymaY$ zx9;ZU*~)9^PfwIp{?S<)z^@B;S0--03D zWTSAg1V2|$*eZToq;f(HiIaTCP<bu|v4TNgl$%RJwK=4@+PV zP#W{JMH+Xd25gnQZ18Lg869b)l4D^RMHUyOGzBU3QcC@_LF%C>pBJRV27n~2;pCQ7 z`&&Z?7Y|R&Jce(3_i9(C>9;tS%L~KIYWS9#jP8k)QhN^I;sjh5IXdB==xiyi4P>;{ zg<_>oKFCMtodNmuJu$0D?~pdm4<)2_M;~xQnjzrkQ6Sciyun2za4e8!59D72@B=si zx&xX5uySbQf5~5GNFuE*Eg`+n z9lnBnLCVuskDIq>@1DK7bs041Cxs;MPoD0FZr3fl^5V_%p8o&!pia^sC})EPoGlfHc#DAL z%rY2Q&XcbImj#XomiyC$V=O`9)bt-B2=9d;T+Z3*1Ixm|8d%~70}EdtVBtFpSk7fP z0?T=57_git-v$O!oXXdXQo%U+t#ETCzmn!EUrS&unI?}J_9a*^6cH|Rb~h33P(=8L z5JV}ImVr?&37;i}Eh0R~M0l`?@GukMS4@OQnFvod5w0|D5ptH*t={zIvNRE1-$b~< zM0j@-;e!$GHrI@5pfO_AjA_w4{2D&wH~Re+;`c7lS&aVpGx0CrQQiUm)j_{-VSvA+A2rI`J4pBhfE|_ah6DEY z2c8y0J_z^m4M) zdM(=dfdOJrISRsGJOccby21?$%`-v3nQltFmDTXg8{_3> zsGsKd*(@Jm!H3Zle@!c&fXy6$7n4>0>eZ;+ObX<{0EwSjv$9V>U~)j2%*;&j$FDx% zlbH#Bt{NY|bg3^gqd>@@&>xU^&U`&{rjkBs=~5rW^R$iePb5Am>FNGV2J_7`6?u~2 z5S^JhZL=b8TwGETen~DcVA@8dJT7ogP?*esAjKa=adC0pD8HXtAHJws7Z;@$DDhG- z9OuM6k$NqGZs-l}(hlNEcxNEM+b56%0u+Cs;z#}ZE6q@XGf=&#Z+~AuiT7n+;g{*- zE42$he$-7Is1(2E<%~epuk=%i_f?uplAzO~jZ3J_oau|ZIwit53a?>=;mFSqy$?_V zV4;B-@VmfBCut|0|o<({hVZSEYkU*QzHWXT0!`W zN2QNTUstk_Tu{3TdS^ceo$5-eHE;ChVSj&gJ3qw>5~X#-QxeUWff#v|!EcQ4SDKX( zQ}W4^jEoE|qNLi~oZ;;=bNqN;U$mQ_%0{L4_V#}QhxTKiyzl^DMvHG(>D2@ZA1kXG z{sEgeX!J?~E!@g#+UCvUB>@;^ifodv6%rsE3537GC@*~UW=SCAG7eSxnl-Izcq3wi z5~@g~(63%?<$$7Y_Ep0(AgK}#dZ_|$#(zfHjNzd7M-}*@s3>nn1_a8Z2bph1iHtJ& z^fPl;uU;ecoKeCX9X^l#Ma|6z{8f6N5_gNSqlIT0gf@&H+ zUJaiq8Y4;c8AO)^U@zs>!odh-z-+M@?DNBgkwQOuGuUY1F2V?7TuHDI!Adyv3tUow zkRt;jSHeXT!8s^(q^WJ>J2g(!4IHK_nTr%B@;E3uK~fkyw!+6>Eh@;6#!n>|{?Ndx zQ#4CnxJ-2c{=S7GOZo&NSaGTjQq&jt2WA?>p~ldwH9r0fo$IYeVW26>Q=*_)g|m#X z0BHjw8z|ts9OX&s60W3ZI?X9hna1!u={ZB1OdWL;7~t)jN3S%cpOPSeP+3Mk(pS8- z3=j)Jl;URip#YjjP;;S@B4vOeEx;7wGQylFmwYqJ0B2K)C<|bS>nF-8SrYmZMG0=9 z;cqmI^?(vyq7b&w{6>s5g+li~jg9<`xUd{X`Hb=!<@Z(PF=G`=xkWxBmU4^V7%%l9 z{u01pz(Qxle(+1U9t*uV!7_&kAN(&Xbeu~~TL@O1x}GWv8cU(Wd7(H1>Z!1M8lOxX zR-K^4r%6f_zhZhG`;205M3V2=@sx7(KAm0{LfMC|P~4fT#IG{wPWTn#lyrI-6-znC zE)ZXcAl`Y9c>fW)uwgUt^0mb0!in!apoF{kC}YzW;@x}b$W(uXpQlqRR#M^x#6NgQ zymAF`P!RE|Aj)33l6cK(;%!@L*VwU0vzxN_?IS*VgbvM^K@)9lX>f-Q6yWGceEKx) zpFNwFj~ho_fBA*@(j~gMZXM0?^rSZzE|6>g{xlXwl;+=mPd_(kKz!#8@v~=iUn%_=KlVY2Y7!pgwcy^@Z~kwtgM$p5#Y-`68VO-a+Sf zY@>VO7wGg3 z+x#tkyR9lwPzkEEvo@ zKP`AeF|ki+=gwWqI{h@-_vq21N}rrNcP`DGIg_SOpH7n}Po{Bb^N}M*k_WCCK)KV9 zAw#Hd-@eqndw1&8sT2M5(@)f}VMD4{uO8K{TbJt8siW{L-1q;lAG*ZCV(0E1J1i`A zY!Aj?xUGXrv~}-QyH3N#O%K4k%x2cynIpP>S@)-3nl%WquvptNaQ?j6pMP8RyPwxJ z|8a-K+U_$JEtvC})qhHrUbn8vK8v-z|6aV%{)>tqbt?1QAnW}WYg_-dWci03zxm+z zN{!n8u-9Vk6t@+t&HA<~Rvkbh9XRR4(+MmrDtqh5UxZ`JUD)aK9W(qCtiy#Lwk0p`E|LESFTyAcmsSq z9ftJ(wcF1vcI}a9UoR&|y8(SVmv6UzgZOn$=%fA9lEN$&9`Q3fbIOluSM{SxrTnN| zf*&OSa?Je9(vtivRvz&yf%q~NYWtNbTbxQ1hYKY0t^77p7kpRPa!vQ zXn+!e*`7F?X=m_G%8kn*PR%90_!4?-CZ+GaL^&t#(Crf!C@tj`y@Gz_)O1SN5KOt( z?oi0|S@dAuBIw&o#LQx#J{%^1kz6yQcx>YOapB&1`&ZdVC9#910&o4l^Po6*>ou~Bk=O|*@RC;yt zIAz|vPA{)qqFp2Zgg!k-HxBNnonyV|(z5w?xFaYCk2P zI!HJ6@1T1d7ov?0(evZbi&+_Ta`qV7@V74=nSy!f;vPzSkwDKM$I!JE6Dc}uJ0(AT zOmX|>Q_OA5mzTCsLi}SoJ*_{bpFrDPTt^Y$d)BWx${j_|9z8%9=7o#P>D9TFloGz4 z{5=NI1JifY{j_lch^?(zloS#9bj~}8tCzsOQ%LnP! zwF?w};Vg|A(;IsBH9dI}M|YyP($&cAbnam=U5YtKEyvcT;5+N6-jc7$?~D&M+uDde zSX_+C?D(209H>Jz&a|XPH+oaYcv!=xw4&cK=F+-N8)-HAR5lip&z?W0d-rkk=loTQ z#eOF7MKV20NTlf4IO@1HivsrDrv|f9=*;s85EBzachP_E zpbuWZew`vBBIpYG`X%(g3+SiEjvb@@`}fnVS+kV+$j!}-1`HTLP&4%7k3UkiYSol+ zZzisL_M5uOL(zY_cYf2mRlDAAUMtZ(42>GK%6XGd+T&ti>3->%J+gD!t8T@H zjce9wl-on2$h`N`_aL0R?{v=vwJO*7zG<^^t=l%Q)vmWna4$W>XXD0=?tP{#U+yvR z%Nn&SH2Jz&>ja;=trUV+85!wQH-?@(acu97QUCqo+b^rt{=Lc9<@zLat=mondUdZD z;*Xw!J8oazvXVkDpmjn*LWf$p5}zLZeCCv+r_Y=|6;`E&6$q@V*8aT<;uAh^l!f@T zq{x{IkDNJs=G1`-wX0fvQK?jk66GX1p=+%iCI0Hnl}FB=J9EnOGa)Ej{KF56e~^%X znccJ9FJ`}`9_okqbLY>V8us;o3N;#hP{!Wg&b%jfe8Zc!`o%2e)uGs#D-NGOf9^u# zubT|0(DZ|^F0J^kbm^&6r;cga2=TuqKAf@PaCmscoll!IX)@qv^M)7K+m|g@Z_pou zT@e2=<;t}g>-R<6x^<@{8t~`V9~5sq4Ml zf<^HXrHYsP#`4Q*75bwFU+!PMZAQSfd$(`jdfHMc)+Z%Ol=|`uR8;kvoz2W1?+scz zW1j!KdH#P@`L47OwEn(AjjB>nRcfLBqW1S5uetttHmNd zI_c4dm6z{7c=9CX(9ChcJ9cf|y!r1|IoWD*v*g5tD=k~L>Cp8->Fd{TT)P3&XJ0 z1C;Q4`^-_y7^H+R^^N zK$Hc0R&C9aB5}~^=hv^NqmXgOoH^8?Ip*e@H^~Qb#GgE=?j2=)0X@%{4_xf*h%q=+hq8`^QP$~Gl!dj+t7{Ro#?^`T`i`YTQzy}1L+xnWqzQC)^G1q0 za){EgA5FS-lOjToV*k07F6|1Y#UoIM;b$l*_7R=mx0_NE6W~8i2exdaXZP<=@{w)G z4{L<*LzH@KD@AUdM_We?hJQDmIdP2QkZ$kzffPQ+4fQ!2;aln4=0)^4bUp1}w~P+# z+e3-_W}rS7WAD43ZXZ}h8OO0U3SUd%yOv>HvY4jeO!0ZtP2%(O5RSFy#T8h)ZXmw9 zm*S8QKSSLlM^jP^XtG{W`pX2Yk7DW8!k*aEW9=FhM!B))X!CNkQ`$4S?^7K84eP9< zZj`d$h2pm2Im}C0-#puow!RQZ(Z`ljYD_rAKDFK@cJ$`zVtR6A z7bQMCPdCmTpse}ex4Hto+1!v)LcJ&h``$Na$D%$L(DMsRDdF07O1rlY{r)vQKj2Kc znMw32@ev(4e2`9TA47L;-=N`M))cs_4~0J1PF?(dpnkjBk>6=gsy6*AGGAgrWj5jt zR&ZsiyuUuxIM$3BUHX-*qn+vZm$PU?WHj}-c%3X~ZK4j_vuWDSThwTN7Ik(ysH{1% zu;xukNgeV!F-aKXB?2e0=!-fsRJK1*h2hKT~qpvq^+?eXumlGxIVT`Wt zYjqi7^uHUwe$%&ic5e3Tf{8LS5s)_n=s3L2c;5e2iPuPzlbe@O-zL??HEEfvtyuCB zyiQN-a~$sM>GTX_l#`pCY1X>(@AZChbZqlW({C#`$?cm*i-)Y#{{4M>YHs=X)w8nt z_O4#_kA{tZY~Hq=b;qtw^((hD>V1%xNa~-M=!+wB)h(Jkcix$A?W}6lH74lN%e_*Q z`~+zUFVkKGuQ9rLfq?o-x`A??|-XU~QH8XV|{ zI@iuiQ2N8VjWqJePFe=3Pmw_Av0lC&apiK=Mn7l?KK$T=4~lMoFs{>lq&+I#vy@`{<_fr ztR(1S{&kt#L6d8GSSArkNPUb1vllE_a0~9nU2?|xleiw{&LwUySWsz%g$Gt7 z;)ZGTNMNMD^ML+P$$#o%{!z`_3#L{2#G+M|27?CM3?Gt5|2R&ezjN>2y$25ms`Nj1 zF*on@yWQ{q{m7!R*7<5F{9naBe*Ad${Q2`AfE@JS34Plxy&}zvn>VZX^@rv?%4?mk zYD=mASC5cDp_c>?9#$IMykk#kqhjW${|}0r|DbigvXzt`_v+EUgJH90;Op=AkO1>y zdt-xIDDCy3vGe^=xrD-hen?2z5-1?J{mJ(qKCEw~k8D-4p|!NyTPytDL8U(wvUcy1IeBj4mof`OZc7+@`aPZ*H zU7GW&46&^Mw7|PNd1aVB9rGf~0=^9woH+ zy=jZ~-G0?XQgTodBqa3jpO~7i&mbxKSwcd6+p1Lu4EXLlq;mST4|+I6GA2k()svgy z*#DW6Ms80?Crk$&PZNw;J|_1Vxv)>7~gbk zX7w%wQ+M$0b(>bsnL9mT%$Pq0;#eBw1AmQZXuKMy6;d z9BM5ghgA2eQ)kZnd&-naINBH4J>A^e_ccou?vMK?r|7HGm?&9w{!U(AE-qLHc%jQA zW7U-OHnBcLO9rG5&3{&{uKyp|EwTQJl0_I?l=9aEh8<6dFN}@$N+T|z(HZf-v|AeO zlmC-;ODUGvEyFasWiGCds&-3ifnz@99$3EJ(o)FDP{esUf>Ch)`()TH6{{|6mpRqG{8Hp_u(!9jI&$PlI_!@OdmO{g#<00D>~IV_EW@74 zu*ov)whWuCY8J-d(Srv+LVAA(2M4R;$4{igR>`pIF>G`UdmY2}$gtf4&NJ-244Wsz z&dac2GVGNcwtnp!q@U?DVuaPHvlr5#JQ?;uhJBA=*JId<8Mab}ZI@v~X4pI#wqAx! zmSIojOY7EUBmE?27gwtbrw*pW*2u77GVFm28z93L&9Iv??6HuW8TMy}jhSI5W}E?V zSU`Z>&KUIP$Z=Me*9=XEO_N~}TPnlW%CONg?6(YCFCUyUC&ho@z;lhPtlCRjfSHq% zQE5jDcu*veDc7q>SRjMR6TfPQhbmP4XSJy_j z?>&4CJ2AuV%&<{2Y~&2vI>QFfu+cN@s|=ee!|r+q_e7nL>`Q=*8(Ot$^~lteB!+#M zVKZjfnHe@{hMk&W&t}-Rm3{{sIrmiSY#5SN0+>tsjqBIUdUbYACTz{Qut}qiHUV}r z?Ar|6H=oP#@Z(1FAbmNBY0h1=#Vuj{_RnbUnC3>}O+qN9i=V<~zC=ovzF$8ZmLTf|=?gMTD zZUJ@x8UUIznN?@h7@#-cB;Wv3iD2a(o+TI>0A?glI1dxW^KMrh(NYIp0UiJsKtF)= zi3R{Gz<&Xs06qXnT}yjOo8yu$USYsbmH-R@c>$q5;2QumbAH+IT-XU8IkcLY{{YrIDR;HbojdQtHkM(J;#BB@2M_M^iQ{2Bdsbk^ z(4j*UTeoTRpk~clr$71Rldb0F<_nQ#a^uRCmwI>a{s8B7sV5gK$ho?HeeUU?pxmG_ zV=_kd=#kL8Mve2JTMiK2-54NsS3rhVty)z=e?Nt@4aSy|pU2~yvYZ8gju6Pf6{Z_&Hj~5$Byy)#*E2<9aGiC(6xN`E^~A=r-JgG1 zw(LIS>kE+f`B*OlboC&~8|NzwTMs{fg7le544WC}q~r7CS%>{K`t^Ys(lm zLcThA3iB4s1AF%}&L^2qK(~cMx8Aq`{RqAC08kHU z@!;s_Xe;^dgAO`&{5W(I(xxXd>}m`<8pEE(u&wdcOAGnM=@|?=B*P}munl7#z}Vc3 zkEHI}rOI&fq$&gIksm$6hhTCQ8D2t<&vtZ7`}Cuab|T9W0O^;Ct{Faj_z;{aF*Xex z8WzThvG<^x6x&5-XRKs$i61C1ClVn}FcDBni>ojZ5=Y1D1zq)5Ji{7VH_ z;0k-b`vk{+utvhIN<|gQ4po!zsv+m1WF24}YT`Sa*D(F|K6$4B0T?6>$*qyc|-yg7fl2YLqPUD!aGkD!hv zk6`2_GAu_sEM3BU>?+Eg3O$*IeyGmrc^PXD(he>iJH{c~zX75z2y?{{*c#DXj~F&k zhE13`?h?bk%GJZ$bCWAQ*z#yYE))16uMZf7JT9qy%_zfql>6!(^!qoQxig$oLog1c z>p={o%gN4R*wGp1)4XZw)Ys)c{&+3279AybY=&ICc#(0ezz-ir^0SB6!0QxWkJ`?S z?+@g5kq%trtbxl-vtZcL(f=Wzw6FAc^jT#M^D2jNkAZ{7dGpTkUVLpI<_T0kZ26p? zoWyBKiGT!tlaa>fwr=6dUwjdatUE~l82dvmW9(wz#gS1l{QM#2^aqFeR@63b`^<}b zC5_|nuXSVCjrqc|mB=4`8}jDKaPKHa~eiRqaFCx)?w>I<5>-*VoJc>K+e#4zHv>H!+ z%D6{@a-U^Je^ttVQ~K&trLEs2yx|M0Rxs{BFz%5+UqPr-SieaBFzN@SySsipx2aR- z2=Z6j-_z4`$n~4IIq}6ye)8xpzj%ncy&l97rw?;=u=!yPvtc{)chj>N_gpyp9NJxU zOBy;@P7W&$>QU))S6}n%< zjsd?u^gE}7*rWUyN0*l|?n-d*p;J6>-*q0d?-BpHUo=<@JY2y&A*U|oGrlAahIzYRh2b`P55i`c}(ID(0^iK3V zbO*~Eh;_I!Hf2tNPS4YmsEY$WJ~_>5)bK;LQg(9yjq#m0x=TA*~Kvj1EK%#aN2{Tm`CiG4?8d)8m8jI!=ZPbR5&_?i+FFU&9%9n|R-hK*;o0 z&H^W=1n173L%KC=I2d`#-aztx%T1g(aR`pKVfu{W^vpM$6TKflPJfDJNaLOjr)Iq2 z7pZSJAtjriCcZ*7KjFCOJN)S0CC0rWPCPRc`#2}Yog#j|q5&r?uf+lG15zq}{>5Qr zHX9&o*4lu)H4mQioRmLad4CG33_9@q{$03swU=>+jGv%SzrdWChW1T)^@?A-e8~ye zv*2zJKYkL=xF5t1qaX9V$j5x^K^)(_eV6ZFMm?T5lpDNfZ|3Z|bD#d&qle73UjvHe zWt#tBHfGG2p%1WLllfot;Hr^hc&XhmhFzMsj2gwkzP=nXeLA1Q_`M3I z?;gi!>2N;{O@5bg2MIjxGVUWWELx1YbuI3wR!4Ty-wU+6CPFcPfB(gi4$iO-WHZ9+2UsJU3%4B?y;6Zp;9`HVYl zoN!E);Z86m-lkc z`F)H#e2hDNjC+6B$KO-+Uu-Pqy<~oY{?w^+=WEDfET9@dS?dB9aJzT!+5hUrbIyIZ zk8>}~Q1iZ^L1jjaoxFa z;ljk?#fwY-{}~{!Lcir!tX%c?mX)(_=I$TJxyQ#th6S7}<(9mq+?OzaT-pl2Tykj- z0J=%Z8~gW*r#Sg!Snhy7{y2W=`0?ij=3|Uq>C^4nwVR1d>j8?)Uj)M4w&rtY2l_0S z8yL8C<($nS>*gK~-L&A`@hwZQT?k$geJ^C)^Qa@6GoKyX$AypbI-vAn5-C>)uxzHf-p>eEIS=SWi~R z{k+l$mpqFaa|l*lvV~uZsG{%dkRM3?A((;A0#E`_2JnpzTHsmsoh|f$FdU1T6?$LL z5+um8s)GLMAJOTe+d2VckL?VQI+y%v0looz4v_i~{b#IOJeqV;Z*o>3XEL%!5*|jV zfalKu(x3imn}YN$CvpgnYE7Fqor4=+3D(xu(YUJ+QK3SG{Rm$MkiIKt9)khx0CEOX z86b06S%7gKHF{RvpGW3vX~kAqn>{?&7V*&{CyJtp~lldhQ1p2KW{56F}BN(thQ&=bkvn zSv<&KxHz(F*VwT=dp;kFa})v3E?u7VK>gIHP$2}gO8^r9Jpt7L#{Q1ScRH!7vN$j7 z+`D)0TQDQz`12CSJ%--Qd0hZ-7lv^!h8HfF&tp(V7@}UaZrwT(^Gf(0zFOww#G!xx&6hD>#$inp7aGcO*ejmKUU?tZVGD*164nLB7)G?!dN((|vt$X@zR<0hA&WqM# zZl2h;@2hHGeH8||=Ky*EWW1Ox1Ny{wFoL~01OB*ffh;ayD|epbW3g_{eEEKOg0*!x z_9X(BR`ThfNlJYl#u_C{e-`zgGAQ#j&H}>GUvW2&+tsZrbLApHpLdmE@ZiDLxXOX+ zb2nMmtI)S{{tNjdG5;sL8(v{;9KHhk7|0Gt*dNF%ogDejUw`3;hB4R49#Qs8vcHqP z%e%^ey}HpQVBNYBZKst%&Lw3`h#lxH{bXZfV=ZT#xI2ZTvsKt0oL1W5=_9l+&b*A! zeQyWGoiD~6FiwAoyd>@s_El#gJ0R=$0$%U##NmE^>i$ypc>kshvQE;uLS+!W*AgIO zx`2aTv0jUO9e>?s_t$7Z5d=Nn{YB7WK2VD(M!^gWPZqV&>7aa z`2qEC2G>d>6umF!mT{5Tb0(k+8sL5)>*3SQU**X^c$9%dr%X}LOSfT7D`$xRrVLnT zz02W_ENi-T0tA=&#llzs=uK;^lWL>PeLfQ7HupURK(w^M z_f}TNA^UKEtXC8VJBl^#88NQN6evTi$p07_Y0%(&2i8ORomCb2aUYdKu@_b5rgxPg z{2F*ckUM~ri74+g$p0ET4E|)md&+Pcbv&Yd`&bA)1yDtm9+UjAVb<7NDtl#p86H1` z{EskaYVh;*-&hZo_SaG5w;kDz!$sf7*^HbK6;%eD*Ki7CjLKr%L+41Wry{0J-`xPZj3aXyfR^GG8^V_hI?49G2OF6EqE$^cu7HTEUSS%>WRjb(WJ z@FvcVPUJzG2SZs8R)>C2-oAZ1$@;^m8;m$vAibY2PU)&W}=GlxybRfu|j=ZM^=&xL2>pkNfld^!{y=ux-+$x8#Sd##-iF+}SQrhNogPx{Gy- z27@vK*$BTsTg}~0-Q)VRaDEQ?BUY|7ExVLS&JZAf*5wGLzBgP#JAp6mI& zm%Y3dy7?{nv36197CWyvIo=ojiLQfpNcc4NGcgMns`HrWpVTaMz4wka-x_y!Zd$F{ zcF3Qne{ddXy=TuJm`0x!D8ut8wBK9D7PM8>eJgNv|p z;=LZ#9)n`>xip-EKf!oMzY_f~@*lw-M{Kty)~#t+e{1^37xG&f$1l!StYz(tcNaJ@ z>LO=t`-u~-ZR3P!$bS`;uEB&I_j%yPqylAVwk(S)J4W&tn^7DgcEk5M(eaU(b1@{8 z`B%zvA7wd=v#0m0m+oVXP3hO~1?08`$X-Bkcz@Gc?Ch_utwH&TGw>SB^Q+rAQRKh0 zqDZ)WavvWEJI-@y`Qaec~(eGDc6M04FdmLeby<$$AN6IuyFcpFPPitLGaW$WpcU`~obKE{3FyYMNr`TE7Y-o;57o9`@xoac%@ zGBr<|ShwQ6FJ3uf1UIy@lC!yQ0eSl{oYPt3yqQx%eOP3V^~LMrmxDQ3}pTVJd z95H>Wq8nwtH|elXf9E`HV%-{!wYgvK-j5--oJr)ZJ@HJim0II|H76eOQDnb|SCyVz z+6DQq;mqK#e-0rtc&nEO@0PV(QBLft4CTd3kwR%f9`2$S4-sua3a$<2ch!iNV^rD0|;K!m@TwU12Ueh2tKWY#;}jk3nsGS&d2`R3S4{OHI6OqVbC)gz3rqi+8i z;a9*v(n$+>NEO6`1tr(%XyX9 z>crM8`*=A6N{hi>Z+AE5b(J|QVlm^LKu){27i*~nuz`8Q-ug4=toa&c`v~`)zQCUS zJFH#4$KJP@noiOJB3Jgr`eLwBjsvr2a|G5Zac9qR9QM5_*ptMe?eAb+8ai_(&c2*@ z#=wD>T{?7-^JqCw&fBN!W!69A_Pn*+QA~$eYeP-klPHBY`wZo;0-JJeezB?vUk>cS z`^Npwn>+{cT&$awb6Z>6H{<*CdF0l$>xQmB|19StvbPl-`Ci$j9I&-oi(ORCA7uZ9 z-@H`h&3ts4bB_#Uyik}I_O3FBy;#<-#yy1W;~rn#%kgIyapFN2ez~&;r)_GCa+O56 zK2`hTx~h!zOA^G8vv@y%tPQ#W8UUpJi|q3V&vRqPjmZsoutj*z+Rt(Uro4!v-q>Hm#l+uQU+P$ zyjOnIe+SVyBBzu=)^XC8_Cfbbe+-#3heMYw<11JrMPe=g2)g^j&em{?Qs+1KMH5>@z9Mj9*XKq?<<4sVR06+7?OSkC~(GUT6!|W z`o~uKkMaCX@I}b;F4=>}jpJRuzPtzfp?%QtAy}Ur^!Mk(I5(HGpbJ=UUB#a62IloB z^c}g!VLaC;+?T{&fQ zf=^z(zfbmK(1Ryt&BA@!*?eZ+JoNQT%DzbSmgq73KBUstqQ4&FUJZVhHls|LGAkj^ zj{xa2|JZft(7{sf*~&dXBRqQ?$vFpXu%~c@y}=9TQIoOWov*B=(~isChlRNNFrV*k zn2UY;9KNt<7KhFFTdDWLvY%V9fbrV^ip~|ipg8C^xsRaSg~dF%75Bk#e|j5){vGhy zKNcQi8)L;w^quH)Bc$EkU!?5H-3hU^$ld+CI}Drfj16)4cEC=?Zv-%YGXQI;O&q&( z6Yh7c<-`Z~8NYdeeIhoB=$pztW_gzTIam|jgigRc@tcr)BA~$8@_WkiA%30auC%Eh z@P7YW_5M#}SMOo$Lpcrr4grn=j&bZ!_%XJXduXzLmir(gyR0Q-O^SB;`|rQyTyO=z z8X$WAy$*Jl2BO16Z)Rtta5mnl$+@!le^ho3Kgl~*2XU;Ni9U%hkqS&qQH=E{{Ti#@)|x;1N}G0+OgF88}-{+0DRUYEZOePR#VGJtv|Mt8(=%`2FU$@ z!<>LM;hr^Xo`Gj`Kv~>N7!r!LGS=!ZVedHye#<9Mp6rUdnw=3Z`xBA*z5O0E{}b-5 za)PND+OieoY7c$V4`uY9GG)pDgy*e=QGS_UW&f>D5AVNs_?_Kk`Tbb@&Tg2zUzsc{ ze2Jn2x6tr6L}B7biz*nSO2Me90vOULej_H$|Nk~N=4Zr4xr}leO!ECJTaqsqsSck-QRqrQ zEp7!}yoG$PM!sz01Uv{Anv7rSQNO(--#dC+7TMAicAqNW3CjOcj-&ET9ZQ&4^S{GW z20rzhJjQQV8NXR2-$e4na#xi&|BF`oFBTR2PEmEdv@753s$N(S&0p#*dF1OzAEEY) z-(a!?g?ukdzCZLG>ednUAU+SkP)K8mSAf+oWVzxiQu3v(CIF$fM6KE*O#Nb#`V}sb z^iO>4Nd2ynUH-AWPChdn+~AEn4B2AJmrU^Xprz`Q@0gh5mvmI0e8t29lrp$WD&H_E z21?Z@Uoa^SO3n9)97?E^mde*lN@$dp%C}2OYLxI*m184MbD0wRpcV$4ujQ2LX3Q`*cHmLpoCIMXuyfQ`>$RaSN?9#Sm_$JsZ(=C7>s2&RwauVR^0H0rjPR zQ7KrVsgE4$W05VmmGUBEeO$RSL+iWZilLj*YezvE^(#}tzYAhUgR6Ww%m)6Z3cay6 z$rs9`7fZg=>4ZXBMsoLozc$pnS)N>dlos^R(Kkf9HB|b%ykIFZiR|iku3V6_ zlTIr6-e6DAdw{#}Zm7_A2G3y%rIf}E*8)YVVHmTrDJonJ1Fq(oq0Utr>1~pSv0UPl zZ=k8)Hq6f}|Lb9@jOsVB?9lsUB| zx!B=5u`c$_twz~ktUz*hE@91XP)81)l9elLb-3H&{b{S@=eO%IVWW%}*zi(DgpFm0PhOg72 zW=5&pRG(){trChXgwfnSGoPGf08dZlIq| z2ZmjPey-7e^^fHhNtB>PXI(T`?lo%s`vHsX-Ahv*(TDlfx2wf;IGFIF_3@)W`f66i z=Q6SFW_Ls)O)@)_>mqt5FOy)a){oGq__L&VFFC)MOxPnNY{1%fWnNaXv3~8WUV)mReCRGMZNsJYR0;_@tO0~lewS*Iaj+hDB{GMw^ zE(E?3_xJMjs|0&(fukKw+R+j+4sRP1xw>3`*v6l-ymejuK*+e0=F?7^BLuR2+7M2u iBI6vM5+y6_9*^TcCX0#5jmv)UI;}$t=-2W;KfVDHA$6<( diff --git a/other-licenses/7zstub/src/7zip/Bundles/SFXSetup-moz/Main.cpp b/other-licenses/7zstub/src/7zip/Bundles/SFXSetup-moz/Main.cpp index c14572ad44a3..1de7d6893e4c 100644 --- a/other-licenses/7zstub/src/7zip/Bundles/SFXSetup-moz/Main.cpp +++ b/other-licenses/7zstub/src/7zip/Bundles/SFXSetup-moz/Main.cpp @@ -236,6 +236,20 @@ struct AutoLoadSystemDependencies { AutoLoadSystemDependencies() { + HMODULE module = ::GetModuleHandleW(L"kernel32.dll"); + if (module) { + // SetDefaultDllDirectories is always available on Windows 8 and above. It + // is also available on Windows Vista, Windows Server 2008, and + // Windows 7 when MS KB2533623 has been applied. + typedef BOOL (WINAPI *SetDefaultDllDirectoriesType)(DWORD); + SetDefaultDllDirectoriesType setDefaultDllDirectories = + (SetDefaultDllDirectoriesType) GetProcAddress(module, "SetDefaultDllDirectories"); + if (setDefaultDllDirectories) { + setDefaultDllDirectories(0x0800 /* LOAD_LIBRARY_SEARCH_SYSTEM32 */ ); + return; + } + } + static LPCWSTR delayDLLs[] = { L"dwmapi.dll", L"cryptbase.dll", L"SHCore.dll", L"uxtheme.dll", L"oleacc.dll", L"apphelp.dll" }; diff --git a/other-licenses/7zstub/src/7zip/Bundles/SFXSetup-moz/SFXSetup-moz.dsp b/other-licenses/7zstub/src/7zip/Bundles/SFXSetup-moz/SFXSetup-moz.dsp index ffa6286560e8..e9ff6ad48b14 100644 --- a/other-licenses/7zstub/src/7zip/Bundles/SFXSetup-moz/SFXSetup-moz.dsp +++ b/other-licenses/7zstub/src/7zip/Bundles/SFXSetup-moz/SFXSetup-moz.dsp @@ -54,7 +54,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 -# ADD LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"C:\Util\7zS.sfx" /opt:NOWIN98 +# ADD LINK32 comctl32.lib kernel32.lib user32.lib shell32.lib oleaut32.lib delayimp.lib /nologo /subsystem:windows /machine:I386 /out:"C:\Util\7zS.sfx" /delayload:comctl32.dll /delayload:user32.dll /delayload:shell32.dll /delayload:oleaut32.dll /opt:NOWIN98 # SUBTRACT LINK32 /pdb:none !ELSEIF "$(CFG)" == "SFXSetup-moz - Win32 Debug" @@ -81,7 +81,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"C:\UTIL\7zSfxS.exe" /pdbtype:sept +# ADD LINK32 comctl32.lib kernel32.lib user32.lib shell32.lib oleaut32.lib delayimp.lib /nologo /subsystem:windows /debug /machine:I386 /out:"C:\UTIL\7zSfxS.exe" /pdbtype:sept /delayload:comctl32.dll /delayload:user32.dll /delayload:shell32.dll /delayload:oleaut32.dll !ELSEIF "$(CFG)" == "SFXSetup-moz - Win32 ReleaseD" @@ -109,7 +109,7 @@ BSC32=bscmake.exe LINK32=link.exe # ADD BASE LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"C:\UTIL\7zWinSR.exe" # SUBTRACT BASE LINK32 /debug /nodefaultlib -# ADD LINK32 comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"ReleaseD\7zSD.sfx" /opt:NOWIN98 +# ADD LINK32 comctl32.lib kernel32.lib user32.lib shell32.lib oleaut32.lib delayimp.lib /nologo /subsystem:windows /machine:I386 /out:"ReleaseD\7zSD.sfx" /delayload:comctl32.dll /delayload:user32.dll /delayload:shell32.dll /delayload:oleaut32.dll /opt:NOWIN98 # SUBTRACT LINK32 /pdb:none !ENDIF From 51adf3206ba4c00882b61a916b7e77da09b5c983 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Thu, 18 May 2017 09:24:33 -0700 Subject: [PATCH 25/35] Bug 1365831 - Replace assertion that non-display SVG containers are only reflowed with NS_FRAME_IS_DIRTY with a real test of the condition. r=heycam The primary patch in bug 1308876 causes frames to be reflowed less often with NS_FRAME_IS_DIRTY, particularly when multiple passes of reflow are required for the frame or one of its ancestors (which is generally the case for a document that ends up not having scrollbars). This change causes this assert to fire on various SVG tests such as layout/reftests/svg/svg-integration/conditions-outer-svg-01.xhtml . This happens because the outer SVG with conditional processing (in this test, systemLanguage="x") is reflowed due to its parent resizing, without NS_FRAME_IS_DIRTY set. This is a relatively normal thing to happen during reflow; we just didn't have any tests that exercise it. This patch adds a crashtest that triggers the assertion through the same mechanism, but with a dynamic change, rather than depending on the non-dirty reflow triggered by bug 1308876. (I confirmed locally that this test does trigger the assertion without this patch, when run in the crashtest harness.) I think fundamentally the assertion isn't valid, and we should instead be testing the condition that it asserts. MozReview-Commit-ID: D8hjAbjKyuL --HG-- extra : transplant_source : %98C%3A%B1%93jb%E7%3D%81%19%97%A6%04%0F%88%8B%D2%A35 --- ...nal-outer-svg-nondirty-reflow-assert.xhtml | 28 +++++++++++++++++++ layout/svg/crashtests/crashtests.list | 1 + layout/svg/nsSVGContainerFrame.cpp | 13 +++++---- 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 layout/svg/crashtests/conditional-outer-svg-nondirty-reflow-assert.xhtml diff --git a/layout/svg/crashtests/conditional-outer-svg-nondirty-reflow-assert.xhtml b/layout/svg/crashtests/conditional-outer-svg-nondirty-reflow-assert.xhtml new file mode 100644 index 000000000000..b23f064a6c36 --- /dev/null +++ b/layout/svg/crashtests/conditional-outer-svg-nondirty-reflow-assert.xhtml @@ -0,0 +1,28 @@ + + + + Test of NS_FRAME_IS_NONDISPLAY / NS_FRAME_IS_DIRTY assertion + + + + + +
+ +
+ + diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list index 6c2287006ab8..a337d05a4b26 100644 --- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -199,4 +199,5 @@ load 1182496-1.html load 1209525-1.svg load 1223281-1.svg load 1348564.svg +load conditional-outer-svg-nondirty-reflow-assert.xhtml load extref-test-1.xhtml diff --git a/layout/svg/nsSVGContainerFrame.cpp b/layout/svg/nsSVGContainerFrame.cpp index 914708731e66..3a19471a1f27 100644 --- a/layout/svg/nsSVGContainerFrame.cpp +++ b/layout/svg/nsSVGContainerFrame.cpp @@ -100,18 +100,19 @@ nsSVGContainerFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) * inherited font-size of an ancestor changes, or a delayed webfont loads and * applies. * - * We assume that any change that requires the anonymous kid of an - * SVGTextFrame to reflow will result in an NS_FRAME_IS_DIRTY reflow. When + * However, we only need to do this work if we were reflowed with + * NS_FRAME_IS_DIRTY, which implies that all descendants are dirty. When * that reflow reaches an NS_FRAME_IS_NONDISPLAY frame it would normally * stop, but this helper looks for any SVGTextFrame descendants of such - * frames and marks them NS_FRAME_IS_DIRTY so that the next time that they are - * painted their anonymous kid will first get the necessary reflow. + * frames and marks them NS_FRAME_IS_DIRTY so that the next time that they + * are painted their anonymous kid will first get the necessary reflow. */ /* static */ void nsSVGContainerFrame::ReflowSVGNonDisplayText(nsIFrame* aContainer) { - NS_ASSERTION(aContainer->GetStateBits() & NS_FRAME_IS_DIRTY, - "expected aContainer to be NS_FRAME_IS_DIRTY"); + if (!(aContainer->GetStateBits() & NS_FRAME_IS_DIRTY)) { + return; + } NS_ASSERTION((aContainer->GetStateBits() & NS_FRAME_IS_NONDISPLAY) || !aContainer->IsFrameOfType(nsIFrame::eSVG), "it is wasteful to call ReflowSVGNonDisplayText on a container " From b138a8ec44cbabd21a3970dfded3a86c8f95ca2a Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Thu, 18 May 2017 13:16:06 -0400 Subject: [PATCH 26/35] Bug 1365935 - add Moz2D Factory methods for making an FT_Library. r=jrmuizel MozReview-Commit-ID: 7gQuVrl38aT --- gfx/2d/2D.h | 9 +++++++++ gfx/2d/Factory.cpp | 16 ++++++++++++++++ gfx/webrender_bindings/Moz2DImageRenderer.cpp | 15 +++++---------- gfx/webrender_bindings/moz.build | 4 ---- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h index b087d3fdf270..b12c3cd14c88 100644 --- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -31,6 +31,12 @@ #include "mozilla/DebugOnly.h" +#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GTK) + #ifndef MOZ_ENABLE_FREETYPE + #define MOZ_ENABLE_FREETYPE + #endif +#endif + struct _cairo_surface; typedef _cairo_surface cairo_surface_t; @@ -1618,6 +1624,9 @@ public: static void SetFTLibrary(FT_Library aFTLibrary); static FT_Library GetFTLibrary(); + static FT_Library NewFTLibrary(); + static void ReleaseFTLibrary(FT_Library aFTLibrary); + static FT_Face NewFTFace(FT_Library aFTLibrary, const char* aFileName, int aFaceIndex); static FT_Face NewFTFaceFromData(FT_Library aFTLibrary, const uint8_t* aData, size_t aDataSize, int aFaceIndex); static void ReleaseFTFace(FT_Face aFace); diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp index ee5cd5743105..9c04cfaab811 100644 --- a/gfx/2d/Factory.cpp +++ b/gfx/2d/Factory.cpp @@ -662,6 +662,22 @@ Factory::GetFTLibrary() return mFTLibrary; } +FT_Library +Factory::NewFTLibrary() +{ + FT_Library library; + if (FT_Init_FreeType(&library) != FT_Err_Ok) { + return nullptr; + } + return library; +} + +void +Factory::ReleaseFTLibrary(FT_Library aFTLibrary) +{ + FT_Done_FreeType(aFTLibrary); +} + FT_Face Factory::NewFTFace(FT_Library aFTLibrary, const char* aFileName, int aFaceIndex) { diff --git a/gfx/webrender_bindings/Moz2DImageRenderer.cpp b/gfx/webrender_bindings/Moz2DImageRenderer.cpp index 3ce3c85b68d8..5fe879189a92 100644 --- a/gfx/webrender_bindings/Moz2DImageRenderer.cpp +++ b/gfx/webrender_bindings/Moz2DImageRenderer.cpp @@ -14,9 +14,6 @@ #include #ifdef MOZ_ENABLE_FREETYPE -#include "ft2build.h" -#include FT_FREETYPE_H - #include "mozilla/ThreadLocal.h" #endif @@ -56,17 +53,19 @@ static bool Moz2DRenderCallback(const Range aBlob, return false; } + void* fontContext = nullptr; #ifdef MOZ_ENABLE_FREETYPE if (!sFTLibrary.init()) { return false; } if (!sFTLibrary.get()) { - FT_Library library; - if (FT_Init_FreeType(&library) != FT_Err_Ok) { + FT_Library library = gfx::Factory::NewFTLibrary(); + if (!library) { return false; } sFTLibrary.set(library); } + fontContext = sFTLibrary.get(); #endif // In bindings.rs we allocate a buffer filled with opaque white. @@ -88,11 +87,7 @@ static bool Moz2DRenderCallback(const Range aBlob, InMemoryStreamBuffer streamBuffer(aBlob); std::istream stream(&streamBuffer); -#ifdef MOZ_ENABLE_FREETYPE - gfx::InlineTranslator translator(dt, sFTLibrary.get()); -#else - gfx::InlineTranslator translator(dt); -#endif + gfx::InlineTranslator translator(dt, fontContext); auto ret = translator.TranslateRecording(stream); diff --git a/gfx/webrender_bindings/moz.build b/gfx/webrender_bindings/moz.build index ae5c9236e1de..30a39184ee43 100644 --- a/gfx/webrender_bindings/moz.build +++ b/gfx/webrender_bindings/moz.build @@ -37,10 +37,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': 'RenderMacIOSurfaceTextureHostOGL.cpp', ] -if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gtk3'): - DEFINES['MOZ_ENABLE_FREETYPE'] = True - CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS'] - include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' From 96f477fd993a66b357d25d6a35ce56d224bf0dec Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Thu, 18 May 2017 13:40:06 -0400 Subject: [PATCH 27/35] Bug 1366004 - Update pdf.js to version 1.8.363. r=bdahl --- browser/extensions/pdfjs/README.mozilla | 4 +- browser/extensions/pdfjs/content/PdfJs.jsm | 2 - .../extensions/pdfjs/content/PdfJsNetwork.jsm | 1 - .../pdfjs/content/PdfJsTelemetry.jsm | 1 - .../pdfjs/content/PdfStreamConverter.jsm | 2 - .../pdfjs/content/PdfjsChromeUtils.jsm | 1 - .../pdfjs/content/PdfjsContentUtils.jsm | 1 - browser/extensions/pdfjs/content/build/pdf.js | 134 +++++++++--------- .../pdfjs/content/build/pdf.worker.js | 10 +- .../content/pdfjschildbootstrap-enabled.js | 1 - .../pdfjs/content/pdfjschildbootstrap.js | 1 - .../extensions/pdfjs/content/web/viewer.js | 111 +++++++-------- 12 files changed, 131 insertions(+), 138 deletions(-) diff --git a/browser/extensions/pdfjs/README.mozilla b/browser/extensions/pdfjs/README.mozilla index 330768bfb7b0..81f3e1f6a19a 100644 --- a/browser/extensions/pdfjs/README.mozilla +++ b/browser/extensions/pdfjs/README.mozilla @@ -1,5 +1,5 @@ This is the PDF.js project output, https://github.com/mozilla/pdf.js -Current extension version is: 1.8.346 +Current extension version is: 1.8.363 -Taken from upstream commit: 15425d5b +Taken from upstream commit: 658fb03d diff --git a/browser/extensions/pdfjs/content/PdfJs.jsm b/browser/extensions/pdfjs/content/PdfJs.jsm index 53faf3d30ca6..8f66c6b6ff9c 100644 --- a/browser/extensions/pdfjs/content/PdfJs.jsm +++ b/browser/extensions/pdfjs/content/PdfJs.jsm @@ -12,8 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals Components, Services, XPCOMUtils, PdfjsChromeUtils, - PdfjsContentUtils, PdfStreamConverter */ "use strict"; diff --git a/browser/extensions/pdfjs/content/PdfJsNetwork.jsm b/browser/extensions/pdfjs/content/PdfJsNetwork.jsm index 31d7c8b542e8..1175b2e52b85 100644 --- a/browser/extensions/pdfjs/content/PdfJsNetwork.jsm +++ b/browser/extensions/pdfjs/content/PdfJsNetwork.jsm @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals Components, Services */ "use strict"; diff --git a/browser/extensions/pdfjs/content/PdfJsTelemetry.jsm b/browser/extensions/pdfjs/content/PdfJsTelemetry.jsm index b57fe980136b..608cb03432c5 100644 --- a/browser/extensions/pdfjs/content/PdfJsTelemetry.jsm +++ b/browser/extensions/pdfjs/content/PdfJsTelemetry.jsm @@ -13,7 +13,6 @@ * limitations under the License. */ /* eslint max-len: ["error", 100] */ -/* globals Components, Services */ "use strict"; diff --git a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm index 9b10554b853c..69b5c47eaf32 100644 --- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm +++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm @@ -12,8 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals Components, Services, XPCOMUtils, NetUtil, PrivateBrowsingUtils, - dump, NetworkManager, PdfJsTelemetry, PdfjsContentUtils */ "use strict"; diff --git a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm index df698e412614..47c92883efd0 100644 --- a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm +++ b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals Components, Services, XPCOMUtils */ "use strict"; diff --git a/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm b/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm index 0790476b1f20..e5c003daa520 100644 --- a/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm +++ b/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals Components, Services, XPCOMUtils */ "use strict"; diff --git a/browser/extensions/pdfjs/content/build/pdf.js b/browser/extensions/pdfjs/content/build/pdf.js index 6deb9c8591de..34e823583f59 100644 --- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -1199,78 +1199,73 @@ exports.DOMCMapReaderFactory = exports.DOMCanvasFactory = exports.DEFAULT_LINK_R var _util = __w_pdfjs_require__(0); var DEFAULT_LINK_REL = 'noopener noreferrer nofollow'; -function DOMCanvasFactory() {} -DOMCanvasFactory.prototype = { - create: function DOMCanvasFactory_create(width, height) { +class DOMCanvasFactory { + create(width, height) { (0, _util.assert)(width > 0 && height > 0, 'invalid canvas size'); - var canvas = document.createElement('canvas'); - var context = canvas.getContext('2d'); + let canvas = document.createElement('canvas'); + let context = canvas.getContext('2d'); canvas.width = width; canvas.height = height; return { canvas, context }; - }, - reset: function DOMCanvasFactory_reset(canvasAndContextPair, width, height) { - (0, _util.assert)(canvasAndContextPair.canvas, 'canvas is not specified'); + } + reset(canvasAndContext, width, height) { + (0, _util.assert)(canvasAndContext.canvas, 'canvas is not specified'); (0, _util.assert)(width > 0 && height > 0, 'invalid canvas size'); - canvasAndContextPair.canvas.width = width; - canvasAndContextPair.canvas.height = height; - }, - destroy: function DOMCanvasFactory_destroy(canvasAndContextPair) { - (0, _util.assert)(canvasAndContextPair.canvas, 'canvas is not specified'); - canvasAndContextPair.canvas.width = 0; - canvasAndContextPair.canvas.height = 0; - canvasAndContextPair.canvas = null; - canvasAndContextPair.context = null; + canvasAndContext.canvas.width = width; + canvasAndContext.canvas.height = height; } -}; -var DOMCMapReaderFactory = function DOMCMapReaderFactoryClosure() { - function DOMCMapReaderFactory(params) { - this.baseUrl = params.baseUrl || null; - this.isCompressed = params.isCompressed || false; + destroy(canvasAndContext) { + (0, _util.assert)(canvasAndContext.canvas, 'canvas is not specified'); + canvasAndContext.canvas.width = 0; + canvasAndContext.canvas.height = 0; + canvasAndContext.canvas = null; + canvasAndContext.context = null; } - DOMCMapReaderFactory.prototype = { - fetch(params) { - var name = params.name; - if (!name) { - return Promise.reject(new Error('CMap name must be specified.')); +} +class DOMCMapReaderFactory { + constructor({ baseUrl = null, isCompressed = false }) { + this.baseUrl = baseUrl; + this.isCompressed = isCompressed; + } + fetch({ name }) { + if (!name) { + return Promise.reject(new Error('CMap name must be specified.')); + } + return new Promise((resolve, reject) => { + let url = this.baseUrl + name + (this.isCompressed ? '.bcmap' : ''); + let request = new XMLHttpRequest(); + request.open('GET', url, true); + if (this.isCompressed) { + request.responseType = 'arraybuffer'; } - return new Promise((resolve, reject) => { - var url = this.baseUrl + name + (this.isCompressed ? '.bcmap' : ''); - var request = new XMLHttpRequest(); - request.open('GET', url, true); - if (this.isCompressed) { - request.responseType = 'arraybuffer'; + request.onreadystatechange = () => { + if (request.readyState !== XMLHttpRequest.DONE) { + return; } - request.onreadystatechange = () => { - if (request.readyState !== XMLHttpRequest.DONE) { + if (request.status === 200 || request.status === 0) { + let data; + if (this.isCompressed && request.response) { + data = new Uint8Array(request.response); + } else if (!this.isCompressed && request.responseText) { + data = (0, _util.stringToBytes)(request.responseText); + } + if (data) { + resolve({ + cMapData: data, + compressionType: this.isCompressed ? _util.CMapCompressionType.BINARY : _util.CMapCompressionType.NONE + }); return; } - if (request.status === 200 || request.status === 0) { - var data; - if (this.isCompressed && request.response) { - data = new Uint8Array(request.response); - } else if (!this.isCompressed && request.responseText) { - data = (0, _util.stringToBytes)(request.responseText); - } - if (data) { - resolve({ - cMapData: data, - compressionType: this.isCompressed ? _util.CMapCompressionType.BINARY : _util.CMapCompressionType.NONE - }); - return; - } - } - reject(new Error('Unable to load ' + (this.isCompressed ? 'binary ' : '') + 'CMap at: ' + url)); - }; - request.send(null); - }); - } - }; - return DOMCMapReaderFactory; -}(); + } + reject(new Error('Unable to load ' + (this.isCompressed ? 'binary ' : '') + 'CMap at: ' + url)); + }; + request.send(null); + }); + } +} var CustomStyle = function CustomStyleClosure() { var prefixes = ['ms', 'Moz', 'Webkit', 'O']; var _cache = Object.create(null); @@ -3328,7 +3323,12 @@ var InternalRenderTask = function InternalRenderTaskClosure() { } var params = this.params; this.gfx = new _canvas.CanvasGraphics(params.canvasContext, this.commonObjs, this.objs, this.canvasFactory, params.imageLayer); - this.gfx.beginDrawing(params.transform, params.viewport, transparency); + this.gfx.beginDrawing({ + transform: params.transform, + viewport: params.viewport, + transparency, + background: params.background + }); this.operatorListIdx = 0; this.graphicsReady = true; if (this.graphicsReadyCallback) { @@ -3405,8 +3405,8 @@ var _UnsupportedManager = function UnsupportedManagerClosure() { }(); var version, build; { - exports.version = version = '1.8.346'; - exports.build = build = '15425d5b'; + exports.version = version = '1.8.363'; + exports.build = build = '658fb03d'; } exports.getDocument = getDocument; exports.LoopbackPort = LoopbackPort; @@ -4408,8 +4408,8 @@ if (!_util.globalScope.PDFJS) { } var PDFJS = _util.globalScope.PDFJS; { - PDFJS.version = '1.8.346'; - PDFJS.build = '15425d5b'; + PDFJS.version = '1.8.363'; + PDFJS.build = '658fb03d'; } PDFJS.pdfBug = false; if (PDFJS.verbosity !== undefined) { @@ -5060,11 +5060,11 @@ var CanvasGraphics = function CanvasGraphicsClosure() { var NORMAL_CLIP = {}; var EO_CLIP = {}; CanvasGraphics.prototype = { - beginDrawing: function CanvasGraphics_beginDrawing(transform, viewport, transparency) { + beginDrawing({ transform, viewport, transparency, background = null }) { var width = this.ctx.canvas.width; var height = this.ctx.canvas.height; this.ctx.save(); - this.ctx.fillStyle = 'rgb(255, 255, 255)'; + this.ctx.fillStyle = background || 'rgb(255, 255, 255)'; this.ctx.fillRect(0, 0, width, height); this.ctx.restore(); if (transparency) { @@ -6723,8 +6723,8 @@ exports.TilingPattern = TilingPattern; "use strict"; -var pdfjsVersion = '1.8.346'; -var pdfjsBuild = '15425d5b'; +var pdfjsVersion = '1.8.363'; +var pdfjsBuild = '658fb03d'; var pdfjsSharedUtil = __w_pdfjs_require__(0); var pdfjsDisplayGlobal = __w_pdfjs_require__(8); var pdfjsDisplayAPI = __w_pdfjs_require__(3); diff --git a/browser/extensions/pdfjs/content/build/pdf.worker.js b/browser/extensions/pdfjs/content/build/pdf.worker.js index b91d11f24b44..18e5ca6e69ec 100644 --- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -16738,6 +16738,12 @@ var PartialEvaluator = function PartialEvaluatorClosure() { if (nativeImageDecoderSupport !== NativeImageDecoding.NONE && !softMask && !mask && image instanceof JpegStream && NativeImageDecoder.isSupported(image, this.xref, resources)) { operatorList.addOp(OPS.paintJpegXObject, args); this.handler.send('obj', [objId, this.pageIndex, 'JpegStream', image.getIR(this.options.forceDataSchema)]); + if (cacheKey) { + imageCache[cacheKey] = { + fn: OPS.paintJpegXObject, + args + }; + } return; } var nativeImageDecoder = null; @@ -36639,8 +36645,8 @@ exports.Type1Parser = Type1Parser; "use strict"; -var pdfjsVersion = '1.8.346'; -var pdfjsBuild = '15425d5b'; +var pdfjsVersion = '1.8.363'; +var pdfjsBuild = '658fb03d'; var pdfjsCoreWorker = __w_pdfjs_require__(17); ; exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler; diff --git a/browser/extensions/pdfjs/content/pdfjschildbootstrap-enabled.js b/browser/extensions/pdfjs/content/pdfjschildbootstrap-enabled.js index 93a7489213cc..dda7a7c1168a 100644 --- a/browser/extensions/pdfjs/content/pdfjschildbootstrap-enabled.js +++ b/browser/extensions/pdfjs/content/pdfjschildbootstrap-enabled.js @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals Components, PdfJs, Services */ "use strict"; diff --git a/browser/extensions/pdfjs/content/pdfjschildbootstrap.js b/browser/extensions/pdfjs/content/pdfjschildbootstrap.js index d70c15ed5491..1d30acdd1a9f 100644 --- a/browser/extensions/pdfjs/content/pdfjschildbootstrap.js +++ b/browser/extensions/pdfjs/content/pdfjschildbootstrap.js @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals Components, PdfjsContentUtils */ "use strict"; diff --git a/browser/extensions/pdfjs/content/web/viewer.js b/browser/extensions/pdfjs/content/web/viewer.js index a2e3fdc57e17..5d9c17a202c8 100644 --- a/browser/extensions/pdfjs/content/web/viewer.js +++ b/browser/extensions/pdfjs/content/web/viewer.js @@ -4852,16 +4852,15 @@ var PDFPageView = function PDFPageViewClosure() { getPagePoint: function PDFPageView_getPagePoint(x, y) { return this.viewport.convertToPdfPoint(x, y); }, - draw: function PDFPageView_draw() { + draw() { if (this.renderingState !== _pdf_rendering_queue.RenderingStates.INITIAL) { console.error('Must be in new state before drawing'); this.reset(); } this.renderingState = _pdf_rendering_queue.RenderingStates.RUNNING; - var self = this; - var pdfPage = this.pdfPage; - var div = this.div; - var canvasWrapper = document.createElement('div'); + let pdfPage = this.pdfPage; + let div = this.div; + let canvasWrapper = document.createElement('div'); canvasWrapper.style.width = div.style.width; canvasWrapper.style.height = div.style.height; canvasWrapper.classList.add('canvasWrapper'); @@ -4870,10 +4869,9 @@ var PDFPageView = function PDFPageViewClosure() { } else { div.appendChild(canvasWrapper); } - var textLayerDiv = null; - var textLayer = null; + let textLayer = null; if (this.textLayerFactory) { - textLayerDiv = document.createElement('div'); + let textLayerDiv = document.createElement('div'); textLayerDiv.className = 'textLayer'; textLayerDiv.style.width = canvasWrapper.style.width; textLayerDiv.style.height = canvasWrapper.style.height; @@ -4885,13 +4883,13 @@ var PDFPageView = function PDFPageViewClosure() { textLayer = this.textLayerFactory.createTextLayerBuilder(textLayerDiv, this.id - 1, this.viewport, this.enhanceTextSelection); } this.textLayer = textLayer; - var renderContinueCallback = null; + let renderContinueCallback = null; if (this.renderingQueue) { - renderContinueCallback = function renderContinueCallback(cont) { - if (!self.renderingQueue.isHighestPriority(self)) { - self.renderingState = _pdf_rendering_queue.RenderingStates.PAUSED; - self.resume = function resumeCallback() { - self.renderingState = _pdf_rendering_queue.RenderingStates.RUNNING; + renderContinueCallback = cont => { + if (!this.renderingQueue.isHighestPriority(this)) { + this.renderingState = _pdf_rendering_queue.RenderingStates.PAUSED; + this.resume = () => { + this.renderingState = _pdf_rendering_queue.RenderingStates.RUNNING; cont(); }; return; @@ -4899,28 +4897,28 @@ var PDFPageView = function PDFPageViewClosure() { cont(); }; } - var finishPaintTask = function finishPaintTask(error) { - if (paintTask === self.paintTask) { - self.paintTask = null; + let finishPaintTask = error => { + if (paintTask === this.paintTask) { + this.paintTask = null; } if (error instanceof _pdfjs.RenderingCancelledException) { - self.error = null; + this.error = null; return Promise.resolve(undefined); } - self.renderingState = _pdf_rendering_queue.RenderingStates.FINISHED; - if (self.loadingIconDiv) { - div.removeChild(self.loadingIconDiv); - delete self.loadingIconDiv; + this.renderingState = _pdf_rendering_queue.RenderingStates.FINISHED; + if (this.loadingIconDiv) { + div.removeChild(this.loadingIconDiv); + delete this.loadingIconDiv; } - self._resetZoomLayer(true); - self.error = error; - self.stats = pdfPage.stats; - if (self.onAfterDraw) { - self.onAfterDraw(); + this._resetZoomLayer(true); + this.error = error; + this.stats = pdfPage.stats; + if (this.onAfterDraw) { + this.onAfterDraw(); } - self.eventBus.dispatch('pagerendered', { - source: self, - pageNumber: self.id, + this.eventBus.dispatch('pagerendered', { + source: this, + pageNumber: this.id, cssTransform: false }); if (error) { @@ -4928,10 +4926,10 @@ var PDFPageView = function PDFPageViewClosure() { } return Promise.resolve(undefined); }; - var paintTask = this.renderer === _ui_utils.RendererType.SVG ? this.paintOnSvg(canvasWrapper) : this.paintOnCanvas(canvasWrapper); + let paintTask = this.renderer === _ui_utils.RendererType.SVG ? this.paintOnSvg(canvasWrapper) : this.paintOnCanvas(canvasWrapper); paintTask.onRenderContinue = renderContinueCallback; this.paintTask = paintTask; - var resultPromise = paintTask.promise.then(function () { + let resultPromise = paintTask.promise.then(function () { return finishPaintTask(null).then(function () { if (textLayer) { pdfPage.getTextContent({ normalizeWhitespace: true }).then(function textContentResolved(textContent) { @@ -5024,16 +5022,16 @@ var PDFPageView = function PDFPageViewClosure() { cont(); } }; - renderTask.promise.then(function pdfPageRenderCallback() { + renderTask.promise.then(function () { showCanvas(); renderCapability.resolve(undefined); - }, function pdfPageRenderError(error) { + }, function (error) { showCanvas(); renderCapability.reject(error); }); return result; }, - paintOnSvg: function PDFPageView_paintOnSvg(wrapper) { + paintOnSvg(wrapper) { return { promise: Promise.reject(new Error('SVG rendering is not supported.')), onRenderContinue(cont) {}, @@ -5845,53 +5843,52 @@ var PDFThumbnailView = function PDFThumbnailViewClosure() { this.canvas.height = 0; delete this.canvas; }, - draw: function PDFThumbnailView_draw() { + draw() { if (this.renderingState !== _pdf_rendering_queue.RenderingStates.INITIAL) { console.error('Must be in new state before drawing'); return Promise.resolve(undefined); } this.renderingState = _pdf_rendering_queue.RenderingStates.RUNNING; - var renderCapability = (0, _pdfjs.createPromiseCapability)(); - var self = this; - function thumbnailDrawCallback(error) { - if (renderTask === self.renderTask) { - self.renderTask = null; + let renderCapability = (0, _pdfjs.createPromiseCapability)(); + let finishRenderTask = error => { + if (renderTask === this.renderTask) { + this.renderTask = null; } if (error instanceof _pdfjs.RenderingCancelledException) { renderCapability.resolve(undefined); return; } - self.renderingState = _pdf_rendering_queue.RenderingStates.FINISHED; - self._convertCanvasToImage(); + this.renderingState = _pdf_rendering_queue.RenderingStates.FINISHED; + this._convertCanvasToImage(); if (!error) { renderCapability.resolve(undefined); } else { renderCapability.reject(error); } - } - var ctx = this._getPageDrawContext(); - var drawViewport = this.viewport.clone({ scale: this.scale }); - var renderContinueCallback = function renderContinueCallback(cont) { - if (!self.renderingQueue.isHighestPriority(self)) { - self.renderingState = _pdf_rendering_queue.RenderingStates.PAUSED; - self.resume = function resumeCallback() { - self.renderingState = _pdf_rendering_queue.RenderingStates.RUNNING; + }; + let ctx = this._getPageDrawContext(); + let drawViewport = this.viewport.clone({ scale: this.scale }); + let renderContinueCallback = cont => { + if (!this.renderingQueue.isHighestPriority(this)) { + this.renderingState = _pdf_rendering_queue.RenderingStates.PAUSED; + this.resume = () => { + this.renderingState = _pdf_rendering_queue.RenderingStates.RUNNING; cont(); }; return; } cont(); }; - var renderContext = { + let renderContext = { canvasContext: ctx, viewport: drawViewport }; - var renderTask = this.renderTask = this.pdfPage.render(renderContext); + let renderTask = this.renderTask = this.pdfPage.render(renderContext); renderTask.onContinue = renderContinueCallback; - renderTask.promise.then(function pdfPageRenderCallback() { - thumbnailDrawCallback(null); - }, function pdfPageRenderError(error) { - thumbnailDrawCallback(error); + renderTask.promise.then(function () { + finishRenderTask(null); + }, function (error) { + finishRenderTask(error); }); return renderCapability.promise; }, From 9bf70967d92742545bc31988b0e4438948a41149 Mon Sep 17 00:00:00 2001 From: Jordan Lund Date: Wed, 17 May 2017 17:24:01 -0700 Subject: [PATCH 28/35] Bug 1365588 - fix l10n repacks for DevEdition on Beta, gecko, DONTBUILD, r=rail MozReview-Commit-ID: 1arksrFhsv9 --HG-- rename : browser/config/mozconfigs/linux32/l10n-mozconfig => browser/config/mozconfigs/linux32/l10n-mozconfig-devedition rename : browser/config/mozconfigs/linux64/l10n-mozconfig => browser/config/mozconfigs/linux64/l10n-mozconfig-devedition rename : browser/config/mozconfigs/macosx64/l10n-mozconfig => browser/config/mozconfigs/macosx64/l10n-mozconfig-devedition rename : browser/config/mozconfigs/win32/l10n-mozconfig => browser/config/mozconfigs/win32/l10n-mozconfig-devedition rename : browser/config/mozconfigs/win64/l10n-mozconfig => browser/config/mozconfigs/win64/l10n-mozconfig-devedition rename : testing/mozharness/configs/single_locale/dev-mozilla-beta.py => testing/mozharness/configs/single_locale/dev-mozilla-beta_devedition.py rename : testing/mozharness/configs/single_locale/linux64.py => testing/mozharness/configs/single_locale/linux64_devedition.py rename : testing/mozharness/configs/single_locale/linux.py => testing/mozharness/configs/single_locale/linux_devedition.py rename : testing/mozharness/configs/single_locale/macosx64.py => testing/mozharness/configs/single_locale/macosx64_devedition.py rename : testing/mozharness/configs/single_locale/mozilla-beta.py => testing/mozharness/configs/single_locale/mozilla-beta_devedition.py rename : testing/mozharness/configs/single_locale/win32.py => testing/mozharness/configs/single_locale/win32_devedition.py rename : testing/mozharness/configs/single_locale/win64.py => testing/mozharness/configs/single_locale/win64_devedition.py extra : rebase_source : c1242ce513624d519756d1628896f3343a047d00 extra : amend_source : f434761fe2f88cfc4af97229d5bdbe07becf75a2 extra : source : 9cee0e8b00d28d7e977df6ee3e1adc9680004b8d --- .../linux32/l10n-mozconfig-devedition | 20 +++ .../linux64/l10n-mozconfig-devedition | 20 +++ .../macosx64/l10n-mozconfig-devedition | 22 ++++ .../win32/l10n-mozconfig-devedition | 19 +++ .../win64/l10n-mozconfig-devedition | 20 +++ .../dev-mozilla-beta_devedition.py | 37 ++++++ .../single_locale/linux32_devedition.py | 1 + .../single_locale/linux64_devedition.py | 104 +++++++++++++++ .../configs/single_locale/linux_devedition.py | 124 ++++++++++++++++++ .../single_locale/macosx64_devedition.py | 72 ++++++++++ .../single_locale/mozilla-beta_devedition.py | 37 ++++++ .../configs/single_locale/win32_devedition.py | 76 +++++++++++ .../configs/single_locale/win64_devedition.py | 76 +++++++++++ 13 files changed, 628 insertions(+) create mode 100644 browser/config/mozconfigs/linux32/l10n-mozconfig-devedition create mode 100644 browser/config/mozconfigs/linux64/l10n-mozconfig-devedition create mode 100644 browser/config/mozconfigs/macosx64/l10n-mozconfig-devedition create mode 100644 browser/config/mozconfigs/win32/l10n-mozconfig-devedition create mode 100644 browser/config/mozconfigs/win64/l10n-mozconfig-devedition create mode 100644 testing/mozharness/configs/single_locale/dev-mozilla-beta_devedition.py create mode 120000 testing/mozharness/configs/single_locale/linux32_devedition.py create mode 100644 testing/mozharness/configs/single_locale/linux64_devedition.py create mode 100644 testing/mozharness/configs/single_locale/linux_devedition.py create mode 100644 testing/mozharness/configs/single_locale/macosx64_devedition.py create mode 100644 testing/mozharness/configs/single_locale/mozilla-beta_devedition.py create mode 100644 testing/mozharness/configs/single_locale/win32_devedition.py create mode 100644 testing/mozharness/configs/single_locale/win64_devedition.py diff --git a/browser/config/mozconfigs/linux32/l10n-mozconfig-devedition b/browser/config/mozconfigs/linux32/l10n-mozconfig-devedition new file mode 100644 index 000000000000..c424d85cbac9 --- /dev/null +++ b/browser/config/mozconfigs/linux32/l10n-mozconfig-devedition @@ -0,0 +1,20 @@ +no_sccache=1 + +ac_add_options --with-l10n-base=../../l10n +ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} +ac_add_options --with-branding=browser/branding/aurora + +. $topsrcdir/build/unix/mozconfig.linux32 + +export MOZILLA_OFFICIAL=1 + +# Enable Telemetry +export MOZ_TELEMETRY_REPORTING=1 + +ac_add_options --disable-stdcxx-compat + +# Don't autoclobber l10n, as this can lead to missing binaries and broken builds +# Bug 1283438 +mk_add_options AUTOCLOBBER= + +. "$topsrcdir/build/mozconfig.common.override" diff --git a/browser/config/mozconfigs/linux64/l10n-mozconfig-devedition b/browser/config/mozconfigs/linux64/l10n-mozconfig-devedition new file mode 100644 index 000000000000..5840790c36a4 --- /dev/null +++ b/browser/config/mozconfigs/linux64/l10n-mozconfig-devedition @@ -0,0 +1,20 @@ +no_sccache=1 + +ac_add_options --with-l10n-base=../../l10n +ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} +ac_add_options --with-branding=browser/branding/aurora + +. $topsrcdir/build/unix/mozconfig.linux + +export MOZILLA_OFFICIAL=1 + +# Enable Telemetry +export MOZ_TELEMETRY_REPORTING=1 + +ac_add_options --disable-stdcxx-compat + +# Don't autoclobber l10n, as this can lead to missing binaries and broken builds +# Bug 1283438 +mk_add_options AUTOCLOBBER= + +. "$topsrcdir/build/mozconfig.common.override" diff --git a/browser/config/mozconfigs/macosx64/l10n-mozconfig-devedition b/browser/config/mozconfigs/macosx64/l10n-mozconfig-devedition new file mode 100644 index 000000000000..f0675932bd94 --- /dev/null +++ b/browser/config/mozconfigs/macosx64/l10n-mozconfig-devedition @@ -0,0 +1,22 @@ +. "$topsrcdir/browser/config/mozconfigs/common" +. "$topsrcdir/build/macosx/mozconfig.common" + +ac_add_options --with-l10n-base=../../l10n +ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} +ac_add_options --with-branding=browser/branding/aurora + +if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then +ac_add_options --with-macbundlename-prefix=Firefox +fi + +export MOZILLA_OFFICIAL=1 + +# Enable Telemetry +export MOZ_TELEMETRY_REPORTING=1 + +# Don't autoclobber l10n, as this can lead to missing binaries and broken builds +# Bug 1283438 +mk_add_options AUTOCLOBBER= + +. "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" diff --git a/browser/config/mozconfigs/win32/l10n-mozconfig-devedition b/browser/config/mozconfigs/win32/l10n-mozconfig-devedition new file mode 100644 index 000000000000..5542dbbc031c --- /dev/null +++ b/browser/config/mozconfigs/win32/l10n-mozconfig-devedition @@ -0,0 +1,19 @@ +. "$topsrcdir/browser/config/mozconfigs/common" + +ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} +ac_add_options --with-l10n-base=../../l10n +ac_add_options --with-windows-version=603 +ac_add_options --with-branding=browser/branding/aurora + +export MOZILLA_OFFICIAL=1 + +# Enable Telemetry +export MOZ_TELEMETRY_REPORTING=1 + +# Don't autoclobber l10n, as this can lead to missing binaries and broken builds +# Bug 1283438 +mk_add_options AUTOCLOBBER= + +. $topsrcdir/build/win32/mozconfig.vs-latest + +. "$topsrcdir/build/mozconfig.common.override" diff --git a/browser/config/mozconfigs/win64/l10n-mozconfig-devedition b/browser/config/mozconfigs/win64/l10n-mozconfig-devedition new file mode 100644 index 000000000000..03a0365a0c8f --- /dev/null +++ b/browser/config/mozconfigs/win64/l10n-mozconfig-devedition @@ -0,0 +1,20 @@ +. "$topsrcdir/browser/config/mozconfigs/common" +. "$topsrcdir/browser/config/mozconfigs/win64/common-win64" + +ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} +ac_add_options --with-l10n-base=../../l10n +ac_add_options --with-windows-version=603 +ac_add_options --with-branding=browser/branding/aurora + +export MOZILLA_OFFICIAL=1 + +# Enable Telemetry +export MOZ_TELEMETRY_REPORTING=1 + +# Don't autoclobber l10n, as this can lead to missing binaries and broken builds +# Bug 1283438 +mk_add_options AUTOCLOBBER= + +. $topsrcdir/build/win64/mozconfig.vs-latest + +. "$topsrcdir/build/mozconfig.common.override" diff --git a/testing/mozharness/configs/single_locale/dev-mozilla-beta_devedition.py b/testing/mozharness/configs/single_locale/dev-mozilla-beta_devedition.py new file mode 100644 index 000000000000..56e133f48f46 --- /dev/null +++ b/testing/mozharness/configs/single_locale/dev-mozilla-beta_devedition.py @@ -0,0 +1,37 @@ +config = { + "branch": "date", + "nightly_build": True, + "update_channel": "aurora-dev", # devedition uses aurora based branding + + # l10n + "hg_l10n_base": "https://hg.mozilla.org/releases/l10n/mozilla-beta", + + # repositories + # staging beta dev releases use date repo for now + "mozilla_dir": "date", + "repos": [{ + "vcs": "hg", + "repo": "https://hg.mozilla.org/build/tools", + "branch": "default", + "dest": "tools", + }, { + "vcs": "hg", + "repo": "https://hg.mozilla.org/projects/date", + "branch": "%(revision)s", + "dest": "date", + "clone_upstream_url": "https://hg.mozilla.org/mozilla-unified", + }], + # purge options + 'is_automation': True, + 'purge_minsize': 12, + 'default_actions': [ + "clobber", + "pull", + "clone-locales", + "list-locales", + "setup", + "repack", + "taskcluster-upload", + "summary", + ], +} diff --git a/testing/mozharness/configs/single_locale/linux32_devedition.py b/testing/mozharness/configs/single_locale/linux32_devedition.py new file mode 120000 index 000000000000..b308e6feb977 --- /dev/null +++ b/testing/mozharness/configs/single_locale/linux32_devedition.py @@ -0,0 +1 @@ +linux_devedition.py \ No newline at end of file diff --git a/testing/mozharness/configs/single_locale/linux64_devedition.py b/testing/mozharness/configs/single_locale/linux64_devedition.py new file mode 100644 index 000000000000..7a13602f2be1 --- /dev/null +++ b/testing/mozharness/configs/single_locale/linux64_devedition.py @@ -0,0 +1,104 @@ +import os + +config = { + "platform": "linux64", + "stage_product": "firefox", + "update_platform": "Linux_x86_64-gcc3", + "mozconfig": "%(branch)s/browser/config/mozconfigs/linux64/l10n-mozconfig-devedition", + "bootstrap_env": { + "MOZ_OBJDIR": "obj-l10n", + "EN_US_BINARY_URL": "%(en_us_binary_url)s", + "LOCALE_MERGEDIR": "%(abs_merge_dir)s/", + "MOZ_UPDATE_CHANNEL": "%(update_channel)s", + "DIST": "%(abs_objdir)s", + "LOCALE_MERGEDIR": "%(abs_merge_dir)s/", + "L10NBASEDIR": "../../l10n", + "MOZ_MAKE_COMPLETE_MAR": "1", + 'TOOLTOOL_CACHE': '/builds/tooltool_cache', + 'TOOLTOOL_HOME': '/builds', + 'EN_US_PACKAGE_NAME': 'target.tar.bz2', + }, + "ssh_key_dir": "/home/mock_mozilla/.ssh", + "log_name": "single_locale", + "objdir": "obj-l10n", + "js_src_dir": "js/src", + "vcs_share_base": "/builds/hg-shared", + + # tooltool + 'tooltool_url': 'https://api.pub.build.mozilla.org/tooltool/', + 'tooltool_script': ["/builds/tooltool.py"], + 'tooltool_bootstrap': "setup.sh", + 'tooltool_manifest_src': 'browser/config/tooltool-manifests/linux64/releng.manifest', + # balrog credential file: + 'balrog_credentials_file': 'oauth.txt', + + # l10n + "ignore_locales": ["en-US", "ja-JP-mac"], + "l10n_dir": "l10n", + "locales_file": "%(branch)s/browser/locales/all-locales", + "locales_dir": "browser/locales", + "hg_l10n_tag": "default", + "merge_locales": True, + + # MAR + "previous_mar_dir": "dist/previous", + "current_mar_dir": "dist/current", + "update_mar_dir": "dist/update", # sure? + "previous_mar_filename": "previous.mar", + "current_work_mar_dir": "current.work", + "package_base_dir": "dist/l10n-stage", + "application_ini": "application.ini", + "buildid_section": 'App', + "buildid_option": "BuildID", + "unpack_script": "tools/update-packaging/unwrap_full_update.pl", + "incremental_update_script": "tools/update-packaging/make_incremental_update.sh", + "balrog_release_pusher_script": "scripts/updates/balrog-release-pusher.py", + "update_packaging_dir": "tools/update-packaging", + "local_mar_tool_dir": "dist/host/bin", + "mar": "mar", + "mbsdiff": "mbsdiff", + "current_mar_filename": "firefox-%(version)s.%(locale)s.linux-x86_64.complete.mar", + "complete_mar": "firefox-%(version)s.en-US.linux-x86_64.complete.mar", + "localized_mar": "firefox-%(version)s.%(locale)s.linux-x86_64.complete.mar", + "partial_mar": "firefox-%(version)s.%(locale)s.linux-x86_64.partial.%(from_buildid)s-%(to_buildid)s.mar", + "installer_file": "firefox-%(version)s.en-US.linux-x86_64.tar.bz2", + + # Mock + 'mock_target': 'mozilla-centos6-x86_64', + + 'mock_packages': [ + 'autoconf213', 'python', 'mozilla-python27', 'zip', 'mozilla-python27-mercurial', + 'git', 'ccache', 'perl-Test-Simple', 'perl-Config-General', + 'yasm', 'wget', + 'mpfr', # required for system compiler + 'xorg-x11-font*', # fonts required for PGO + 'imake', # required for makedepend!?! + ### <-- from releng repo + 'gcc45_0moz3', 'gcc454_0moz1', 'gcc472_0moz1', 'gcc473_0moz1', + 'yasm', 'ccache', + ### + 'valgrind', 'dbus-x11', + ######## 64 bit specific ########### + 'glibc-static', 'libstdc++-static', + 'gtk2-devel', 'libnotify-devel', + 'alsa-lib-devel', 'libcurl-devel', 'wireless-tools-devel', + 'libX11-devel', 'libXt-devel', 'mesa-libGL-devel', 'gnome-vfs2-devel', + 'GConf2-devel', + ### from releng repo + 'gcc45_0moz3', 'gcc454_0moz1', 'gcc472_0moz1', 'gcc473_0moz1', + 'yasm', 'ccache', + ### + 'pulseaudio-libs-devel', 'gstreamer-devel', + 'gstreamer-plugins-base-devel', 'freetype-2.3.11-6.el6_1.8.x86_64', + 'freetype-devel-2.3.11-6.el6_1.8.x86_64' + ], + 'mock_files': [ + ('/home/cltbld/.ssh', '/home/mock_mozilla/.ssh'), + ('/home/cltbld/.hgrc', '/builds/.hgrc'), + ('/home/cltbld/.boto', '/builds/.boto'), + ('/builds/gapi.data', '/builds/gapi.data'), + ('/builds/relengapi.tok', '/builds/relengapi.tok'), + ('/tools/tooltool.py', '/builds/tooltool.py'), + ('/usr/local/lib/hgext', '/usr/local/lib/hgext'), + ], +} diff --git a/testing/mozharness/configs/single_locale/linux_devedition.py b/testing/mozharness/configs/single_locale/linux_devedition.py new file mode 100644 index 000000000000..9717294624be --- /dev/null +++ b/testing/mozharness/configs/single_locale/linux_devedition.py @@ -0,0 +1,124 @@ +import os + +config = { + "platform": "linux", + "stage_product": "firefox", + "update_platform": "Linux_x86-gcc3", + "mozconfig": "%(branch)s/browser/config/mozconfigs/linux32/l10n-mozconfig-devedition", + "bootstrap_env": { + "MOZ_OBJDIR": "obj-l10n", + "EN_US_BINARY_URL": "%(en_us_binary_url)s", + "LOCALE_MERGEDIR": "%(abs_merge_dir)s/", + "MOZ_UPDATE_CHANNEL": "%(update_channel)s", + "DIST": "%(abs_objdir)s", + "LOCALE_MERGEDIR": "%(abs_merge_dir)s/", + "L10NBASEDIR": "../../l10n", + "MOZ_MAKE_COMPLETE_MAR": "1", + 'TOOLTOOL_CACHE': '/builds/tooltool_cache', + 'TOOLTOOL_HOME': '/builds', + 'EN_US_PACKAGE_NAME': 'target.tar.bz2', + }, + "ssh_key_dir": "/home/mock_mozilla/.ssh", + "log_name": "single_locale", + "objdir": "obj-l10n", + "js_src_dir": "js/src", + "vcs_share_base": "/builds/hg-shared", + + # tooltool + 'tooltool_url': 'https://api.pub.build.mozilla.org/tooltool/', + 'tooltool_script': ["/builds/tooltool.py"], + 'tooltool_bootstrap': "setup.sh", + 'tooltool_manifest_src': 'browser/config/tooltool-manifests/linux32/releng.manifest', + # balrog credential file: + 'balrog_credentials_file': 'oauth.txt', + + # l10n + "ignore_locales": ["en-US", "ja-JP-mac"], + "l10n_dir": "l10n", + "locales_file": "%(branch)s/browser/locales/all-locales", + "locales_dir": "browser/locales", + "hg_l10n_tag": "default", + "merge_locales": True, + + # MAR + "previous_mar_dir": "dist/previous", + "current_mar_dir": "dist/current", + "update_mar_dir": "dist/update", # sure? + "previous_mar_filename": "previous.mar", + "current_work_mar_dir": "current.work", + "package_base_dir": "dist/l10n-stage", + "application_ini": "application.ini", + "buildid_section": 'App', + "buildid_option": "BuildID", + "unpack_script": "tools/update-packaging/unwrap_full_update.pl", + "incremental_update_script": "tools/update-packaging/make_incremental_update.sh", + "balrog_release_pusher_script": "scripts/updates/balrog-release-pusher.py", + "update_packaging_dir": "tools/update-packaging", + "local_mar_tool_dir": "dist/host/bin", + "mar": "mar", + "mbsdiff": "mbsdiff", + "current_mar_filename": "firefox-%(version)s.%(locale)s.linux-i686.complete.mar", + "complete_mar": "firefox-%(version)s.en-US.linux-i686.complete.mar", + "localized_mar": "firefox-%(version)s.%(locale)s.linux-i686.complete.mar", + "partial_mar": "firefox-%(version)s.%(locale)s.linux-i686.partial.%(from_buildid)s-%(to_buildid)s.mar", + 'installer_file': "firefox-%(version)s.en-US.linux-i686.tar.bz2", + + # Mock + 'mock_target': 'mozilla-centos6-x86_64', + 'mock_packages': [ + 'autoconf213', 'python', 'mozilla-python27', 'zip', 'mozilla-python27-mercurial', + 'git', 'ccache', 'perl-Test-Simple', 'perl-Config-General', + 'yasm', 'wget', + 'mpfr', # required for system compiler + 'xorg-x11-font*', # fonts required for PGO + 'imake', # required for makedepend!?! + ### <-- from releng repo + 'gcc45_0moz3', 'gcc454_0moz1', 'gcc472_0moz1', 'gcc473_0moz1', + 'yasm', 'ccache', + ### + 'valgrind', + ######## 32 bit specific ########### + 'glibc-static.i686', 'libstdc++-static.i686', + 'gtk2-devel.i686', 'libnotify-devel.i686', + 'alsa-lib-devel.i686', 'libcurl-devel.i686', + 'wireless-tools-devel.i686', 'libX11-devel.i686', + 'libXt-devel.i686', 'mesa-libGL-devel.i686', + 'gnome-vfs2-devel.i686', 'GConf2-devel.i686', + 'pulseaudio-libs-devel.i686', + 'gstreamer-devel.i686', 'gstreamer-plugins-base-devel.i686', + # Packages already installed in the mock environment, as x86_64 + # packages. + 'glibc-devel.i686', 'libgcc.i686', 'libstdc++-devel.i686', + # yum likes to install .x86_64 -devel packages that satisfy .i686 + # -devel packages dependencies. So manually install the dependencies + # of the above packages. + 'ORBit2-devel.i686', 'atk-devel.i686', 'cairo-devel.i686', + 'check-devel.i686', 'dbus-devel.i686', 'dbus-glib-devel.i686', + 'fontconfig-devel.i686', 'glib2-devel.i686', + 'hal-devel.i686', 'libICE-devel.i686', 'libIDL-devel.i686', + 'libSM-devel.i686', 'libXau-devel.i686', 'libXcomposite-devel.i686', + 'libXcursor-devel.i686', 'libXdamage-devel.i686', + 'libXdmcp-devel.i686', 'libXext-devel.i686', 'libXfixes-devel.i686', + 'libXft-devel.i686', 'libXi-devel.i686', 'libXinerama-devel.i686', + 'libXrandr-devel.i686', 'libXrender-devel.i686', + 'libXxf86vm-devel.i686', 'libdrm-devel.i686', 'libidn-devel.i686', + 'libpng-devel.i686', 'libxcb-devel.i686', 'libxml2-devel.i686', + 'pango-devel.i686', 'perl-devel.i686', 'pixman-devel.i686', + 'zlib-devel.i686', + # Freetype packages need to be installed be version, because a newer + # version is available, but we don't want it for Firefox builds. + 'freetype-2.3.11-6.el6_1.8.i686', + 'freetype-devel-2.3.11-6.el6_1.8.i686', + 'freetype-2.3.11-6.el6_1.8.x86_64', + ######## 32 bit specific ########### + ], + 'mock_files': [ + ('/home/cltbld/.ssh', '/home/mock_mozilla/.ssh'), + ('/home/cltbld/.hgrc', '/builds/.hgrc'), + ('/home/cltbld/.boto', '/builds/.boto'), + ('/builds/gapi.data', '/builds/gapi.data'), + ('/builds/relengapi.tok', '/builds/relengapi.tok'), + ('/tools/tooltool.py', '/builds/tooltool.py'), + ('/usr/local/lib/hgext', '/usr/local/lib/hgext'), + ], +} diff --git a/testing/mozharness/configs/single_locale/macosx64_devedition.py b/testing/mozharness/configs/single_locale/macosx64_devedition.py new file mode 100644 index 000000000000..4e59e6b1c8cb --- /dev/null +++ b/testing/mozharness/configs/single_locale/macosx64_devedition.py @@ -0,0 +1,72 @@ +import os + +config = { + # mozconfig file to use, it depends on branch and platform names + "platform": "macosx64", + "stage_product": "firefox", + "update_platform": "Darwin_x86_64-gcc3", + "mozconfig": "%(branch)s/browser/config/mozconfigs/macosx64/l10n-mozconfig-devedition", + "bootstrap_env": { + "SHELL": '/bin/bash', + "MOZ_OBJDIR": "obj-l10n", + "EN_US_BINARY_URL": "%(en_us_binary_url)s", + "MOZ_UPDATE_CHANNEL": "%(update_channel)s", + "MOZ_PKG_PLATFORM": "mac", + # "IS_NIGHTLY": "yes", + "DIST": "%(abs_objdir)s", + "LOCALE_MERGEDIR": "%(abs_merge_dir)s/", + "L10NBASEDIR": "../../l10n", + "MOZ_MAKE_COMPLETE_MAR": "1", + "LOCALE_MERGEDIR": "%(abs_merge_dir)s/", + 'TOOLTOOL_CACHE': '/builds/tooltool_cache', + 'TOOLTOOL_HOME': '/builds', + }, + "ssh_key_dir": "~/.ssh", + "log_name": "single_locale", + "objdir": "obj-l10n", + "js_src_dir": "js/src", + "vcs_share_base": "/builds/hg-shared", + + "upload_env_extra": { + "MOZ_PKG_PLATFORM": "mac", + }, + + # tooltool + 'tooltool_url': 'https://api.pub.build.mozilla.org/tooltool/', + 'tooltool_script': ["/builds/tooltool.py"], + 'tooltool_bootstrap': "setup.sh", + 'tooltool_manifest_src': 'browser/config/tooltool-manifests/macosx64/releng.manifest', + # balrog credential file: + 'balrog_credentials_file': 'oauth.txt', + + # l10n + "ignore_locales": ["en-US", "ja"], + "l10n_dir": "l10n", + "locales_file": "%(branch)s/browser/locales/all-locales", + "locales_dir": "browser/locales", + "hg_l10n_tag": "default", + "merge_locales": True, + + # MAR + "previous_mar_dir": "dist/previous", + "current_mar_dir": "dist/current", + "update_mar_dir": "dist/update", # sure? + "previous_mar_filename": "previous.mar", + "current_work_mar_dir": "current.work", + "package_base_dir": "dist/l10n-stage", + "application_ini": "Contents/Resources/application.ini", + "buildid_section": 'App', + "buildid_option": "BuildID", + "unpack_script": "tools/update-packaging/unwrap_full_update.pl", + "incremental_update_script": "tools/update-packaging/make_incremental_update.sh", + "balrog_release_pusher_script": "scripts/updates/balrog-release-pusher.py", + "update_packaging_dir": "tools/update-packaging", + "local_mar_tool_dir": "dist/host/bin", + "mar": "mar", + "mbsdiff": "mbsdiff", + "current_mar_filename": "firefox-%(version)s.%(locale)s.mac.complete.mar", + "complete_mar": "firefox-%(version)s.en-US.mac.complete.mar", + "localized_mar": "firefox-%(version)s.%(locale)s.mac.complete.mar", + "partial_mar": "firefox-%(version)s.%(locale)s.mac.partial.%(from_buildid)s-%(to_buildid)s.mar", + 'installer_file': "firefox-%(version)s.en-US.mac.dmg", +} diff --git a/testing/mozharness/configs/single_locale/mozilla-beta_devedition.py b/testing/mozharness/configs/single_locale/mozilla-beta_devedition.py new file mode 100644 index 000000000000..00b5c2744a9c --- /dev/null +++ b/testing/mozharness/configs/single_locale/mozilla-beta_devedition.py @@ -0,0 +1,37 @@ +config = { + "nightly_build": True, + "branch": "mozilla-beta", + "en_us_binary_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-beta/", + "update_channel": "aurora", # devedition uses aurora based branding + + # l10n + "hg_l10n_base": "https://hg.mozilla.org/releases/l10n/mozilla-beta", + + # repositories + "mozilla_dir": "mozilla-beta", + "repos": [{ + "vcs": "hg", + "repo": "https://hg.mozilla.org/build/tools", + "branch": "default", + "dest": "tools", + }, { + "vcs": "hg", + "repo": "https://hg.mozilla.org/releases/mozilla-beta", + "revision": "%(revision)s", + "dest": "mozilla-beta", + "clone_upstream_url": "https://hg.mozilla.org/mozilla-unified", + }], + # purge options + 'purge_minsize': 12, + 'is_automation': True, + 'default_actions': [ + "clobber", + "pull", + "clone-locales", + "list-locales", + "setup", + "repack", + "taskcluster-upload", + "summary", + ], +} diff --git a/testing/mozharness/configs/single_locale/win32_devedition.py b/testing/mozharness/configs/single_locale/win32_devedition.py new file mode 100644 index 000000000000..aa79e96d6ac5 --- /dev/null +++ b/testing/mozharness/configs/single_locale/win32_devedition.py @@ -0,0 +1,76 @@ +import os +import sys + +config = { + "platform": "win32", + "stage_product": "firefox", + "update_platform": "WINNT_x86-msvc", + "mozconfig": "%(branch)s/browser/config/mozconfigs/win32/l10n-mozconfig-devedition", + "bootstrap_env": { + "MOZ_OBJDIR": "obj-l10n", + "EN_US_BINARY_URL": "%(en_us_binary_url)s", + "LOCALE_MERGEDIR": "%(abs_merge_dir)s", + "MOZ_UPDATE_CHANNEL": "%(update_channel)s", + "DIST": "%(abs_objdir)s", + "L10NBASEDIR": "../../l10n", + "MOZ_MAKE_COMPLETE_MAR": "1", + "PATH": 'C:\\mozilla-build\\nsis-3.01;' + '%s' % (os.environ.get('path')), + 'TOOLTOOL_CACHE': 'c:/builds/tooltool_cache', + 'TOOLTOOL_HOME': '/c/builds', + }, + "ssh_key_dir": "~/.ssh", + "log_name": "single_locale", + "objdir": "obj-l10n", + "js_src_dir": "js/src", + "vcs_share_base": "c:/builds/hg-shared", + + # tooltool + 'tooltool_url': 'https://api.pub.build.mozilla.org/tooltool/', + 'tooltool_script': [sys.executable, + 'C:/mozilla-build/tooltool.py'], + 'tooltool_bootstrap': "setup.sh", + 'tooltool_manifest_src': 'browser/config/tooltool-manifests/win32/releng.manifest', + # balrog credential file: + 'balrog_credentials_file': 'oauth.txt', + + # l10n + "ignore_locales": ["en-US", "ja-JP-mac"], + "l10n_dir": "l10n", + "locales_file": "%(branch)s/browser/locales/all-locales", + "locales_dir": "browser/locales", + "hg_l10n_tag": "default", + "merge_locales": True, + + # MAR + "previous_mar_dir": "dist\\previous", + "current_mar_dir": "dist\\current", + "update_mar_dir": "dist\\update", # sure? + "previous_mar_filename": "previous.mar", + "current_work_mar_dir": "current.work", + "package_base_dir": "dist\\l10n-stage", + "application_ini": "application.ini", + "buildid_section": 'App', + "buildid_option": "BuildID", + "unpack_script": "tools\\update-packaging\\unwrap_full_update.pl", + "incremental_update_script": "tools\\update-packaging\\make_incremental_update.sh", + "balrog_release_pusher_script": "scripts\\updates\\balrog-release-pusher.py", + "update_packaging_dir": "tools\\update-packaging", + "local_mar_tool_dir": "dist\\host\\bin", + "mar": "mar.exe", + "mbsdiff": "mbsdiff.exe", + "current_mar_filename": "firefox-%(version)s.%(locale)s.win32.complete.mar", + "complete_mar": "firefox-%(version)s.en-US.win32.complete.mar", + "localized_mar": "firefox-%(version)s.%(locale)s.win32.complete.mar", + "partial_mar": "firefox-%(version)s.%(locale)s.win32.partial.%(from_buildid)s-%(to_buildid)s.mar", + 'installer_file': "firefox-%(version)s.en-US.win32.installer.exe", + + # use mozmake? + "enable_mozmake": True, + 'exes': { + 'virtualenv': [ + sys.executable, + 'c:/mozilla-build/buildbotve/virtualenv.py' + ], + } +} diff --git a/testing/mozharness/configs/single_locale/win64_devedition.py b/testing/mozharness/configs/single_locale/win64_devedition.py new file mode 100644 index 000000000000..a5a2dcdd03eb --- /dev/null +++ b/testing/mozharness/configs/single_locale/win64_devedition.py @@ -0,0 +1,76 @@ +import os +import sys + +config = { + "platform": "win64", + "stage_product": "firefox", + "update_platform": "WINNT_x86_64-msvc", + "mozconfig": "%(branch)s/browser/config/mozconfigs/win64/l10n-mozconfig-devedition", + "bootstrap_env": { + "MOZ_OBJDIR": "obj-l10n", + "EN_US_BINARY_URL": "%(en_us_binary_url)s", + "MOZ_UPDATE_CHANNEL": "%(update_channel)s", + "DIST": "%(abs_objdir)s", + "LOCALE_MERGEDIR": "%(abs_merge_dir)s", + "L10NBASEDIR": "../../l10n", + "MOZ_MAKE_COMPLETE_MAR": "1", + "PATH": 'C:\\mozilla-build\\nsis-3.01;' + '%s' % (os.environ.get('path')), + 'TOOLTOOL_CACHE': 'c:/builds/tooltool_cache', + 'TOOLTOOL_HOME': '/c/builds', + }, + "ssh_key_dir": "~/.ssh", + "log_name": "single_locale", + "objdir": "obj-l10n", + "js_src_dir": "js/src", + "vcs_share_base": "c:/builds/hg-shared", + + # tooltool + 'tooltool_url': 'https://api.pub.build.mozilla.org/tooltool/', + 'tooltool_script': [sys.executable, + 'C:/mozilla-build/tooltool.py'], + 'tooltool_bootstrap': "setup.sh", + 'tooltool_manifest_src': 'browser/config/tooltool-manifests/win64/releng.manifest', + # balrog credential file: + 'balrog_credentials_file': 'oauth.txt', + + # l10n + "ignore_locales": ["en-US", "ja-JP-mac"], + "l10n_dir": "l10n", + "locales_file": "%(branch)s/browser/locales/all-locales", + "locales_dir": "browser/locales", + "hg_l10n_tag": "default", + "merge_locales": True, + + # MAR + "previous_mar_dir": "dist\\previous", + "current_mar_dir": "dist\\current", + "update_mar_dir": "dist\\update", # sure? + "previous_mar_filename": "previous.mar", + "current_work_mar_dir": "current.work", + "package_base_dir": "dist\\l10n-stage", + "application_ini": "application.ini", + "buildid_section": 'App', + "buildid_option": "BuildID", + "unpack_script": "tools\\update-packaging\\unwrap_full_update.pl", + "incremental_update_script": "tools\\update-packaging\\make_incremental_update.sh", + "balrog_release_pusher_script": "scripts\\updates\\balrog-release-pusher.py", + "update_packaging_dir": "tools\\update-packaging", + "local_mar_tool_dir": "dist\\host\\bin", + "mar": "mar.exe", + "mbsdiff": "mbsdiff.exe", + "current_mar_filename": "firefox-%(version)s.%(locale)s.win64.complete.mar", + "complete_mar": "firefox-%(version)s.en-US.win64.complete.mar", + "localized_mar": "firefox-%(version)s.%(locale)s.win64.complete.mar", + "partial_mar": "firefox-%(version)s.%(locale)s.win64.partial.%(from_buildid)s-%(to_buildid)s.mar", + 'installer_file': "firefox-%(version)s.en-US.win64.installer.exe", + + # use mozmake? + "enable_mozmake": True, + 'exes': { + 'virtualenv': [ + sys.executable, + 'c:/mozilla-build/buildbotve/virtualenv.py' + ], + } +} From dd07e2a56bcbd10a491f8ecf4f80c82003d84232 Mon Sep 17 00:00:00 2001 From: Michael Layzell Date: Tue, 2 May 2017 15:36:35 -0400 Subject: [PATCH 29/35] Bug 1357829 - Part 1: Expose profiler_suspend_and_sample_thread, r=njn This patch performs a refactoring to the internals of the profiler in order to expose a function, profiler_suspend_and_sample_thread, which can be called from a background thread to suspend, sample the native stack, and then resume the target passed-in thread. The interface was designed to expose as few internals of the profiler as possible, exposing only a single callback which accepts the list of program counters and stack pointers collected during the backtrace. A method `profiler_current_thread_id` was also added to get the thread_id of the current thread, which can then be passed by another thread into profiler_suspend_sample_thread to sample the stack of that thread. This is implemented in two parts: 1) Splitting SamplerThread into two classes: Sampler, and SamplerThread. Sampler was created to extract the core logic from SamplerThread which manages unix signals on android and linux, as well as suspends the target thread on all platforms. SamplerThread was then modified to subclass this type, adding the extra methods and fields required for the creation and management of the actual Sampler Thread. Some work was done to ensure that the methods on Sampler would not require ActivePS to be present, as we intend to sample threads when the profiler is not active for the Background Hang Reporter. 2) Moving the Tick() logic into the TickController interface. A TickController interface was added to platform which has 2 methods: Tick and Backtrace. The Tick method replaces the previous Tick() static method, allowing it to be overridden by a different consumer of SuspendAndSampleAndResumeThread, while the Backtrace() method replaces the previous MergeStacksIntoProfile method, allowing it to be overridden by different consumers of DoNativeBacktrace. This interface object is then used to wrap implementation specific data, such as the ProfilerBuffer, and is threaded through the SuspendAndSampleAndResumeThread and DoNativeBacktrace methods. This change added 2 virtual calls to the SamplerThread's critical section, which I believe should be a small enough overhead that it will not affect profiling performance. These virtual calls could be avoided using templating, but I decided that doing so would be unnecessary. MozReview-Commit-ID: AT48xb2asgV --- .../profiler/core/platform-linux-android.cpp | 166 ++++++---- tools/profiler/core/platform-macos.cpp | 99 +++--- tools/profiler/core/platform-win32.cpp | 196 ++++++----- tools/profiler/core/platform.cpp | 308 ++++++++++++------ tools/profiler/public/GeckoProfiler.h | 15 + 5 files changed, 484 insertions(+), 300 deletions(-) diff --git a/tools/profiler/core/platform-linux-android.cpp b/tools/profiler/core/platform-linux-android.cpp index 2d607b2af5e5..cf348b33bbcf 100644 --- a/tools/profiler/core/platform-linux-android.cpp +++ b/tools/profiler/core/platform-linux-android.cpp @@ -130,7 +130,7 @@ public: }; //////////////////////////////////////////////////////////////////////// -// BEGIN SamplerThread target specifics +// BEGIN Sampler target specifics // The only way to reliably interrupt a Linux thread and inspect its register // and stack state is by sending a signal to it, and doing the work inside the @@ -199,7 +199,7 @@ struct SigHandlerCoordinator ucontext_t mUContext; // Context at signal }; -struct SigHandlerCoordinator* SamplerThread::sSigHandlerCoordinator = nullptr; +struct SigHandlerCoordinator* Sampler::sSigHandlerCoordinator = nullptr; static void SigprofHandler(int aSignal, siginfo_t* aInfo, void* aContext) @@ -208,18 +208,18 @@ SigprofHandler(int aSignal, siginfo_t* aInfo, void* aContext) int savedErrno = errno; MOZ_ASSERT(aSignal == SIGPROF); - MOZ_ASSERT(SamplerThread::sSigHandlerCoordinator); + MOZ_ASSERT(Sampler::sSigHandlerCoordinator); // By sending us this signal, the sampler thread has sent us message 1 in // the comment above, with the meaning "|sSigHandlerCoordinator| is ready // for use, please copy your register context into it." - SamplerThread::sSigHandlerCoordinator->mUContext = + Sampler::sSigHandlerCoordinator->mUContext = *static_cast(aContext); // Send message 2: tell the sampler thread that the context has been copied // into |sSigHandlerCoordinator->mUContext|. sem_post can never fail by // being interrupted by a signal, so there's no loop around this call. - int r = sem_post(&SamplerThread::sSigHandlerCoordinator->mMessage2); + int r = sem_post(&Sampler::sSigHandlerCoordinator->mMessage2); MOZ_ASSERT(r == 0); // At this point, the sampler thread assumes we are suspended, so we must @@ -227,7 +227,7 @@ SigprofHandler(int aSignal, siginfo_t* aInfo, void* aContext) // Wait for message 3: the sampler thread tells us to resume. while (true) { - r = sem_wait(&SamplerThread::sSigHandlerCoordinator->mMessage3); + r = sem_wait(&Sampler::sSigHandlerCoordinator->mMessage3); if (r == -1 && errno == EINTR) { // Interrupted by a signal. Try again. continue; @@ -240,33 +240,19 @@ SigprofHandler(int aSignal, siginfo_t* aInfo, void* aContext) // Send message 4: tell the sampler thread that we are finished accessing // |sSigHandlerCoordinator|. After this point it is not safe to touch // |sSigHandlerCoordinator|. - r = sem_post(&SamplerThread::sSigHandlerCoordinator->mMessage4); + r = sem_post(&Sampler::sSigHandlerCoordinator->mMessage4); MOZ_ASSERT(r == 0); errno = savedErrno; } -static void* -ThreadEntry(void* aArg) -{ - auto thread = static_cast(aArg); - thread->mSamplerTid = gettid(); - thread->Run(); - return nullptr; -} - -SamplerThread::SamplerThread(PSLockRef aLock, uint32_t aActivityGeneration, - double aIntervalMilliseconds) - : mActivityGeneration(aActivityGeneration) - , mIntervalMicroseconds( - std::max(1, int(floor(aIntervalMilliseconds * 1000 + 0.5)))) - , mMyPid(getpid()) +Sampler::Sampler(PSLockRef aLock) + : mMyPid(getpid()) // We don't know what the sampler thread's ID will be until it runs, so set - // mSamplerTid to a dummy value and fill it in for real in ThreadEntry(). + // mSamplerTid to a dummy value and fill it in for real in + // SuspendAndSampleAndResumeThread(). , mSamplerTid(-1) { - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - #if defined(USE_EHABI_STACKWALK) mozilla::EHABIStackWalkInit(); #elif defined(USE_LUL_STACKWALK) @@ -304,66 +290,28 @@ SamplerThread::SamplerThread(PSLockRef aLock, uint32_t aActivityGeneration, } } #endif - - // Start the sampling thread. It repeatedly sends a SIGPROF signal. Sending - // the signal ourselves instead of relying on itimer provides much better - // accuracy. - if (pthread_create(&mThread, nullptr, ThreadEntry, this) != 0) { - MOZ_CRASH("pthread_create failed"); - } -} - -SamplerThread::~SamplerThread() -{ - pthread_join(mThread, nullptr); } void -SamplerThread::Stop(PSLockRef aLock) +Sampler::Disable(PSLockRef aLock) { - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - // Restore old signal handler. This is global state so it's important that - // we do it now, while gPSMutex is locked. It's safe to do this now even - // though this SamplerThread is still alive, because the next time the main - // loop of Run() iterates it won't get past the mActivityGeneration check, - // and so won't send any signals. + // we do it now, while gPSMutex is locked. sigaction(SIGPROF, &mOldSigprofHandler, 0); } void -SamplerThread::SleepMicro(uint32_t aMicroseconds) -{ - if (aMicroseconds >= 1000000) { - // Use usleep for larger intervals, because the nanosleep - // code below only supports intervals < 1 second. - MOZ_ALWAYS_TRUE(!::usleep(aMicroseconds)); - return; - } - - struct timespec ts; - ts.tv_sec = 0; - ts.tv_nsec = aMicroseconds * 1000UL; - - int rv = ::nanosleep(&ts, &ts); - - while (rv != 0 && errno == EINTR) { - // Keep waiting in case of interrupt. - // nanosleep puts the remaining time back into ts. - rv = ::nanosleep(&ts, &ts); - } - - MOZ_ASSERT(!rv, "nanosleep call failed"); -} - -void -SamplerThread::SuspendAndSampleAndResumeThread(PSLockRef aLock, - TickSample& aSample) +Sampler::SuspendAndSampleAndResumeThread(PSLockRef aLock, + TickController& aController, + TickSample& aSample) { // Only one sampler thread can be sampling at once. So we expect to have // complete control over |sSigHandlerCoordinator|. MOZ_ASSERT(!sSigHandlerCoordinator); + if (mSamplerTid == -1) { + mSamplerTid = gettid(); + } int sampleeTid = aSample.mThreadId; MOZ_RELEASE_ASSERT(sampleeTid != mSamplerTid); @@ -410,7 +358,7 @@ SamplerThread::SuspendAndSampleAndResumeThread(PSLockRef aLock, // Extract the current PC and sp. FillInSample(aSample, &sSigHandlerCoordinator->mUContext); - Tick(aLock, ActivePS::Buffer(aLock), aSample); + aController.Tick(aLock, aSample); //----------------------------------------------------------------// // Resume the target thread. @@ -440,6 +388,80 @@ SamplerThread::SuspendAndSampleAndResumeThread(PSLockRef aLock, sSigHandlerCoordinator = nullptr; } +// END Sampler target specifics +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +// BEGIN SamplerThread target specifics + +static void* +ThreadEntry(void* aArg) +{ + auto thread = static_cast(aArg); + thread->Run(); + return nullptr; +} + +SamplerThread::SamplerThread(PSLockRef aLock, uint32_t aActivityGeneration, + double aIntervalMilliseconds) + : Sampler(aLock) + , mActivityGeneration(aActivityGeneration) + , mIntervalMicroseconds( + std::max(1, int(floor(aIntervalMilliseconds * 1000 + 0.5)))) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + // Start the sampling thread. It repeatedly sends a SIGPROF signal. Sending + // the signal ourselves instead of relying on itimer provides much better + // accuracy. + if (pthread_create(&mThread, nullptr, ThreadEntry, this) != 0) { + MOZ_CRASH("pthread_create failed"); + } +} + +SamplerThread::~SamplerThread() +{ + pthread_join(mThread, nullptr); +} + +void +SamplerThread::SleepMicro(uint32_t aMicroseconds) +{ + if (aMicroseconds >= 1000000) { + // Use usleep for larger intervals, because the nanosleep + // code below only supports intervals < 1 second. + MOZ_ALWAYS_TRUE(!::usleep(aMicroseconds)); + return; + } + + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = aMicroseconds * 1000UL; + + int rv = ::nanosleep(&ts, &ts); + + while (rv != 0 && errno == EINTR) { + // Keep waiting in case of interrupt. + // nanosleep puts the remaining time back into ts. + rv = ::nanosleep(&ts, &ts); + } + + MOZ_ASSERT(!rv, "nanosleep call failed"); +} + +void +SamplerThread::Stop(PSLockRef aLock) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + // Restore old signal handler. This is global state so it's important that + // we do it now, while gPSMutex is locked. It's safe to do this now even + // though this SamplerThread is still alive, because the next time the main + // loop of Run() iterates it won't get past the mActivityGeneration check, + // and so won't send any signals. + Sampler::Disable(aLock); +} + // END SamplerThread target specifics //////////////////////////////////////////////////////////////////////// diff --git a/tools/profiler/core/platform-macos.cpp b/tools/profiler/core/platform-macos.cpp index 163ad4570447..90613ac2b7b0 100644 --- a/tools/profiler/core/platform-macos.cpp +++ b/tools/profiler/core/platform-macos.cpp @@ -62,54 +62,21 @@ private: }; //////////////////////////////////////////////////////////////////////// -// BEGIN SamplerThread target specifics +// BEGIN Sampler target specifics -static void* -ThreadEntry(void* aArg) +Sampler::Sampler(PSLockRef aLock) { - auto thread = static_cast(aArg); - thread->Run(); - return nullptr; -} - -SamplerThread::SamplerThread(PSLockRef aLock, uint32_t aActivityGeneration, - double aIntervalMilliseconds) - : mActivityGeneration(aActivityGeneration) - , mIntervalMicroseconds( - std::max(1, int(floor(aIntervalMilliseconds * 1000 + 0.5)))) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - pthread_attr_t* attr_ptr = nullptr; - if (pthread_create(&mThread, attr_ptr, ThreadEntry, this) != 0) { - MOZ_CRASH("pthread_create failed"); - } -} - -SamplerThread::~SamplerThread() -{ - pthread_join(mThread, nullptr); } void -SamplerThread::Stop(PSLockRef aLock) +Sampler::Disable(PSLockRef aLock) { - MOZ_RELEASE_ASSERT(NS_IsMainThread()); } void -SamplerThread::SleepMicro(uint32_t aMicroseconds) -{ - usleep(aMicroseconds); - // FIXME: the OSX 10.12 page for usleep says "The usleep() function is - // obsolescent. Use nanosleep(2) instead." This implementation could be - // merged with the linux-android version. Also, this doesn't handle the - // case where the usleep call is interrupted by a signal. -} - -void -SamplerThread::SuspendAndSampleAndResumeThread(PSLockRef aLock, - TickSample& aSample) +Sampler::SuspendAndSampleAndResumeThread(PSLockRef aLock, + TickController& aController, + TickSample& aSample) { thread_act_t samplee_thread = aSample.mPlatformData->ProfiledThread(); @@ -166,7 +133,7 @@ SamplerThread::SuspendAndSampleAndResumeThread(PSLockRef aLock, aSample.mSP = reinterpret_cast
(state.REGISTER_FIELD(sp)); aSample.mFP = reinterpret_cast
(state.REGISTER_FIELD(bp)); - Tick(aLock, ActivePS::Buffer(aLock), aSample); + aController.Tick(aLock, aSample); } #undef REGISTER_FIELD @@ -181,6 +148,58 @@ SamplerThread::SuspendAndSampleAndResumeThread(PSLockRef aLock, // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING } +// END Sampler target specifics +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +// BEGIN SamplerThread target specifics + +static void* +ThreadEntry(void* aArg) +{ + auto thread = static_cast(aArg); + thread->Run(); + return nullptr; +} + +SamplerThread::SamplerThread(PSLockRef aLock, uint32_t aActivityGeneration, + double aIntervalMilliseconds) + : Sampler(aLock) + , mActivityGeneration(aActivityGeneration) + , mIntervalMicroseconds( + std::max(1, int(floor(aIntervalMilliseconds * 1000 + 0.5)))) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + pthread_attr_t* attr_ptr = nullptr; + if (pthread_create(&mThread, attr_ptr, ThreadEntry, this) != 0) { + MOZ_CRASH("pthread_create failed"); + } +} + +SamplerThread::~SamplerThread() +{ + pthread_join(mThread, nullptr); +} + +void +SamplerThread::SleepMicro(uint32_t aMicroseconds) +{ + usleep(aMicroseconds); + // FIXME: the OSX 10.12 page for usleep says "The usleep() function is + // obsolescent. Use nanosleep(2) instead." This implementation could be + // merged with the linux-android version. Also, this doesn't handle the + // case where the usleep call is interrupted by a signal. +} + +void +SamplerThread::Stop(PSLockRef aLock) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + Sampler::Disable(aLock); +} + // END SamplerThread target specifics //////////////////////////////////////////////////////////////////////// diff --git a/tools/profiler/core/platform-win32.cpp b/tools/profiler/core/platform-win32.cpp index ea97f321b22b..e313b422635d 100644 --- a/tools/profiler/core/platform-win32.cpp +++ b/tools/profiler/core/platform-win32.cpp @@ -79,105 +79,26 @@ GetThreadHandle(PlatformData* aData) static const HANDLE kNoThread = INVALID_HANDLE_VALUE; //////////////////////////////////////////////////////////////////////// -// BEGIN SamplerThread target specifics +// BEGIN Sampler target specifics -static unsigned int __stdcall -ThreadEntry(void* aArg) +Sampler::Sampler(PSLockRef aLock) { - auto thread = static_cast(aArg); - thread->Run(); - return 0; -} - -SamplerThread::SamplerThread(PSLockRef aLock, uint32_t aActivityGeneration, - double aIntervalMilliseconds) - : mActivityGeneration(aActivityGeneration) - , mIntervalMicroseconds( - std::max(1, int(floor(aIntervalMilliseconds * 1000 + 0.5)))) -{ - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - // By default we'll not adjust the timer resolution which tends to be - // around 16ms. However, if the requested interval is sufficiently low - // we'll try to adjust the resolution to match. - if (mIntervalMicroseconds < 10*1000) { - ::timeBeginPeriod(mIntervalMicroseconds / 1000); - } - - // Create a new thread. It is important to use _beginthreadex() instead of - // the Win32 function CreateThread(), because the CreateThread() does not - // initialize thread-specific structures in the C runtime library. - mThread = reinterpret_cast( - _beginthreadex(nullptr, - /* stack_size */ 0, - ThreadEntry, - this, - /* initflag */ 0, - nullptr)); - if (mThread == 0) { - MOZ_CRASH("_beginthreadex failed"); - } -} - -SamplerThread::~SamplerThread() -{ - WaitForSingleObject(mThread, INFINITE); - - // Close our own handle for the thread. - if (mThread != kNoThread) { - CloseHandle(mThread); - } } void -SamplerThread::Stop(PSLockRef aLock) +Sampler::Disable(PSLockRef aLock) { - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - // Disable any timer resolution changes we've made. Do it now while - // gPSMutex is locked, i.e. before any other SamplerThread can be created - // and call ::timeBeginPeriod(). - // - // It's safe to do this now even though this SamplerThread is still alive, - // because the next time the main loop of Run() iterates it won't get past - // the mActivityGeneration check, and so it won't make any more ::Sleep() - // calls. - if (mIntervalMicroseconds < 10 * 1000) { - ::timeEndPeriod(mIntervalMicroseconds / 1000); - } } void -SamplerThread::SleepMicro(uint32_t aMicroseconds) -{ - // For now, keep the old behaviour of minimum Sleep(1), even for - // smaller-than-usual sleeps after an overshoot, unless the user has - // explicitly opted into a sub-millisecond profiler interval. - if (mIntervalMicroseconds >= 1000) { - ::Sleep(std::max(1u, aMicroseconds / 1000)); - } else { - TimeStamp start = TimeStamp::Now(); - TimeStamp end = start + TimeDuration::FromMicroseconds(aMicroseconds); - - // First, sleep for as many whole milliseconds as possible. - if (aMicroseconds >= 1000) { - ::Sleep(aMicroseconds / 1000); - } - - // Then, spin until enough time has passed. - while (TimeStamp::Now() < end) { - _mm_pause(); - } - } -} - -void -SamplerThread::SuspendAndSampleAndResumeThread(PSLockRef aLock, - TickSample& aSample) +Sampler::SuspendAndSampleAndResumeThread(PSLockRef aLock, + TickController& aController, + TickSample& aSample) { HANDLE profiled_thread = aSample.mPlatformData->ProfiledThread(); - if (profiled_thread == nullptr) + if (profiled_thread == nullptr) { return; + } // Context used for sampling the register state of the profiled thread. CONTEXT context; @@ -228,7 +149,7 @@ SamplerThread::SuspendAndSampleAndResumeThread(PSLockRef aLock, aSample.mContext = &context; - Tick(aLock, ActivePS::Buffer(aLock), aSample); + aController.Tick(aLock, aSample); //----------------------------------------------------------------// // Resume the target thread. @@ -240,6 +161,105 @@ SamplerThread::SuspendAndSampleAndResumeThread(PSLockRef aLock, // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING } +// END Sampler target specifics +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +// BEGIN SamplerThread target specifics + +static unsigned int __stdcall +ThreadEntry(void* aArg) +{ + auto thread = static_cast(aArg); + thread->Run(); + return 0; +} + +SamplerThread::SamplerThread(PSLockRef aLock, uint32_t aActivityGeneration, + double aIntervalMilliseconds) + : Sampler(aLock) + , mActivityGeneration(aActivityGeneration) + , mIntervalMicroseconds( + std::max(1, int(floor(aIntervalMilliseconds * 1000 + 0.5)))) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + // By default we'll not adjust the timer resolution which tends to be + // around 16ms. However, if the requested interval is sufficiently low + // we'll try to adjust the resolution to match. + if (mIntervalMicroseconds < 10*1000) { + ::timeBeginPeriod(mIntervalMicroseconds / 1000); + } + + // Create a new thread. It is important to use _beginthreadex() instead of + // the Win32 function CreateThread(), because the CreateThread() does not + // initialize thread-specific structures in the C runtime library. + mThread = reinterpret_cast( + _beginthreadex(nullptr, + /* stack_size */ 0, + ThreadEntry, + this, + /* initflag */ 0, + nullptr)); + if (mThread == 0) { + MOZ_CRASH("_beginthreadex failed"); + } +} + +SamplerThread::~SamplerThread() +{ + WaitForSingleObject(mThread, INFINITE); + + // Close our own handle for the thread. + if (mThread != kNoThread) { + CloseHandle(mThread); + } +} + +void +SamplerThread::SleepMicro(uint32_t aMicroseconds) +{ + // For now, keep the old behaviour of minimum Sleep(1), even for + // smaller-than-usual sleeps after an overshoot, unless the user has + // explicitly opted into a sub-millisecond profiler interval. + if (mIntervalMicroseconds >= 1000) { + ::Sleep(std::max(1u, aMicroseconds / 1000)); + } else { + TimeStamp start = TimeStamp::Now(); + TimeStamp end = start + TimeDuration::FromMicroseconds(aMicroseconds); + + // First, sleep for as many whole milliseconds as possible. + if (aMicroseconds >= 1000) { + ::Sleep(aMicroseconds / 1000); + } + + // Then, spin until enough time has passed. + while (TimeStamp::Now() < end) { + _mm_pause(); + } + } +} + +void +SamplerThread::Stop(PSLockRef aLock) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + // Disable any timer resolution changes we've made. Do it now while + // gPSMutex is locked, i.e. before any other SamplerThread can be created + // and call ::timeBeginPeriod(). + // + // It's safe to do this now even though this SamplerThread is still alive, + // because the next time the main loop of Run() iterates it won't get past + // the mActivityGeneration check, and so it won't make any more ::Sleep() + // calls. + if (mIntervalMicroseconds < 10 * 1000) { + ::timeEndPeriod(mIntervalMicroseconds / 1000); + } + + Sampler::Disable(aLock); +} + // END SamplerThread target specifics //////////////////////////////////////////////////////////////////////// diff --git a/tools/profiler/core/platform.cpp b/tools/profiler/core/platform.cpp index a546739e8486..9bff49eb5902 100644 --- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -754,6 +754,15 @@ struct NativeStack size_t count; }; +class TickController +{ +public: + // NOTE: This method is called when the target thread of the sample is paused. + // Do not allocate or attempt to grab any locks during this function call. + virtual void Tick(PSLockRef aLock, + const TickSample& aSample) = 0; +}; + mozilla::Atomic WALKING_JS_STACK(false); struct AutoWalkJSStack @@ -980,31 +989,22 @@ StackWalkCallback(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure) } static void -DoNativeBacktrace(PSLockRef aLock, ProfileBuffer* aBuffer, +DoNativeBacktrace(PSLockRef aLock, NativeStack& aNativeStack, const TickSample& aSample) { - void* pc_array[1000]; - void* sp_array[1000]; - NativeStack nativeStack = { - pc_array, - sp_array, - mozilla::ArrayLength(pc_array), - 0 - }; - // Start with the current function. We use 0 as the frame number here because // the FramePointerStackWalk() and MozStackWalk() calls below will use 1..N. // This is a bit weird but it doesn't matter because StackWalkCallback() // doesn't use the frame number argument. - StackWalkCallback(/* frameNum */ 0, aSample.mPC, aSample.mSP, &nativeStack); + StackWalkCallback(/* frameNum */ 0, aSample.mPC, aSample.mSP, &aNativeStack); - uint32_t maxFrames = uint32_t(nativeStack.size - nativeStack.count); + uint32_t maxFrames = uint32_t(aNativeStack.size - aNativeStack.count); #if defined(GP_OS_darwin) || (defined(GP_PLAT_x86_windows)) void* stackEnd = aSample.mStackTop; if (aSample.mFP >= aSample.mSP && aSample.mFP <= stackEnd) { FramePointerStackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames, - &nativeStack, reinterpret_cast(aSample.mFP), + &aNativeStack, reinterpret_cast(aSample.mFP), stackEnd); } #else @@ -1012,28 +1012,17 @@ DoNativeBacktrace(PSLockRef aLock, ProfileBuffer* aBuffer, // MozStackWalk(). uintptr_t thread = GetThreadHandle(aSample.mPlatformData); MOZ_ASSERT(thread); - MozStackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames, &nativeStack, + MozStackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames, &aNativeStack, thread, /* platformData */ nullptr); #endif - - MergeStacksIntoProfile(aLock, aBuffer, aSample, nativeStack); } #endif #ifdef USE_EHABI_STACKWALK static void -DoNativeBacktrace(PSLockRef aLock, ProfileBuffer* aBuffer, +DoNativeBacktrace(PSLockRef aLock, NativeStack& aNativeStack, const TickSample& aSample) { - void* pc_array[1000]; - void* sp_array[1000]; - NativeStack nativeStack = { - pc_array, - sp_array, - mozilla::ArrayLength(pc_array), - 0 - }; - const mcontext_t* mcontext = &reinterpret_cast(aSample.mContext)->uc_mcontext; mcontext_t savedContext; @@ -1054,11 +1043,11 @@ DoNativeBacktrace(PSLockRef aLock, ProfileBuffer* aBuffer, // the saved state. uint32_t* vSP = reinterpret_cast(entry.stackAddress()); - nativeStack.count += EHABIStackWalk(*mcontext, - /* stackBase = */ vSP, - sp_array + nativeStack.count, - pc_array + nativeStack.count, - nativeStack.size - nativeStack.count); + aNativeStack.count += EHABIStackWalk(*mcontext, + /* stackBase = */ vSP, + aNativeStack.sp_array + aNativeStack.count, + aNativeStack.pc_array + aNativeStack.count, + aNativeStack.size - aNativeStack.count); memset(&savedContext, 0, sizeof(savedContext)); @@ -1080,13 +1069,11 @@ DoNativeBacktrace(PSLockRef aLock, ProfileBuffer* aBuffer, // Now unwind whatever's left (starting from either the last EnterJIT frame // or, if no EnterJIT was found, the original registers). - nativeStack.count += EHABIStackWalk(*mcontext, - aSample.mStackTop, - sp_array + nativeStack.count, - pc_array + nativeStack.count, - nativeStack.size - nativeStack.count); - - MergeStacksIntoProfile(aLock, aBuffer, aSample, nativeStack); + aNativeStack.count += EHABIStackWalk(*mcontext, + aSample.mStackTop, + aNativeStack.sp_array + aNativeStack.count, + aNativeStack.pc_array + aNativeStack.count, + aNativeStack.size - aNativeStack.count); } #endif @@ -1111,7 +1098,7 @@ ASAN_memcpy(void* aDst, const void* aSrc, size_t aLen) #endif static void -DoNativeBacktrace(PSLockRef aLock, ProfileBuffer* aBuffer, +DoNativeBacktrace(PSLockRef aLock, NativeStack& aNativeStack, const TickSample& aSample) { const mcontext_t* mc = @@ -1219,36 +1206,28 @@ DoNativeBacktrace(PSLockRef aLock, ProfileBuffer* aBuffer, // The maximum number of frames that LUL will produce. Setting it // too high gives a risk of it wasting a lot of time looping on - // corrupted stacks. + // corrupted stacks. Limit the size of the passed-in native stack + // to not exceed this number. const int MAX_NATIVE_FRAMES = 256; + if (aNativeStack.size > MAX_NATIVE_FRAMES) { + aNativeStack.size = MAX_NATIVE_FRAMES; + } size_t scannedFramesAllowed = 0; - - uintptr_t framePCs[MAX_NATIVE_FRAMES]; - uintptr_t frameSPs[MAX_NATIVE_FRAMES]; - size_t framesAvail = mozilla::ArrayLength(framePCs); - size_t framesUsed = 0; size_t scannedFramesAcquired = 0, framePointerFramesAcquired = 0; lul::LUL* lul = CorePS::Lul(aLock); - lul->Unwind(&framePCs[0], &frameSPs[0], - &framesUsed, &framePointerFramesAcquired, &scannedFramesAcquired, - framesAvail, scannedFramesAllowed, + lul->Unwind(reinterpret_cast(aNativeStack.pc_array), + reinterpret_cast(aNativeStack.sp_array), + &aNativeStack.count, + &framePointerFramesAcquired, &scannedFramesAcquired, + aNativeStack.size, scannedFramesAllowed, &startRegs, &stackImg); - NativeStack nativeStack = { - reinterpret_cast(framePCs), - reinterpret_cast(frameSPs), - mozilla::ArrayLength(framePCs), - framesUsed - }; - - MergeStacksIntoProfile(aLock, aBuffer, aSample, nativeStack); - // Update stats in the LUL stats object. Unfortunately this requires // three global memory operations. lul->mStats.mContext += 1; - lul->mStats.mCFI += framesUsed - 1 - framePointerFramesAcquired - - scannedFramesAcquired; + lul->mStats.mCFI += aNativeStack.count - 1 - framePointerFramesAcquired - + scannedFramesAcquired; lul->mStats.mFP += framePointerFramesAcquired; lul->mStats.mScanned += scannedFramesAcquired; } @@ -1267,24 +1246,51 @@ DoSampleStackTrace(PSLockRef aLock, ProfileBuffer* aBuffer, } } -// This function is called for each sampling period with the current program -// counter. It is called within a signal and so must be re-entrant. -static void -Tick(PSLockRef aLock, ProfileBuffer* aBuffer, const TickSample& aSample) +class ProfilerTickController : public TickController { - aBuffer->addTagThreadId(aSample.mThreadId, aSample.mLastSample); +public: + explicit ProfilerTickController(PSLockRef aLock, ProfileBuffer* aBuffer = nullptr) + : mBuffer(aBuffer ? aBuffer : ActivePS::Buffer(aLock)) + { + } + + // This function is called for each sampling period with the current program + // counter. It is called within a signal and so must be re-entrant. + void Tick(PSLockRef aLock, const TickSample& aSample) override; + +private: + ProfileBuffer* mBuffer; +}; + +void +ProfilerTickController::Tick(PSLockRef aLock, const TickSample& aSample) +{ + MOZ_RELEASE_ASSERT(ActivePS::Exists(aLock)); + + mBuffer->addTagThreadId(aSample.mThreadId, aSample.mLastSample); mozilla::TimeDuration delta = aSample.mTimeStamp - CorePS::ProcessStartTime(aLock); - aBuffer->addTag(ProfileBufferEntry::Time(delta.ToMilliseconds())); + mBuffer->addTag(ProfileBufferEntry::Time(delta.ToMilliseconds())); #if defined(HAVE_NATIVE_UNWIND) if (ActivePS::FeatureStackWalk(aLock)) { - DoNativeBacktrace(aLock, aBuffer, aSample); + void* pc_array[1000]; + void* sp_array[1000]; + NativeStack nativeStack = { + pc_array, + sp_array, + mozilla::ArrayLength(pc_array), + 0 + }; + + DoNativeBacktrace(aLock, nativeStack, aSample); + + MergeStacksIntoProfile(aLock, mBuffer, aSample, nativeStack); } else #endif { - DoSampleStackTrace(aLock, aBuffer, aSample); + DoSampleStackTrace(aLock, mBuffer, aSample); } // Don't process the PseudoStack's markers if we're synchronously sampling @@ -1294,27 +1300,27 @@ Tick(PSLockRef aLock, ProfileBuffer* aBuffer, const TickSample& aSample) aSample.mRacyInfo->GetPendingMarkers(); while (pendingMarkersList && pendingMarkersList->peek()) { ProfilerMarker* marker = pendingMarkersList->popHead(); - aBuffer->addStoredMarker(marker); - aBuffer->addTag(ProfileBufferEntry::Marker(marker)); + mBuffer->addStoredMarker(marker); + mBuffer->addTag(ProfileBufferEntry::Marker(marker)); } } if (aSample.mResponsiveness && aSample.mResponsiveness->HasData()) { mozilla::TimeDuration delta = aSample.mResponsiveness->GetUnresponsiveDuration(aSample.mTimeStamp); - aBuffer->addTag(ProfileBufferEntry::Responsiveness(delta.ToMilliseconds())); + mBuffer->addTag(ProfileBufferEntry::Responsiveness(delta.ToMilliseconds())); } // rssMemory is equal to 0 when we are not recording. if (aSample.mRSSMemory != 0) { double rssMemory = static_cast(aSample.mRSSMemory); - aBuffer->addTag(ProfileBufferEntry::ResidentMemory(rssMemory)); + mBuffer->addTag(ProfileBufferEntry::ResidentMemory(rssMemory)); } // ussMemory is equal to 0 when we are not recording. if (aSample.mUSSMemory != 0) { double ussMemory = static_cast(aSample.mUSSMemory); - aBuffer->addTag(ProfileBufferEntry::UnsharedMemory(ussMemory)); + mBuffer->addTag(ProfileBufferEntry::UnsharedMemory(ussMemory)); } } @@ -1683,17 +1689,72 @@ PrintUsageThenExit(int aExitCode) } //////////////////////////////////////////////////////////////////////// -// BEGIN SamplerThread +// BEGIN Sampler #if defined(GP_OS_linux) || defined(GP_OS_android) struct SigHandlerCoordinator; #endif +// Sampler performs setup and teardown of the state required to sample with the +// profiler. Sampler may exist when ActivePS is not present. +// +// SuspendAndSampleAndResumeThread must only be called from a single thread, +// and must not sample the thread it is being called from. A separate Sampler +// instance must be used for each thread which wants to capture samples. + +// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +// +// With the exception of SamplerThread, all Sampler objects must be Disable-d +// before releasing the lock which was used to create them. This avoids races +// on linux with the SIGPROF signal handler. + +class Sampler +{ +public: + // Sets up the profiler such that it can begin sampling. + explicit Sampler(PSLockRef aLock); + + // Disable the sampler, restoring it to its previous state. This must be + // called once, and only once, before the Sampler is destroyed. + void Disable(PSLockRef aLock); + + // This method suspends and resumes the samplee thread. + void SuspendAndSampleAndResumeThread(PSLockRef aLock, + TickController& aController, + TickSample& aSample); + +private: +#if defined(GP_OS_linux) || defined(GP_OS_android) + // Used to restore the SIGPROF handler when ours is removed. + struct sigaction mOldSigprofHandler; + + // This process' ID. Needed as an argument for tgkill in + // SuspendAndSampleAndResumeThread. + int mMyPid; + + // The sampler thread's ID. Used to assert that it is not sampling itself, + // which would lead to deadlock. + int mSamplerTid; + +public: + // This is the one-and-only variable used to communicate between the sampler + // thread and the samplee thread's signal handler. It's static because the + // samplee thread's signal handler is static. + static struct SigHandlerCoordinator* sSigHandlerCoordinator; +#endif +}; + +// END Sampler +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +// BEGIN SamplerThread + // The sampler thread controls sampling and runs whenever the profiler is // active. It periodically runs through all registered threads, finds those // that should be sampled, then pauses and samples them. -class SamplerThread +class SamplerThread : public Sampler { public: // Creates a sampler thread, but doesn't start it. @@ -1701,10 +1762,6 @@ public: double aIntervalMilliseconds); ~SamplerThread(); - // This runs on the sampler thread. It suspends and resumes the samplee - // threads. - void SuspendAndSampleAndResumeThread(PSLockRef aLock, TickSample& aSample); - // This runs on (is!) the sampler thread. void Run(); @@ -1729,26 +1786,6 @@ private: pthread_t mThread; #endif -#if defined(GP_OS_linux) || defined(GP_OS_android) - // Used to restore the SIGPROF handler when ours is removed. - struct sigaction mOldSigprofHandler; - - // This process' ID. Needed as an argument for tgkill in - // SuspendAndSampleAndResumeThread. - int mMyPid; - -public: - // The sampler thread's ID. Used to assert that it is not sampling itself, - // which would lead to deadlock. - int mSamplerTid; - - // This is the one-and-only variable used to communicate between the sampler - // thread and the samplee thread's signal handler. It's static because the - // samplee thread's signal handler is static. - static struct SigHandlerCoordinator* sSigHandlerCoordinator; -#endif - -private: SamplerThread(const SamplerThread&) = delete; void operator=(const SamplerThread&) = delete; }; @@ -1831,8 +1868,9 @@ SamplerThread::Run() } TickSample sample(info, rssMemory, ussMemory); + ProfilerTickController controller(lock); - SuspendAndSampleAndResumeThread(lock, sample); + SuspendAndSampleAndResumeThread(lock, controller, sample); } #if defined(USE_LUL_STACKWALK) @@ -2778,7 +2816,8 @@ profiler_get_backtrace() #endif #endif - Tick(lock, buffer, sample); + ProfilerTickController controller(lock, buffer); + controller.Tick(lock, sample); return UniqueProfilerBacktrace( new ProfilerBacktrace("SyncProfile", tid, buffer)); @@ -3004,5 +3043,74 @@ profiler_get_stack_top() return nullptr; } +int +profiler_current_thread_id() +{ + return Thread::GetCurrentId(); +} + +class SimpleTickController : public TickController +{ +public: + explicit SimpleTickController(const std::function& aCallback, + bool aSampleNative) + : mCallback(aCallback) + , mSampleNative(aSampleNative) + { + } + + void Tick(PSLockRef aLock, const TickSample& aSample) override { + void* pc_array[1000]; + void* sp_array[1000]; + NativeStack nativeStack = { + pc_array, + sp_array, + mozilla::ArrayLength(pc_array), + 0 + }; + +#if defined(HAVE_NATIVE_UNWIND) + if (mSampleNative) { + DoNativeBacktrace(aLock, nativeStack, aSample); + } +#endif + + mCallback(nativeStack.pc_array, nativeStack.count); + } + +private: + const std::function& mCallback; + bool mSampleNative; +}; + +// NOTE: The callback function passed in will be called while the target thread +// is paused. Doing stuff in this function like allocating which may try to +// claim locks is a surefire way to deadlock. +void +profiler_suspend_and_sample_thread(int aThreadId, + const std::function& aCallback, + bool aSampleNative /* = true */) +{ + PSAutoLock lock(gPSMutex); + + const CorePS::ThreadVector& liveThreads = CorePS::LiveThreads(lock); + for (uint32_t i = 0; i < liveThreads.size(); i++) { + ThreadInfo* info = liveThreads.at(i); + + if (info->ThreadId() == aThreadId) { + // Suspend, sample, and then resume the target thread. + Sampler sampler(lock); + TickSample sample(info, 0, 0); + SimpleTickController controller(aCallback, aSampleNative); + sampler.SuspendAndSampleAndResumeThread(lock, controller, sample); + + // NOTE: Make sure to disable the sampler before it is destroyed, in case + // the profiler is running at the same time. + sampler.Disable(lock); + break; + } + } +} + // END externally visible functions //////////////////////////////////////////////////////////////////////// diff --git a/tools/profiler/public/GeckoProfiler.h b/tools/profiler/public/GeckoProfiler.h index 9d2654fbde09..df19e9be038a 100644 --- a/tools/profiler/public/GeckoProfiler.h +++ b/tools/profiler/public/GeckoProfiler.h @@ -22,6 +22,7 @@ #include #include +#include #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" @@ -382,6 +383,20 @@ PROFILER_FUNC_VOID(profiler_log(const char *str)) // this method will return nullptr. PROFILER_FUNC(void* profiler_get_stack_top(), nullptr) +PROFILER_FUNC(int profiler_current_thread_id(), 0) + +// This method suspends the thread identified by aThreadId, optionally samples +// it for its native stack, and then calls the callback. The callback is passed +// the native stack's program counters and length as two arguments if +// aSampleNative is true. +// +// WARNING: The target thread is suspended during the callback. Do not try to +// allocate or acquire any locks, or you could deadlock. The target thread will +// have resumed by the time that this function returns. +PROFILER_FUNC_VOID(profiler_suspend_and_sample_thread(int aThreadId, + const std::function& aCallback, + bool aSampleNative = true)) + // End of the functions defined whether the profiler is enabled or not. #if defined(MOZ_GECKO_PROFILER) From 3a8384acc05b8f8da017a7841195a365a0ca78c4 Mon Sep 17 00:00:00 2001 From: Michael Layzell Date: Tue, 2 May 2017 17:55:23 -0400 Subject: [PATCH 30/35] Bug 1357829 - Part 2: Use profiler_suspend_sample_thread in the background hang monitor, r=froydnj This patch uses the profiler_suspend_sample_thread method which was added in part 1. With this patch, we no longer manually run code to pause the target thread, instead using the profiler's provided code to do so. In addition, we no longer manually walk the stack to collect native stack frames, instead relying on the profiler's cross-platform stack walking logic. This helps remove some of the code from ThreadStackHelper which was redundant with the profiler. Much of the pseudostack code in ThreadStackHelper is also redundant, and should hopefully be eliminated in a follow-up. MozReview-Commit-ID: 4RjLHt6inH9 --- .../tests/unit/test_ThreadHangStats.js | 17 +- .../telemetry/tests/unit/xpcshell.ini | 1 + xpcom/threads/BackgroundHangMonitor.cpp | 3 - xpcom/threads/ThreadStackHelper.cpp | 261 +++--------------- xpcom/threads/ThreadStackHelper.h | 59 +--- 5 files changed, 64 insertions(+), 277 deletions(-) diff --git a/toolkit/components/telemetry/tests/unit/test_ThreadHangStats.js b/toolkit/components/telemetry/tests/unit/test_ThreadHangStats.js index 04885441246b..7f13078a751a 100644 --- a/toolkit/components/telemetry/tests/unit/test_ThreadHangStats.js +++ b/toolkit/components/telemetry/tests/unit/test_ThreadHangStats.js @@ -84,16 +84,13 @@ function run_test() { notEqual(endHangs.hangs[0].stack.length, 0); equal(typeof endHangs.hangs[0].stack[0], "string"); - // Native stack gathering is only enabled on Windows x86. - if (mozinfo.os == "win" && mozinfo.bits == 32) { - // Make sure one of the hangs is a permanent - // hang containing a native stack. - ok(endHangs.hangs.some((hang) => ( - hang.nativeStack && - Array.isArray(hang.nativeStack.memoryMap) && - Array.isArray(hang.nativeStack.stacks) - ))); - } + // Make sure one of the hangs is a permanent + // hang containing a native stack. + ok(endHangs.hangs.some((hang) => ( + hang.nativeStack && + Array.isArray(hang.nativeStack.memoryMap) && + Array.isArray(hang.nativeStack.stacks) + ))); check_histogram(endHangs.hangs[0].histogram); diff --git a/toolkit/components/telemetry/tests/unit/xpcshell.ini b/toolkit/components/telemetry/tests/unit/xpcshell.ini index 0bfd9d0b9709..62bcdcb0a527 100644 --- a/toolkit/components/telemetry/tests/unit/xpcshell.ini +++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini @@ -55,6 +55,7 @@ tags = addons tags = addons [test_TelemetrySession_activeTicks.js] [test_ThreadHangStats.js] +skip-if = os == "android" || os == "linux" # BHR is disabled on linux (bug 1365309) run-sequentially = Bug 1046307, test can fail intermittently when CPU load is high [test_TelemetrySend.js] [test_ChildHistograms.js] diff --git a/xpcom/threads/BackgroundHangMonitor.cpp b/xpcom/threads/BackgroundHangMonitor.cpp index 2254e6e86104..79f7604a2d77 100644 --- a/xpcom/threads/BackgroundHangMonitor.cpp +++ b/xpcom/threads/BackgroundHangMonitor.cpp @@ -598,7 +598,6 @@ BackgroundHangMonitor::Startup() if (!strcmp(NS_STRINGIFY(MOZ_UPDATE_CHANNEL), "beta")) { if (XRE_IsParentProcess()) { // cached ClientID hasn't been read yet - ThreadStackHelper::Startup(); BackgroundHangThread::Startup(); BackgroundHangManager::sInstance = new BackgroundHangManager(); @@ -612,7 +611,6 @@ BackgroundHangMonitor::Startup() } } - ThreadStackHelper::Startup(); BackgroundHangThread::Startup(); BackgroundHangManager::sInstance = new BackgroundHangManager(); #endif @@ -633,7 +631,6 @@ BackgroundHangMonitor::Shutdown() we don't want to hold the lock when it's being destroyed. */ BackgroundHangManager::sInstance->Shutdown(); BackgroundHangManager::sInstance = nullptr; - ThreadStackHelper::Shutdown(); BackgroundHangManager::sDisabled = true; #endif } diff --git a/xpcom/threads/ThreadStackHelper.cpp b/xpcom/threads/ThreadStackHelper.cpp index e0c82ea22303..38738c8b2570 100644 --- a/xpcom/threads/ThreadStackHelper.cpp +++ b/xpcom/threads/ThreadStackHelper.cpp @@ -9,9 +9,6 @@ #include "nsJSPrincipals.h" #include "nsScriptSecurityManager.h" #include "jsfriendapi.h" -#ifdef MOZ_THREADSTACKHELPER_NATIVE -#include "shared-libraries.h" -#endif #ifdef MOZ_THREADSTACKHELPER_PSEUDO #include "PseudoStack.h" #endif @@ -65,91 +62,20 @@ #endif #endif -#ifdef MOZ_THREADSTACKHELPER_NATIVE -#if defined(MOZ_THREADSTACKHELPER_X86) || \ - defined(MOZ_THREADSTACKHELPER_X64) -// On these architectures, the stack grows downwards (toward lower addresses). -#define MOZ_THREADSTACKHELPER_STACK_GROWS_DOWN -#else -#error "Unsupported architecture" -#endif -#endif // MOZ_THREADSTACKHELPER_NATIVE - namespace mozilla { -void -ThreadStackHelper::Startup() -{ -#if defined(XP_LINUX) - MOZ_ASSERT(NS_IsMainThread()); - if (!sInitialized) { - // TODO: centralize signal number allocation - sFillStackSignum = SIGRTMIN + 4; - if (sFillStackSignum > SIGRTMAX) { - // Leave uninitialized - MOZ_ASSERT(false); - return; - } - struct sigaction sigact = {}; - sigact.sa_sigaction = FillStackHandler; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = SA_SIGINFO | SA_RESTART; - MOZ_ALWAYS_TRUE(!::sigaction(sFillStackSignum, &sigact, nullptr)); - } - sInitialized++; -#endif -} - -void -ThreadStackHelper::Shutdown() -{ -#if defined(XP_LINUX) - MOZ_ASSERT(NS_IsMainThread()); - if (sInitialized == 1) { - struct sigaction sigact = {}; - sigact.sa_handler = SIG_DFL; - MOZ_ALWAYS_TRUE(!::sigaction(sFillStackSignum, &sigact, nullptr)); - } - sInitialized--; -#endif -} - ThreadStackHelper::ThreadStackHelper() - : mStackToFill(nullptr) #ifdef MOZ_THREADSTACKHELPER_PSEUDO + : mStackToFill(nullptr) , mPseudoStack(profiler_get_pseudo_stack()) , mMaxStackSize(Stack::sMaxInlineStorage) , mMaxBufferSize(512) #endif -{ -#if defined(XP_LINUX) - MOZ_ALWAYS_TRUE(!::sem_init(&mSem, 0, 0)); - mThreadID = ::syscall(SYS_gettid); -#elif defined(XP_WIN) - mInitialized = !!::DuplicateHandle( - ::GetCurrentProcess(), ::GetCurrentThread(), - ::GetCurrentProcess(), &mThreadID, - THREAD_SUSPEND_RESUME #ifdef MOZ_THREADSTACKHELPER_NATIVE - | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION + , mNativeStackToFill(nullptr) #endif - , FALSE, 0); - mStackTop = profiler_get_stack_top(); - MOZ_ASSERT(mInitialized); -#elif defined(XP_MACOSX) - mThreadID = mach_thread_self(); -#endif -} - -ThreadStackHelper::~ThreadStackHelper() { -#if defined(XP_LINUX) - MOZ_ALWAYS_TRUE(!::sem_destroy(&mSem)); -#elif defined(XP_WIN) - if (mInitialized) { - MOZ_ALWAYS_TRUE(!!::CloseHandle(mThreadID)); - } -#endif + mThreadId = profiler_current_thread_id(); } namespace { @@ -170,138 +96,10 @@ ThreadStackHelper::GetPseudoStack(Stack& aStack) GetStacksInternal(&aStack, nullptr); } -void -ThreadStackHelper::GetStacksInternal(Stack* aStack, NativeStack* aNativeStack) -{ - // Always run PrepareStackBuffer first to clear aStack - if (aStack && !PrepareStackBuffer(*aStack)) { - // Skip and return empty aStack - return; - } - - ScopedSetPtr stackPtr(mStackToFill, aStack); - -#if defined(XP_LINUX) - if (!sInitialized) { - MOZ_ASSERT(false); - return; - } - if (aStack) { - siginfo_t uinfo = {}; - uinfo.si_signo = sFillStackSignum; - uinfo.si_code = SI_QUEUE; - uinfo.si_pid = getpid(); - uinfo.si_uid = getuid(); - uinfo.si_value.sival_ptr = this; - if (::syscall(SYS_rt_tgsigqueueinfo, uinfo.si_pid, - mThreadID, sFillStackSignum, &uinfo)) { - // rt_tgsigqueueinfo was added in Linux 2.6.31. - // Could have failed because the syscall did not exist. - return; - } - MOZ_ALWAYS_TRUE(!::sem_wait(&mSem)); - } - -#elif defined(XP_WIN) - if (!mInitialized) { - MOZ_ASSERT(false); - return; - } - - // NOTE: We can only perform frame pointer stack walking on non win64 - // platforms, because Win64 always omits frame pointers. We don't want to use - // MozStackWalk here, so we just skip collecting stacks entirely. -#ifndef MOZ_THREADSTACKHELPER_X64 - if (aNativeStack) { - aNativeStack->reserve(Telemetry::HangStack::sMaxNativeFrames); - } -#endif - - if (::SuspendThread(mThreadID) == DWORD(-1)) { - MOZ_ASSERT(false); - return; - } - - // SuspendThread is asynchronous, so the thread may still be running. Use - // GetThreadContext to ensure it's really suspended. - // See https://blogs.msdn.microsoft.com/oldnewthing/20150205-00/?p=44743. - CONTEXT context; - memset(&context, 0, sizeof(context)); - context.ContextFlags = CONTEXT_CONTROL; - if (::GetThreadContext(mThreadID, &context)) { - if (aStack) { - FillStackBuffer(); - } - -#ifndef MOZ_THREADSTACKHELPER_X64 - if (aNativeStack) { - auto callback = [](uint32_t, void* aPC, void*, void* aClosure) { - NativeStack* stack = static_cast(aClosure); - stack->push_back(reinterpret_cast(aPC)); - }; - - // Now we need to get our frame pointer, our stack pointer, and our stack - // top. Rather than registering and storing the stack tops ourselves, we use - // the gecko profiler to look it up. - void** framePointer = reinterpret_cast(context.Ebp); - void** stackPointer = reinterpret_cast(context.Esp); - - MOZ_ASSERT(mStackTop, "The thread should be registered by the profiler"); - - // Double check that the values we pulled for the thread make sense before - // walking the stack. - if (mStackTop && framePointer >= stackPointer && framePointer < mStackTop) { - // NOTE: In bug 1346415 this was changed to use FramePointerStackWalk. - // This was done because lowering the background hang timer threshold - // would cause it to fire on infra early during the boot process, causing - // a deadlock in MozStackWalk when the target thread was holding the - // windows-internal lock on the function table, as it would be suspended - // before we tried to grab the lock to walk its stack. - // - // FramePointerStackWalk is implemented entirely in userspace and thus - // doesn't have the same issues with deadlocking. Unfortunately as 64-bit - // windows is not guaranteed to have frame pointers, the stack walking - // code is only enabled on 32-bit windows builds (bug 1357829). - FramePointerStackWalk(callback, /* skipFrames */ 0, - /* maxFrames */ Telemetry::HangStack::sMaxNativeFrames, - reinterpret_cast(aNativeStack), framePointer, - mStackTop); - } - } -#endif - } - - MOZ_ALWAYS_TRUE(::ResumeThread(mThreadID) != DWORD(-1)); - -#elif defined(XP_MACOSX) -# if defined(MOZ_VALGRIND) && defined(RUNNING_ON_VALGRIND) - if (RUNNING_ON_VALGRIND) { - /* thread_suspend and thread_resume sometimes hang runs on Valgrind, - for unknown reasons. So, just avoid them. See bug 1100911. */ - return; - } -# endif - - if (aStack) { - if (::thread_suspend(mThreadID) != KERN_SUCCESS) { - MOZ_ASSERT(false); - return; - } - - FillStackBuffer(); - - MOZ_ALWAYS_TRUE(::thread_resume(mThreadID) == KERN_SUCCESS); - } - -#endif -} - void ThreadStackHelper::GetNativeStack(NativeStack& aNativeStack) { -#ifdef MOZ_THREADSTACKHELPER_NATIVE GetStacksInternal(nullptr, &aNativeStack); -#endif // MOZ_THREADSTACKHELPER_NATIVE } void @@ -310,22 +108,47 @@ ThreadStackHelper::GetPseudoAndNativeStack(Stack& aStack, NativeStack& aNativeSt GetStacksInternal(&aStack, &aNativeStack); } -#ifdef XP_LINUX - -int ThreadStackHelper::sInitialized; -int ThreadStackHelper::sFillStackSignum; - void -ThreadStackHelper::FillStackHandler(int aSignal, siginfo_t* aInfo, - void* aContext) +ThreadStackHelper::GetStacksInternal(Stack* aStack, NativeStack* aNativeStack) { - ThreadStackHelper* const helper = - reinterpret_cast(aInfo->si_value.sival_ptr); - helper->FillStackBuffer(); - ::sem_post(&helper->mSem); -} +#if defined(MOZ_THREADSTACKHELPER_PSEUDO) || defined(MOZ_THREADSTACKHELPER_NATIVE) + // Always run PrepareStackBuffer first to clear aStack + if (aStack && !PrepareStackBuffer(*aStack)) { + // Skip and return empty aStack + return; + } -#endif // XP_LINUX + // Prepare the native stack + if (aNativeStack) { + aNativeStack->clear(); + aNativeStack->reserve(Telemetry::HangStack::sMaxNativeFrames); + } + +#ifdef MOZ_THREADSTACKHELPER_PSEUDO + ScopedSetPtr stackPtr(mStackToFill, aStack); +#endif +#ifdef MOZ_THREADSTACKHELPER_NATIVE + ScopedSetPtr nativeStackPtr(mNativeStackToFill, aNativeStack); +#endif + + auto callback = [&, this] (void** aPCs, size_t aCount) { + FillStackBuffer(); + +#ifdef MOZ_THREADSTACKHELPER_NATIVE + if (mNativeStackToFill) { + while (aCount-- && + mNativeStackToFill->size() < mNativeStackToFill->capacity()) { + mNativeStackToFill->push_back(reinterpret_cast(aPCs[aCount])); + } + } +#endif + }; + + profiler_suspend_and_sample_thread(mThreadId, + callback, + /* aSampleNative = */ !!aNativeStack); +#endif +} bool ThreadStackHelper::PrepareStackBuffer(Stack& aStack) @@ -484,9 +307,9 @@ ThreadStackHelper::AppendJSEntry(const volatile js::ProfileEntry* aEntry, void ThreadStackHelper::FillStackBuffer() { +#ifdef MOZ_THREADSTACKHELPER_PSEUDO MOZ_ASSERT(mStackToFill->empty()); -#ifdef MOZ_THREADSTACKHELPER_PSEUDO size_t reservedSize = mStackToFill->capacity(); size_t reservedBufferSize = mStackToFill->AvailableBufferSize(); intptr_t availableBufferSize = intptr_t(reservedBufferSize); diff --git a/xpcom/threads/ThreadStackHelper.h b/xpcom/threads/ThreadStackHelper.h index a1dafda6f63c..eecf6c0fb065 100644 --- a/xpcom/threads/ThreadStackHelper.h +++ b/xpcom/threads/ThreadStackHelper.h @@ -22,25 +22,20 @@ #include #endif -// Support pseudostack on these platforms. +// Support pseudostack and native stack on these platforms. #if defined(XP_LINUX) || defined(XP_WIN) || defined(XP_MACOSX) # ifdef MOZ_GECKO_PROFILER # define MOZ_THREADSTACKHELPER_PSEUDO +# define MOZ_THREADSTACKHELPER_NATIVE # endif #endif -#if defined(MOZ_THREADSTACKHELPER_PSEUDO) && defined(XP_WIN) -# define MOZ_THREADSTACKHELPER_NATIVE -# if defined(__i386__) || defined(_M_IX86) -# define MOZ_THREADSTACKHELPER_X86 -# elif defined(__x86_64__) || defined(_M_X64) -# define MOZ_THREADSTACKHELPER_X64 -# elif defined(__arm__) || defined(_M_ARM) -# define MOZ_THREADSTACKHELPER_ARM -# else - // Unsupported architecture -# undef MOZ_THREADSTACKHELPER_NATIVE -# endif +// NOTE: Currently, due to a problem with LUL stackwalking initialization taking +// a long time (bug 1365309), we don't perform pseudostack or native stack +// walking on Linux. +#if defined(XP_LINUX) +# undef MOZ_THREADSTACKHELPER_NATIVE +# undef MOZ_THREADSTACKHELPER_PSEUDO #endif namespace mozilla { @@ -67,12 +62,15 @@ public: typedef Telemetry::NativeHangStack NativeStack; private: - Stack* mStackToFill; #ifdef MOZ_THREADSTACKHELPER_PSEUDO + Stack* mStackToFill; const PseudoStack* const mPseudoStack; size_t mMaxStackSize; size_t mMaxBufferSize; #endif +#ifdef MOZ_THREADSTACKHELPER_NATIVE + NativeStack* mNativeStackToFill; +#endif bool PrepareStackBuffer(Stack& aStack); void FillStackBuffer(); @@ -83,22 +81,11 @@ private: #endif public: - /** - * Initialize ThreadStackHelper. Must be called from main thread. - */ - static void Startup(); - /** - * Uninitialize ThreadStackHelper. Must be called from main thread. - */ - static void Shutdown(); - /** * Create a ThreadStackHelper instance targeting the current thread. */ ThreadStackHelper(); - ~ThreadStackHelper(); - /** * Retrieve the current pseudostack of the thread associated * with this ThreadStackHelper. @@ -130,27 +117,9 @@ private: // If only aStack needs to be collected, nullptr may be passed for // aNativeStack, and vice versa. void GetStacksInternal(Stack* aStack, NativeStack* aNativeStack); -#if defined(XP_LINUX) -private: - static int sInitialized; - static int sFillStackSignum; - static void FillStackHandler(int aSignal, siginfo_t* aInfo, void* aContext); - - sem_t mSem; - pid_t mThreadID; - -#elif defined(XP_WIN) -private: - bool mInitialized; - HANDLE mThreadID; - void* mStackTop; - -#elif defined(XP_MACOSX) -private: - thread_act_t mThreadID; - -#endif + // The profiler's unique thread identifier for the target thread. + int mThreadId; }; } // namespace mozilla From e62bfa945f484aaede63fb08a80a482193733c2f Mon Sep 17 00:00:00 2001 From: Michael Layzell Date: Thu, 11 May 2017 16:26:36 -0400 Subject: [PATCH 31/35] Bug 1357829 - Part 3: Remove profiler_get_stack_top, r=njn MozReview-Commit-ID: C4DvuOvYSrs --- tools/profiler/core/platform.cpp | 11 ----------- tools/profiler/public/GeckoProfiler.h | 6 ------ 2 files changed, 17 deletions(-) diff --git a/tools/profiler/core/platform.cpp b/tools/profiler/core/platform.cpp index 9bff49eb5902..ded4fbd9f68c 100644 --- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -3032,17 +3032,6 @@ profiler_clear_js_context() info->mContext = nullptr; } -void* -profiler_get_stack_top() -{ - PSAutoLock lock(gPSMutex); - ThreadInfo* threadInfo = FindLiveThreadInfo(lock); - if (threadInfo) { - return threadInfo->StackTop(); - } - return nullptr; -} - int profiler_current_thread_id() { diff --git a/tools/profiler/public/GeckoProfiler.h b/tools/profiler/public/GeckoProfiler.h index df19e9be038a..62f698f909de 100644 --- a/tools/profiler/public/GeckoProfiler.h +++ b/tools/profiler/public/GeckoProfiler.h @@ -377,12 +377,6 @@ PROFILER_FUNC(double profiler_time(), 0) PROFILER_FUNC_VOID(profiler_log(const char *str)) -// Gets the stack top of the current thread. -// -// The thread must have been previously registered with the profiler, otherwise -// this method will return nullptr. -PROFILER_FUNC(void* profiler_get_stack_top(), nullptr) - PROFILER_FUNC(int profiler_current_thread_id(), 0) // This method suspends the thread identified by aThreadId, optionally samples From 8a302e6be29509af08b192222b31c1fde53a675c Mon Sep 17 00:00:00 2001 From: Michael Layzell Date: Wed, 17 May 2017 23:07:02 -0400 Subject: [PATCH 32/35] Bug 1357829 - Part 4: Remove TickController, r=njn MozReview-Commit-ID: 2IHa6ybR9ug --- .../profiler/core/platform-linux-android.cpp | 7 +- tools/profiler/core/platform-macos.cpp | 7 +- tools/profiler/core/platform-win32.cpp | 7 +- tools/profiler/core/platform.cpp | 155 +++++++----------- 4 files changed, 68 insertions(+), 108 deletions(-) diff --git a/tools/profiler/core/platform-linux-android.cpp b/tools/profiler/core/platform-linux-android.cpp index cf348b33bbcf..422c1bc0c468 100644 --- a/tools/profiler/core/platform-linux-android.cpp +++ b/tools/profiler/core/platform-linux-android.cpp @@ -300,10 +300,11 @@ Sampler::Disable(PSLockRef aLock) sigaction(SIGPROF, &mOldSigprofHandler, 0); } +template void Sampler::SuspendAndSampleAndResumeThread(PSLockRef aLock, - TickController& aController, - TickSample& aSample) + TickSample& aSample, + const Func& aDoSample) { // Only one sampler thread can be sampling at once. So we expect to have // complete control over |sSigHandlerCoordinator|. @@ -358,7 +359,7 @@ Sampler::SuspendAndSampleAndResumeThread(PSLockRef aLock, // Extract the current PC and sp. FillInSample(aSample, &sSigHandlerCoordinator->mUContext); - aController.Tick(aLock, aSample); + aDoSample(); //----------------------------------------------------------------// // Resume the target thread. diff --git a/tools/profiler/core/platform-macos.cpp b/tools/profiler/core/platform-macos.cpp index 90613ac2b7b0..67f27c3cea96 100644 --- a/tools/profiler/core/platform-macos.cpp +++ b/tools/profiler/core/platform-macos.cpp @@ -73,10 +73,11 @@ Sampler::Disable(PSLockRef aLock) { } +template void Sampler::SuspendAndSampleAndResumeThread(PSLockRef aLock, - TickController& aController, - TickSample& aSample) + TickSample& aSample, + const Func& aDoSample) { thread_act_t samplee_thread = aSample.mPlatformData->ProfiledThread(); @@ -133,7 +134,7 @@ Sampler::SuspendAndSampleAndResumeThread(PSLockRef aLock, aSample.mSP = reinterpret_cast
(state.REGISTER_FIELD(sp)); aSample.mFP = reinterpret_cast
(state.REGISTER_FIELD(bp)); - aController.Tick(aLock, aSample); + aDoSample(); } #undef REGISTER_FIELD diff --git a/tools/profiler/core/platform-win32.cpp b/tools/profiler/core/platform-win32.cpp index e313b422635d..bf77c8c902d6 100644 --- a/tools/profiler/core/platform-win32.cpp +++ b/tools/profiler/core/platform-win32.cpp @@ -90,10 +90,11 @@ Sampler::Disable(PSLockRef aLock) { } +template void Sampler::SuspendAndSampleAndResumeThread(PSLockRef aLock, - TickController& aController, - TickSample& aSample) + TickSample& aSample, + const Func& aDoSample) { HANDLE profiled_thread = aSample.mPlatformData->ProfiledThread(); if (profiled_thread == nullptr) { @@ -149,7 +150,7 @@ Sampler::SuspendAndSampleAndResumeThread(PSLockRef aLock, aSample.mContext = &context; - aController.Tick(aLock, aSample); + aDoSample(); //----------------------------------------------------------------// // Resume the target thread. diff --git a/tools/profiler/core/platform.cpp b/tools/profiler/core/platform.cpp index ded4fbd9f68c..c5db54bc09ff 100644 --- a/tools/profiler/core/platform.cpp +++ b/tools/profiler/core/platform.cpp @@ -754,15 +754,6 @@ struct NativeStack size_t count; }; -class TickController -{ -public: - // NOTE: This method is called when the target thread of the sample is paused. - // Do not allocate or attempt to grab any locks during this function call. - virtual void Tick(PSLockRef aLock, - const TickSample& aSample) = 0; -}; - mozilla::Atomic WALKING_JS_STACK(false); struct AutoWalkJSStack @@ -1234,63 +1225,39 @@ DoNativeBacktrace(PSLockRef aLock, NativeStack& aNativeStack, #endif -static void -DoSampleStackTrace(PSLockRef aLock, ProfileBuffer* aBuffer, - const TickSample& aSample) -{ - NativeStack nativeStack = { nullptr, nullptr, 0, 0 }; - MergeStacksIntoProfile(aLock, aBuffer, aSample, nativeStack); - - if (ActivePS::FeatureLeaf(aLock)) { - aBuffer->addTag(ProfileBufferEntry::NativeLeafAddr((void*)aSample.mPC)); - } -} - -class ProfilerTickController : public TickController -{ -public: - explicit ProfilerTickController(PSLockRef aLock, ProfileBuffer* aBuffer = nullptr) - : mBuffer(aBuffer ? aBuffer : ActivePS::Buffer(aLock)) - { - } - - // This function is called for each sampling period with the current program - // counter. It is called within a signal and so must be re-entrant. - void Tick(PSLockRef aLock, const TickSample& aSample) override; - -private: - ProfileBuffer* mBuffer; -}; - void -ProfilerTickController::Tick(PSLockRef aLock, const TickSample& aSample) +Tick(PSLockRef aLock, const TickSample& aSample, ProfileBuffer* aBuffer) { MOZ_RELEASE_ASSERT(ActivePS::Exists(aLock)); - mBuffer->addTagThreadId(aSample.mThreadId, aSample.mLastSample); + aBuffer->addTagThreadId(aSample.mThreadId, aSample.mLastSample); mozilla::TimeDuration delta = aSample.mTimeStamp - CorePS::ProcessStartTime(aLock); - mBuffer->addTag(ProfileBufferEntry::Time(delta.ToMilliseconds())); + aBuffer->addTag(ProfileBufferEntry::Time(delta.ToMilliseconds())); + + void* pc_array[1000]; + void* sp_array[1000]; + NativeStack nativeStack = { + pc_array, + sp_array, + mozilla::ArrayLength(pc_array), + 0 + }; #if defined(HAVE_NATIVE_UNWIND) if (ActivePS::FeatureStackWalk(aLock)) { - void* pc_array[1000]; - void* sp_array[1000]; - NativeStack nativeStack = { - pc_array, - sp_array, - mozilla::ArrayLength(pc_array), - 0 - }; - DoNativeBacktrace(aLock, nativeStack, aSample); - MergeStacksIntoProfile(aLock, mBuffer, aSample, nativeStack); + MergeStacksIntoProfile(aLock, aBuffer, aSample, nativeStack); } else #endif { - DoSampleStackTrace(aLock, mBuffer, aSample); + MergeStacksIntoProfile(aLock, aBuffer, aSample, nativeStack); + + if (ActivePS::FeatureLeaf(aLock)) { + aBuffer->addTag(ProfileBufferEntry::NativeLeafAddr((void*)aSample.mPC)); + } } // Don't process the PseudoStack's markers if we're synchronously sampling @@ -1300,27 +1267,27 @@ ProfilerTickController::Tick(PSLockRef aLock, const TickSample& aSample) aSample.mRacyInfo->GetPendingMarkers(); while (pendingMarkersList && pendingMarkersList->peek()) { ProfilerMarker* marker = pendingMarkersList->popHead(); - mBuffer->addStoredMarker(marker); - mBuffer->addTag(ProfileBufferEntry::Marker(marker)); + aBuffer->addStoredMarker(marker); + aBuffer->addTag(ProfileBufferEntry::Marker(marker)); } } if (aSample.mResponsiveness && aSample.mResponsiveness->HasData()) { mozilla::TimeDuration delta = aSample.mResponsiveness->GetUnresponsiveDuration(aSample.mTimeStamp); - mBuffer->addTag(ProfileBufferEntry::Responsiveness(delta.ToMilliseconds())); + aBuffer->addTag(ProfileBufferEntry::Responsiveness(delta.ToMilliseconds())); } // rssMemory is equal to 0 when we are not recording. if (aSample.mRSSMemory != 0) { double rssMemory = static_cast(aSample.mRSSMemory); - mBuffer->addTag(ProfileBufferEntry::ResidentMemory(rssMemory)); + aBuffer->addTag(ProfileBufferEntry::ResidentMemory(rssMemory)); } // ussMemory is equal to 0 when we are not recording. if (aSample.mUSSMemory != 0) { double ussMemory = static_cast(aSample.mUSSMemory); - mBuffer->addTag(ProfileBufferEntry::UnsharedMemory(ussMemory)); + aBuffer->addTag(ProfileBufferEntry::UnsharedMemory(ussMemory)); } } @@ -1718,10 +1685,15 @@ public: // called once, and only once, before the Sampler is destroyed. void Disable(PSLockRef aLock); - // This method suspends and resumes the samplee thread. + // This method suspends and resumes the samplee thread. It calls the passed-in + // function like object aDoSample while the samplee thread is suspended, after + // filling in register values in aSample. + // + // Func must be a function-like object of type `void()`. + template void SuspendAndSampleAndResumeThread(PSLockRef aLock, - TickController& aController, - TickSample& aSample); + TickSample& aSample, + const Func& aDoSample); private: #if defined(GP_OS_linux) || defined(GP_OS_android) @@ -1868,9 +1840,10 @@ SamplerThread::Run() } TickSample sample(info, rssMemory, ussMemory); - ProfilerTickController controller(lock); - SuspendAndSampleAndResumeThread(lock, controller, sample); + SuspendAndSampleAndResumeThread(lock, sample, [&] { + Tick(lock, sample, ActivePS::Buffer(lock)); + }); } #if defined(USE_LUL_STACKWALK) @@ -2816,8 +2789,7 @@ profiler_get_backtrace() #endif #endif - ProfilerTickController controller(lock, buffer); - controller.Tick(lock, sample); + Tick(lock, sample, buffer); return UniqueProfilerBacktrace( new ProfilerBacktrace("SyncProfile", tid, buffer)); @@ -3038,40 +3010,6 @@ profiler_current_thread_id() return Thread::GetCurrentId(); } -class SimpleTickController : public TickController -{ -public: - explicit SimpleTickController(const std::function& aCallback, - bool aSampleNative) - : mCallback(aCallback) - , mSampleNative(aSampleNative) - { - } - - void Tick(PSLockRef aLock, const TickSample& aSample) override { - void* pc_array[1000]; - void* sp_array[1000]; - NativeStack nativeStack = { - pc_array, - sp_array, - mozilla::ArrayLength(pc_array), - 0 - }; - -#if defined(HAVE_NATIVE_UNWIND) - if (mSampleNative) { - DoNativeBacktrace(aLock, nativeStack, aSample); - } -#endif - - mCallback(nativeStack.pc_array, nativeStack.count); - } - -private: - const std::function& mCallback; - bool mSampleNative; -}; - // NOTE: The callback function passed in will be called while the target thread // is paused. Doing stuff in this function like allocating which may try to // claim locks is a surefire way to deadlock. @@ -3080,6 +3018,17 @@ profiler_suspend_and_sample_thread(int aThreadId, const std::function& aCallback, bool aSampleNative /* = true */) { + // Allocate the space for the native stack + void* pc_array[1000]; + void* sp_array[1000]; + NativeStack nativeStack = { + pc_array, + sp_array, + mozilla::ArrayLength(pc_array), + 0 + }; + + // Lock the profiler mutex PSAutoLock lock(gPSMutex); const CorePS::ThreadVector& liveThreads = CorePS::LiveThreads(lock); @@ -3090,8 +3039,16 @@ profiler_suspend_and_sample_thread(int aThreadId, // Suspend, sample, and then resume the target thread. Sampler sampler(lock); TickSample sample(info, 0, 0); - SimpleTickController controller(aCallback, aSampleNative); - sampler.SuspendAndSampleAndResumeThread(lock, controller, sample); + sampler.SuspendAndSampleAndResumeThread(lock, sample, [&] { + // The target thread is now suspended, collect a native backtrace, and + // call the callback. +#if defined(HAVE_NATIVE_UNWIND) + if (aSampleNative) { + DoNativeBacktrace(lock, nativeStack, sample); + } +#endif + aCallback(nativeStack.pc_array, nativeStack.count); + }); // NOTE: Make sure to disable the sampler before it is destroyed, in case // the profiler is running at the same time. From 48524055f0ec37758077687648713568032a4902 Mon Sep 17 00:00:00 2001 From: Jan Keromnes Date: Thu, 18 May 2017 19:59:47 +0200 Subject: [PATCH 33/35] Bug 1365559 - Remove unused pref. r=dao MozReview-Commit-ID: F3hCiuuk9kx --- browser/app/profile/firefox.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 2f7c672f09a9..3fb46143b329 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1282,8 +1282,6 @@ pref("pdfjs.previousHandler.alwaysAskBeforeHandling", false); // (This is intentionally on the high side; see bug 746055.) pref("image.mem.max_decoded_image_kb", 256000); -pref("social.sidebar.unload_timeout_ms", 10000); - // Activation from inside of share panel is possible if activationPanelEnabled // is true. Pref'd off for release while usage testing is done through beta. pref("social.share.activationPanelEnabled", true); From 7d35bd00fd24d6295f6d2ded38950848c38d02d1 Mon Sep 17 00:00:00 2001 From: Robert Strong Date: Thu, 18 May 2017 11:33:24 -0700 Subject: [PATCH 34/35] Bug 1365861 - Minor gonk cleanup in nsUpdateService.js. r=mhowell Removes unused gonk code Changes use of Array.prototype.indexOf(val) != -1 to Array.prototype.includes(val) Some very minor indentation and logging cleanup --- toolkit/mozapps/update/nsUpdateService.js | 44 +++-------------------- 1 file changed, 5 insertions(+), 39 deletions(-) diff --git a/toolkit/mozapps/update/nsUpdateService.js b/toolkit/mozapps/update/nsUpdateService.js index 2af15d3aebe0..2d3150fed48b 100644 --- a/toolkit/mozapps/update/nsUpdateService.js +++ b/toolkit/mozapps/update/nsUpdateService.js @@ -67,7 +67,6 @@ const FILE_ACTIVE_UPDATE_XML = "active-update.xml"; const FILE_BACKUP_UPDATE_LOG = "backup-update.log"; const FILE_LAST_UPDATE_LOG = "last-update.log"; const FILE_UPDATES_XML = "updates.xml"; -const FILE_UPDATE_LINK = "update.link"; const FILE_UPDATE_LOG = "update.log"; const FILE_UPDATE_MAR = "update.mar"; const FILE_UPDATE_STATUS = "update.status"; @@ -101,7 +100,6 @@ const SERVICE_COULD_NOT_LOCK_UPDATER = 32; const SERVICE_INSTALLDIR_ERROR = 33; const WRITE_ERROR_ACCESS_DENIED = 35; const WRITE_ERROR_CALLBACK_APP = 37; -const FILESYSTEM_MOUNT_READWRITE_ERROR = 43; const SERVICE_COULD_NOT_COPY_UPDATER = 49; const SERVICE_STILL_APPLYING_TERMINATED = 50; const SERVICE_STILL_APPLYING_NO_EXIT_CODE = 51; @@ -148,10 +146,6 @@ const SERVICE_ERRORS = [SERVICE_UPDATER_COULD_NOT_BE_STARTED, // Error codes 80 through 99 are reserved for nsUpdateService.js and are not // defined in common/errors.h -const FOTA_GENERAL_ERROR = 80; -const FOTA_UNKNOWN_ERROR = 81; -const FOTA_FILE_OPERATION_ERROR = 82; -const FOTA_RECOVERY_ERROR = 83; const INVALID_UPDATER_STATE_CODE = 98; const INVALID_UPDATER_STATUS_CODE = 99; @@ -190,8 +184,6 @@ const APPID_TO_TOPIC = { "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}": "sessionstore-windows-restored", // SeaMonkey "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}": "sessionstore-windows-restored", - // Fennec - "{aa3c5121-dab2-40e2-81ca-7ea25febc110}": "sessionstore-windows-restored", // Thunderbird "{3550f703-e582-4d05-9a08-453d09bdfdc6}": "mail-startup-done", // Instantbird @@ -931,26 +923,7 @@ function readStringFromFile(file) { function handleUpdateFailure(update, errorCode) { update.errorCode = parseInt(errorCode); - if (update.errorCode == FOTA_GENERAL_ERROR || - update.errorCode == FOTA_FILE_OPERATION_ERROR || - update.errorCode == FOTA_RECOVERY_ERROR || - update.errorCode == FOTA_UNKNOWN_ERROR) { - // In the case of FOTA update errors, don't reset the state to pending. This - // causes the FOTA update path to try again, which is not necessarily what - // we want. - update.statusText = gUpdateBundle.GetStringFromName("statusFailed"); - - Cc["@mozilla.org/updates/update-prompt;1"]. - createInstance(Ci.nsIUpdatePrompt). - showUpdateError(update); - writeStatusFile(getUpdatesDir(), STATE_FAILED + ": " + errorCode); - cleanupActiveUpdate(); - return true; - } - - // Replace with Array.prototype.includes when it has stabilized. - if (WRITE_ERRORS.indexOf(update.errorCode) != -1 || - update.errorCode == FILESYSTEM_MOUNT_READWRITE_ERROR) { + if (WRITE_ERRORS.includes(update.errorCode)) { Cc["@mozilla.org/updates/update-prompt;1"]. createInstance(Ci.nsIUpdatePrompt). showUpdateError(update); @@ -1000,7 +973,7 @@ function handleUpdateFailure(update, errorCode) { update.QueryInterface(Ci.nsIWritablePropertyBag); update.setProperty("patchingFailed", "elevationFailure"); let prompter = Cc["@mozilla.org/updates/update-prompt;1"]. - createInstance(Ci.nsIUpdatePrompt); + createInstance(Ci.nsIUpdatePrompt); prompter.showUpdateError(update); } else { writeStatusFile(getUpdatesDir(), update.state = STATE_PENDING); @@ -1015,8 +988,7 @@ function handleUpdateFailure(update, errorCode) { Services.prefs.clearUserPref(PREF_APP_UPDATE_CANCELATIONS_OSX); } - // Replace with Array.prototype.includes when it has stabilized. - if (SERVICE_ERRORS.indexOf(update.errorCode) != -1) { + if (SERVICE_ERRORS.includes(update.errorCode)) { var failCount = getPref("getIntPref", PREF_APP_UPDATE_SERVICE_ERRORS, 0); var maxFail = getPref("getIntPref", @@ -1073,7 +1045,7 @@ function handleFallbackToCompleteUpdate(update, postStaging) { LOG("handleFallbackToCompleteUpdate - install of complete or " + "only one patch offered failed. Notifying observers. topic: " + "update-error, status: unknown, " + - "update.patchCount: " + update.patchCount + + "update.patchCount: " + update.patchCount + ", " + "oldType: " + oldType); Services.obs.notifyObservers(update, "update-error", "unknown"); } @@ -3881,14 +3853,8 @@ UpdatePrompt.prototype = { return; // In some cases, we want to just show a simple alert dialog. - // Replace with Array.prototype.includes when it has stabilized. if (update.state == STATE_FAILED && - (WRITE_ERRORS.indexOf(update.errorCode) != -1 || - update.errorCode == FILESYSTEM_MOUNT_READWRITE_ERROR || - update.errorCode == FOTA_GENERAL_ERROR || - update.errorCode == FOTA_FILE_OPERATION_ERROR || - update.errorCode == FOTA_RECOVERY_ERROR || - update.errorCode == FOTA_UNKNOWN_ERROR)) { + WRITE_ERRORS.includes(update.errorCode)) { var title = gUpdateBundle.GetStringFromName("updaterIOErrorTitle"); var text = gUpdateBundle.formatStringFromName("updaterIOErrorMsg", [Services.appinfo.name, From 9461e3384659d04476a6c742be6cf7792ffe6df5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Marie?= Date: Thu, 18 May 2017 11:00:00 -0400 Subject: [PATCH 35/35] Bug 1364547 - Followup to fix build on OpenBSD where size_t is an unsigned long (like OSX). r=jonco --- js/src/vm/JSONPrinter.cpp | 2 +- js/src/vm/JSONPrinter.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/js/src/vm/JSONPrinter.cpp b/js/src/vm/JSONPrinter.cpp index 423c0e7c6a47..a9fb3d4085c6 100644 --- a/js/src/vm/JSONPrinter.cpp +++ b/js/src/vm/JSONPrinter.cpp @@ -158,7 +158,7 @@ JSONPrinter::property(const char* name, uint64_t value) out_.printf("%" PRIu64, value); } -#ifdef XP_DARWIN +#if defined(XP_DARWIN) || defined(__OpenBSD__) void JSONPrinter::property(const char* name, size_t value) { diff --git a/js/src/vm/JSONPrinter.h b/js/src/vm/JSONPrinter.h index 4bfee28135cf..262a21bfe334 100644 --- a/js/src/vm/JSONPrinter.h +++ b/js/src/vm/JSONPrinter.h @@ -51,10 +51,10 @@ class JSONPrinter void property(const char* name, uint32_t value); void property(const char* name, int64_t value); void property(const char* name, uint64_t value); -#ifdef XP_DARWIN - // On OSX, size_t is long unsigned, uint32_t is unsigned, and uint64_t is - // long long unsigned. Everywhere else, size_t matches either uint32_t or - // uint64_t. +#if defined(XP_DARWIN) || defined(__OpenBSD__) + // On OSX and OpenBSD, size_t is long unsigned, uint32_t is unsigned, and + // uint64_t is long long unsigned. Everywhere else, size_t matches either + // uint32_t or uint64_t. void property(const char* name, size_t value); #endif